diff --git a/libtorrent/include/libtorrent/alert.hpp b/libtorrent/include/libtorrent/alert.hpp index a820ef225..b6b6711dc 100644 --- a/libtorrent/include/libtorrent/alert.hpp +++ b/libtorrent/include/libtorrent/alert.hpp @@ -49,15 +49,17 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #ifdef _MSC_VER #pragma warning(pop) #endif +#include "libtorrent/time.hpp" #include "libtorrent/config.hpp" -#define TORRENT_MAX_ALERT_TYPES 10 +#ifndef TORRENT_MAX_ALERT_TYPES +#define TORRENT_MAX_ALERT_TYPES 15 +#endif namespace libtorrent { @@ -70,9 +72,9 @@ namespace libtorrent { virtual ~alert(); // a timestamp is automatically created in the constructor - boost::posix_time::ptime timestamp() const; + ptime timestamp() const; - const std::string& msg() const; + std::string const& msg() const; severity_t severity() const; @@ -81,7 +83,7 @@ namespace libtorrent { private: std::string m_msg; severity_t m_severity; - boost::posix_time::ptime m_timestamp; + ptime m_timestamp; }; class TORRENT_EXPORT alert_manager @@ -112,57 +114,45 @@ namespace libtorrent { struct void_; - template< - class Handler - , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T) - > + template void handle_alert_dispatch( - const std::auto_ptr& alert_ - , const Handler& handler - , const std::type_info& typeid_ - , BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p)) + const std::auto_ptr& alert_, const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p)) { if (typeid_ == typeid(T0)) handler(*static_cast(alert_.get())); else - handle_alert_dispatch( - alert_ - , handler - , typeid_ - , BOOST_PP_ENUM_SHIFTED_PARAMS(TORRENT_MAX_ALERT_TYPES, p), (void_*)0 - ); + handle_alert_dispatch(alert_, handler, typeid_ + , BOOST_PP_ENUM_SHIFTED_PARAMS( + TORRENT_MAX_ALERT_TYPES, p), (void_*)0); } template void handle_alert_dispatch( - const std::auto_ptr& alert_ - , const Handler& handler - , const std::type_info& typeid_ - , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) + const std::auto_ptr& alert_ + , const Handler& handler + , const std::type_info& typeid_ + , BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT)) { throw unhandled_alert(); } } // namespace detail - template< - BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_) - > + template struct TORRENT_EXPORT handle_alert { template - handle_alert( - const std::auto_ptr& alert_ - , const Handler& handler) + handle_alert(const std::auto_ptr& alert_ + , const Handler& handler) { #define ALERT_POINTER_TYPE(z, n, text) (BOOST_PP_CAT(T, n)*)0 - detail::handle_alert_dispatch( - alert_ - , handler - , typeid(*alert_) - , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _) - ); + detail::handle_alert_dispatch(alert_, handler, typeid(*alert_) + , BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)); #undef ALERT_POINTER_TYPE } diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index 878185e42..e3e23ad05 100644 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/include/libtorrent/alert_types.hpp @@ -249,6 +249,26 @@ namespace libtorrent { return std::auto_ptr(new listen_failed_alert(*this)); } }; + struct TORRENT_EXPORT portmap_error_alert: alert + { + portmap_error_alert(const std::string& msg) + : alert(alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new portmap_error_alert(*this)); } + }; + + struct TORRENT_EXPORT portmap_alert: alert + { + portmap_alert(const std::string& msg) + : alert(alert::info, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new portmap_alert(*this)); } + }; + struct TORRENT_EXPORT fastresume_rejected_alert: torrent_alert { fastresume_rejected_alert(torrent_handle const& h @@ -260,6 +280,20 @@ namespace libtorrent { return std::auto_ptr(new fastresume_rejected_alert(*this)); } }; + struct TORRENT_EXPORT peer_blocked_alert: alert + { + peer_blocked_alert(address const& ip_ + , std::string const& msg) + : alert(alert::info, msg) + , ip(ip_) + {} + + address ip; + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new peer_blocked_alert(*this)); } + }; + } diff --git a/libtorrent/include/libtorrent/bandwidth_manager.hpp b/libtorrent/include/libtorrent/bandwidth_manager.hpp index 32d1b510d..dbf93aa77 100644 --- a/libtorrent/include/libtorrent/bandwidth_manager.hpp +++ b/libtorrent/include/libtorrent/bandwidth_manager.hpp @@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_BANDWIDTH_MANAGER_HPP_INCLUDED #include "libtorrent/socket.hpp" + #include #include #include @@ -42,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -namespace pt = boost::posix_time; using boost::weak_ptr; using boost::shared_ptr; using boost::intrusive_ptr; @@ -70,8 +70,8 @@ namespace libtorrent struct history_entry { history_entry(intrusive_ptr p, weak_ptr t - , int a, pt::ptime exp); - pt::ptime expires_at; + , int a, ptime exp); + ptime expires_at; int amount; intrusive_ptr peer; weak_ptr tor; diff --git a/libtorrent/include/libtorrent/bt_peer_connection.hpp b/libtorrent/include/libtorrent/bt_peer_connection.hpp index f68a5d1fa..223bf2650 100644 --- a/libtorrent/include/libtorrent/bt_peer_connection.hpp +++ b/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -48,7 +48,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include @@ -91,14 +90,16 @@ namespace libtorrent bt_peer_connection( aux::session_impl& ses , boost::weak_ptr t - , boost::shared_ptr s - , tcp::endpoint const& remote); + , boost::shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo); // with this constructor we have been contacted and we still don't // know which torrent the connection belongs to bt_peer_connection( aux::session_impl& ses - , boost::shared_ptr s); + , boost::shared_ptr s + , policy::peer* peerinfo); ~bt_peer_connection(); @@ -199,7 +200,7 @@ namespace libtorrent #ifndef NDEBUG void check_invariant() const; - boost::posix_time::ptime m_last_choke; + ptime m_last_choke; #endif private: diff --git a/libtorrent/include/libtorrent/connection_queue.hpp b/libtorrent/include/libtorrent/connection_queue.hpp new file mode 100644 index 000000000..05a8a61fe --- /dev/null +++ b/libtorrent/include/libtorrent/connection_queue.hpp @@ -0,0 +1,96 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_CONNECTION_QUEUE +#define TORRENT_CONNECTION_QUEUE + +#include +#include +#include +#include "libtorrent/socket.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + +class connection_queue : public boost::noncopyable +{ +public: + connection_queue(io_service& ios); + + bool free_slots() const; + + void enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout); + void done(int ticket); + void limit(int limit); + int limit() const; + +#ifndef NDEBUG + + void check_invariant() const; + +#endif + +private: + + void try_connect(); + void on_timeout(asio::error_code const& e); + + struct entry + { + entry(): connecting(false), ticket(0), expires(max_time()) {} + // called when the connection is initiated + boost::function on_connect; + // called if done hasn't been called within the timeout + boost::function on_timeout; + bool connecting; + int ticket; + ptime expires; + time_duration timeout; + }; + + std::list m_queue; + + // the next ticket id a connection will be given + int m_next_ticket; + int m_num_connecting; + int m_half_open_limit; + + deadline_timer m_timer; +}; + +} + +#endif + diff --git a/libtorrent/include/libtorrent/debug.hpp b/libtorrent/include/libtorrent/debug.hpp index 78df16b7a..39c4b0222 100644 --- a/libtorrent/include/libtorrent/debug.hpp +++ b/libtorrent/include/libtorrent/debug.hpp @@ -52,17 +52,6 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - - // PROFILING CODE -#ifdef TORRENT_PROFILE - - void add_checkpoint(std::string const& str); - void print_checkpoints(); -#define TORRENT_CHECKPOINT(str) libtorrent::add_checkpoint(str) -#else -#define TORRENT_CHECKPOINT(str) void(0) -#endif - // DEBUG API struct logger diff --git a/libtorrent/include/libtorrent/extensions.hpp b/libtorrent/include/libtorrent/extensions.hpp index 9068aa62a..5f8172649 100644 --- a/libtorrent/include/libtorrent/extensions.hpp +++ b/libtorrent/include/libtorrent/extensions.hpp @@ -77,6 +77,10 @@ namespace libtorrent // default behavior will be skipped virtual bool on_pause() { return false; } virtual bool on_resume() { return false;} + + // this is called when the initial checking of + // files is completed. + virtual void on_files_checked() {} }; struct TORRENT_EXPORT peer_plugin @@ -84,6 +88,7 @@ namespace libtorrent virtual ~peer_plugin() {} // can add entries to the extension handshake + // this is not called for web seeds virtual void add_handshake(entry&) {} // throwing an exception from any of the handlers (except add_handshake) @@ -92,12 +97,14 @@ namespace libtorrent // this is called when the initial BT handshake is received. Returning false // means that the other end doesn't support this extension and will remove // it from the list of plugins. + // this is not called for web seeds virtual bool on_handshake() { return true; } // called when the extension handshake from the other end is received // if this returns false, it means that this extension isn't // supported by this peer. It will result in this peer_plugin // being removed from the peer_connection and destructed. + // this is not called for web seeds virtual bool on_extension_handshake(entry const& h) { return true; } // returning true from any of the message handlers @@ -137,10 +144,12 @@ namespace libtorrent // the message is not processed by any other plugin and if false // is returned the next plugin in the chain will receive it to // be able to handle it + // this is not called for web seeds virtual bool on_extended(int length , int msg, buffer::const_interval body) { return false; } + // this is not called for web seeds virtual bool on_unknown_message(int length, int msg , buffer::const_interval body) { return false; } diff --git a/libtorrent/include/libtorrent/file.hpp b/libtorrent/include/libtorrent/file.hpp index 1b71c66f5..d11496b28 100644 --- a/libtorrent/include/libtorrent/file.hpp +++ b/libtorrent/include/libtorrent/file.hpp @@ -110,6 +110,7 @@ namespace libtorrent void open(boost::filesystem::path const& p, open_mode m); void close(); + void set_size(size_type size); size_type write(const char*, size_type num_bytes); size_type read(char*, size_type num_bytes); diff --git a/libtorrent/include/libtorrent/file_pool.hpp b/libtorrent/include/libtorrent/file_pool.hpp index 1da47fc85..a22c26538 100644 --- a/libtorrent/include/libtorrent/file_pool.hpp +++ b/libtorrent/include/libtorrent/file_pool.hpp @@ -38,7 +38,6 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #include #include @@ -50,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include "libtorrent/file.hpp" +#include "libtorrent/time.hpp" namespace libtorrent { @@ -59,7 +59,6 @@ namespace libtorrent using boost::multi_index::ordered_unique; using boost::multi_index::indexed_by; using boost::multi_index::member; - namespace pt = boost::posix_time; namespace fs = boost::filesystem; struct TORRENT_EXPORT file_pool : boost::noncopyable @@ -77,11 +76,11 @@ namespace libtorrent { lru_file_entry(boost::shared_ptr const& f) : file_ptr(f) - , last_use(pt::second_clock::universal_time()) {} + , last_use(time_now()) {} mutable boost::shared_ptr file_ptr; fs::path file_path; void* key; - pt::ptime last_use; + ptime last_use; file::open_mode mode; }; @@ -89,7 +88,7 @@ namespace libtorrent lru_file_entry, indexed_by< ordered_unique > - , ordered_non_unique > , ordered_non_unique > diff --git a/libtorrent/include/libtorrent/hasher.hpp b/libtorrent/include/libtorrent/hasher.hpp index 803b758eb..d1743527a 100644 --- a/libtorrent/include/libtorrent/hasher.hpp +++ b/libtorrent/include/libtorrent/hasher.hpp @@ -38,24 +38,27 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_id.hpp" #include "libtorrent/config.hpp" +#include "zlib.h" +#ifdef TORRENT_USE_OPENSSL +extern "C" +{ +#include +} +#else // from sha1.cpp -struct TORRENT_EXPORT SHA1_CTX +struct TORRENT_EXPORT SHA_CTX { boost::uint32_t state[5]; boost::uint32_t count[2]; boost::uint8_t buffer[64]; }; -TORRENT_EXPORT void SHA1Init(SHA1_CTX* context); -TORRENT_EXPORT void SHA1Update(SHA1_CTX* context, boost::uint8_t const* data, boost::uint32_t len); -TORRENT_EXPORT void SHA1Final(SHA1_CTX* context, boost::uint8_t* digest); +TORRENT_EXPORT void SHA1_Init(SHA_CTX* context); +TORRENT_EXPORT void SHA1_Update(SHA_CTX* context, boost::uint8_t const* data, boost::uint32_t len); +TORRENT_EXPORT void SHA1_Final(boost::uint8_t* digest, SHA_CTX* context); -extern "C" -{ - // from zlib/adler32.c - unsigned long adler32(unsigned long adler, const char* data, unsigned int len); -} +#endif namespace libtorrent { @@ -69,7 +72,7 @@ namespace libtorrent { assert(data != 0); assert(len > 0); - m_adler = adler32(m_adler, data, len); + m_adler = adler32(m_adler, (const Bytef*)data, len); } unsigned long final() const { return m_adler; } void reset() { m_adler = adler32(0, 0, 0); } @@ -84,33 +87,33 @@ namespace libtorrent { public: - hasher() { SHA1Init(&m_context); } + hasher() { SHA1_Init(&m_context); } hasher(const char* data, int len) { - SHA1Init(&m_context); + SHA1_Init(&m_context); assert(data != 0); assert(len > 0); - SHA1Update(&m_context, reinterpret_cast(data), len); + SHA1_Update(&m_context, reinterpret_cast(data), len); } void update(const char* data, int len) { assert(data != 0); assert(len > 0); - SHA1Update(&m_context, reinterpret_cast(data), len); + SHA1_Update(&m_context, reinterpret_cast(data), len); } sha1_hash final() { sha1_hash digest; - SHA1Final(&m_context, digest.begin()); + SHA1_Final(digest.begin(), &m_context); return digest; } - void reset() { SHA1Init(&m_context); } + void reset() { SHA1_Init(&m_context); } private: - SHA1_CTX m_context; + SHA_CTX m_context; }; } diff --git a/libtorrent/include/libtorrent/http_connection.hpp b/libtorrent/include/libtorrent/http_connection.hpp new file mode 100644 index 000000000..409213857 --- /dev/null +++ b/libtorrent/include/libtorrent/http_connection.hpp @@ -0,0 +1,154 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_CONNECTION +#define TORRENT_HTTP_CONNECTION + +#include +#include +#include +#include +#include +#include +#include + +#include "libtorrent/socket.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/time.hpp" + +namespace libtorrent +{ + +typedef boost::function http_handler; + +// TODO: add bind interface + +// when bottled, the last two arguments to the handler +// will always be 0 +struct http_connection : boost::enable_shared_from_this, boost::noncopyable +{ + http_connection(asio::io_service& ios, connection_queue& cc + , http_handler handler, bool bottled = true) + : m_sock(ios) + , m_read_pos(0) + , m_resolver(ios) + , m_handler(handler) + , m_timer(ios) + , m_last_receive(time_now()) + , m_bottled(bottled) + , m_called(false) + , m_rate_limit(0) + , m_download_quota(0) + , m_limiter_timer_active(false) + , m_limiter_timer(ios) + , m_redirect(true) + , m_connection_ticket(-1) + , m_cc(cc) + { + assert(!m_handler.empty()); + } + + void rate_limit(int limit); + + int rate_limit() const + { return m_rate_limit; } + + std::string sendbuffer; + + void get(std::string const& url, time_duration timeout = seconds(30) + , bool handle_redirect = true); + + void start(std::string const& hostname, std::string const& port + , time_duration timeout, bool handle_redirect = true); + void close(); + +private: + + void on_resolve(asio::error_code const& e + , tcp::resolver::iterator i); + void connect(int ticket, tcp::endpoint target_address); + void on_connect_timeout(); + void on_connect(asio::error_code const& e +/* , tcp::resolver::iterator i*/); + void on_write(asio::error_code const& e); + void on_read(asio::error_code const& e, std::size_t bytes_transferred); + static void on_timeout(boost::weak_ptr p + , asio::error_code const& e); + void on_assign_bandwidth(asio::error_code const& e); + + std::vector m_recvbuffer; + tcp::socket m_sock; + int m_read_pos; + tcp::resolver m_resolver; + http_parser m_parser; + http_handler m_handler; + deadline_timer m_timer; + time_duration m_timeout; + ptime m_last_receive; + // bottled means that the handler is called once, when + // everything is received (and buffered in memory). + // non bottled means that once the headers have been + // received, data is streamed to the handler + bool m_bottled; + // set to true the first time the handler is called + bool m_called; + std::string m_hostname; + std::string m_port; + + // the current download limit, in bytes per second + // 0 is unlimited. + int m_rate_limit; + + // the number of bytes we are allowed to receive + int m_download_quota; + + // only hand out new quota 4 times a second if the + // quota is 0. If it isn't 0 wait for it to reach + // 0 and continue to hand out quota at that time. + bool m_limiter_timer_active; + + // the timer fires every 250 millisecond as long + // as all the quota was used. + deadline_timer m_limiter_timer; + + // if set to true, the connection should handle + // HTTP redirects. + bool m_redirect; + + int m_connection_ticket; + connection_queue& m_cc; +}; + +} + +#endif diff --git a/libtorrent/include/libtorrent/http_stream.hpp b/libtorrent/include/libtorrent/http_stream.hpp new file mode 100644 index 000000000..2bd124b43 --- /dev/null +++ b/libtorrent/include/libtorrent/http_stream.hpp @@ -0,0 +1,193 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class http_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit http_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + , m_no_connect(false) + {} + + void set_no_connect(bool c) { m_no_connect = c; } + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.io_control(ioc); + } + + template + void io_control(IO_Control_Command& ioc, asio::error_code& ec) + { + m_sock.io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. send HTTP CONNECT method and possibly username+password + // 4. read CONNECT response + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname + , boost::lexical_cast(m_port)); + m_resolver.async_resolve(q, boost::bind( + &http_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(asio::error_code const& e, boost::shared_ptr h); + void handshake1(asio::error_code const& e, boost::shared_ptr h); + void handshake2(asio::error_code const& e, boost::shared_ptr h); + + stream_socket m_sock; + // the http proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; + // this is true if the connection is HTTP based and + // want to talk directly to the proxy + bool m_no_connect; +}; + +} + diff --git a/libtorrent/include/libtorrent/http_tracker_connection.hpp b/libtorrent/include/libtorrent/http_tracker_connection.hpp index 589f62fbb..35d529504 100644 --- a/libtorrent/include/libtorrent/http_tracker_connection.hpp +++ b/libtorrent/include/libtorrent/http_tracker_connection.hpp @@ -43,9 +43,9 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -59,6 +59,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/tracker_manager.hpp" #include "libtorrent/config.hpp" #include "libtorrent/buffer.hpp" +#include "libtorrent/socket_type.hpp" +#include "libtorrent/connection_queue.hpp" namespace libtorrent { @@ -114,6 +116,7 @@ namespace libtorrent http_tracker_connection( asio::strand& str + , connection_queue& cc , tracker_manager& man , tracker_request const& req , std::string const& hostname @@ -122,6 +125,7 @@ namespace libtorrent , address bind_infc , boost::weak_ptr c , session_settings const& stn + , proxy_settings const& ps , std::string const& password = ""); private: @@ -136,6 +140,7 @@ namespace libtorrent , std::string const& request); void name_lookup(asio::error_code const& error, tcp::resolver::iterator i); + void connect(int ticket, tcp::endpoint target_address); void connected(asio::error_code const& error); void sent(asio::error_code const& error); void receive(asio::error_code const& error @@ -152,15 +157,19 @@ namespace libtorrent asio::strand& m_strand; tcp::resolver m_name_lookup; int m_port; - boost::shared_ptr m_socket; + boost::shared_ptr m_socket; int m_recv_pos; std::vector m_buffer; std::string m_send_buffer; session_settings const& m_settings; + proxy_settings const& m_proxy; std::string m_password; bool m_timed_out; + + int m_connection_ticket; + connection_queue& m_cc; }; } diff --git a/libtorrent/include/libtorrent/instantiate_connection.hpp b/libtorrent/include/libtorrent/instantiate_connection.hpp new file mode 100644 index 000000000..49cb1fe18 --- /dev/null +++ b/libtorrent/include/libtorrent/instantiate_connection.hpp @@ -0,0 +1,49 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_INSTANTIATE_CONNECTION +#define TORRENT_INSTANTIATE_CONNECTION + +#include "libtorrent/socket_type.hpp" +#include +#include + +namespace libtorrent +{ + struct proxy_settings; + + boost::shared_ptr instantiate_connection( + asio::io_service& ios, proxy_settings const& ps); +} + +#endif + diff --git a/libtorrent/include/libtorrent/io.hpp b/libtorrent/include/libtorrent/io.hpp index 57a22cf97..f73c3e290 100644 --- a/libtorrent/include/libtorrent/io.hpp +++ b/libtorrent/include/libtorrent/io.hpp @@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_IO_HPP_INCLUDED #include +#include namespace libtorrent { @@ -134,6 +135,18 @@ namespace libtorrent void write_int8(boost::int8_t val, OutIt& start) { write_impl(val, start); } + inline void write_string(std::string const& str, char*& start) + { + std::copy(str.begin(), str.end(), start); + start += str.size(); + } + + template + void write_string(std::string const& str, OutIt& start) + { + std::copy(str.begin(), str.end(), start); + } + } } diff --git a/libtorrent/include/libtorrent/ip_filter.hpp b/libtorrent/include/libtorrent/ip_filter.hpp index c75dafd66..1c62c553b 100644 --- a/libtorrent/include/libtorrent/ip_filter.hpp +++ b/libtorrent/include/libtorrent/ip_filter.hpp @@ -75,7 +75,7 @@ namespace detail // a filter for a specific address type. // it works with IPv4 and IPv6 template - class filter_impl + class TORRENT_EXPORT filter_impl { public: diff --git a/libtorrent/include/libtorrent/lsd.hpp b/libtorrent/include/libtorrent/lsd.hpp new file mode 100644 index 000000000..9ffbcdfc3 --- /dev/null +++ b/libtorrent/include/libtorrent/lsd.hpp @@ -0,0 +1,105 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_LSD_HPP +#define TORRENT_LSD_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" + +#include +#include +#include +#include +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +typedef boost::function peer_callback_t; + +class lsd : boost::noncopyable +{ +public: + lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb); + ~lsd(); + + void rebind(address const& listen_interface); + + void announce(sha1_hash const& ih, int listen_port); + void close(); + +private: + + static address_v4 lsd_multicast_address; + static udp::endpoint lsd_multicast_endpoint; + + void resend_announce(asio::error_code const& e, std::string msg); + void on_announce(asio::error_code const& e + , std::size_t bytes_transferred); + void setup_receive(); + + peer_callback_t m_callback; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_receive_buffer[1024]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to send and receive + // multicast messages on + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + bool m_disabled; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/libtorrent/include/libtorrent/natpmp.hpp b/libtorrent/include/libtorrent/natpmp.hpp new file mode 100644 index 000000000..1c0ffd0be --- /dev/null +++ b/libtorrent/include/libtorrent/natpmp.hpp @@ -0,0 +1,150 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_NATPMP_HPP +#define TORRENT_NATPMP_HPP + +#include "libtorrent/socket.hpp" + +#include + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) +#include +#endif + +namespace libtorrent +{ + +// int: external tcp port +// int: external udp port +// std::string: error message +typedef boost::function portmap_callback_t; + +class natpmp +{ +public: + natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + void set_mappings(int tcp, int udp); + + void close(); + +private: + + void update_mapping(int i, int port); + void send_map_request(int i); + void resend_request(int i, asio::error_code const& e); + void on_reply(asio::error_code const& e + , std::size_t bytes_transferred); + void try_next_mapping(int i); + void update_expiration_timer(); + void refresh_mapping(int i); + void mapping_expired(asio::error_code const& e, int i); + + struct mapping + { + mapping() + : need_update(false) + , local_port(0) + , external_port(0) + , protocol(1) + {} + + // indicates that the mapping has changed + // and needs an update + bool need_update; + + // the time the port mapping will expire + ptime expires; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + // 1 = udp, 2 = tcp + int protocol; + }; + + portmap_callback_t m_callback; + + // 0 is tcp and 1 is udp + mapping m_mappings[2]; + + // the endpoint to the nat router + udp::endpoint m_nat_endpoint; + + // this is the mapping that is currently + // being updated. It is -1 in case no + // mapping is being updated at the moment + int m_currently_mapping; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_response_buffer[16]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the udp socket used to communicate + // with the NAT router + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_send_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + bool m_disabled; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/libtorrent/include/libtorrent/pch.hpp b/libtorrent/include/libtorrent/pch.hpp new file mode 100644 index 000000000..735999826 --- /dev/null +++ b/libtorrent/include/libtorrent/pch.hpp @@ -0,0 +1,96 @@ +#ifdef BOOST_BUILD_PCH_ENABLED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#define Protocol Protocol_ +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +#undef Protocol +#endif + +#endif + diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 0763ed56c..9b3de295c 100644 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -49,7 +49,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include @@ -72,6 +71,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/session.hpp" #include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" // TODO: each time a block is 'taken over' // from another peer. That peer must be given @@ -88,7 +89,7 @@ namespace libtorrent } TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); - TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); + TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); struct TORRENT_EXPORT protocol_error: std::runtime_error { @@ -99,8 +100,8 @@ namespace libtorrent : public boost::noncopyable { friend class invariant_access; - friend void intrusive_ptr_add_ref(peer_connection const*); - friend void intrusive_ptr_release(peer_connection const*); + friend TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*); + friend TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*); public: enum channels @@ -116,18 +117,28 @@ namespace libtorrent peer_connection( aux::session_impl& ses , boost::weak_ptr t - , boost::shared_ptr s + , boost::shared_ptr s , tcp::endpoint const& remote - , tcp::endpoint const& proxy); + , policy::peer* peerinfo); // with this constructor we have been contacted and we still don't // know which torrent the connection belongs to peer_connection( aux::session_impl& ses - , boost::shared_ptr s); + , boost::shared_ptr s + , policy::peer* peerinfo); virtual ~peer_connection(); + void set_peer_info(policy::peer* pi) + { m_peer_info = pi; } + + policy::peer* peer_info_struct() const + { return m_peer_info; } + + enum peer_speed_t { slow, medium, fast }; + peer_speed_t peer_speed(); + #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::shared_ptr); #endif @@ -145,6 +156,9 @@ namespace libtorrent void set_upload_limit(int limit); void set_download_limit(int limit); + int upload_limit() const { return m_upload_limit; } + int download_limit() const { return m_download_limit; } + bool prefer_whole_pieces() const { return m_prefer_whole_pieces; } @@ -160,6 +174,9 @@ namespace libtorrent void set_non_prioritized(bool b) { m_non_prioritized = b; } + bool on_parole() const + { return m_on_parole; } + // this adds an announcement in the announcement queue // it will let the peer know that we have the given piece void announce_piece(int index); @@ -190,6 +207,8 @@ namespace libtorrent bool is_peer_interested() const { return m_peer_interested; } bool has_peer_choked() const { return m_peer_choked; } + void update_interest(); + // returns the torrent this connection is a part of // may be zero if the connection is an incoming connection // and it hasn't received enough information to determine @@ -203,12 +222,12 @@ namespace libtorrent // is called once every second by the main loop void second_tick(float tick_interval); - boost::shared_ptr get_socket() const { return m_socket; } + boost::shared_ptr get_socket() const { return m_socket; } tcp::endpoint const& remote() const { return m_remote; } - tcp::endpoint const& proxy() const { return m_remote_proxy; } std::vector const& get_bitfield() const; + void timed_out(); // this will cause this peer_connection to be disconnected. void disconnect(); bool is_disconnecting() const { return m_disconnecting; } @@ -231,7 +250,7 @@ namespace libtorrent // initiate the tcp connection. This may be postponed until // the library isn't using up the limitation of half-open // tcp connections. - void connect(); + void connect(int ticket); // This is called for every peer right after the upload // bandwidth has been distributed among them @@ -253,6 +272,10 @@ namespace libtorrent // if it was an incoming connection, it is remote bool is_local() const { return m_active; } + bool on_local_network() const; + bool ignore_bandwidth_limits() const + { return m_ignore_bandwidth_limits; } + void set_failed() { m_failed = true; } bool failed() const { return m_failed; } @@ -309,7 +332,7 @@ namespace libtorrent #ifndef NDEBUG void check_invariant() const; - boost::posix_time::ptime m_last_choke; + ptime m_last_choke; #endif virtual void get_peer_info(peer_info& p) const = 0; @@ -336,6 +359,7 @@ namespace libtorrent buffer::interval allocate_send_buffer(int size); void setup_send(); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void set_country(char const* c) { assert(strlen(c) == 2); @@ -343,6 +367,7 @@ namespace libtorrent m_country[1] = c[1]; } bool has_country() const { return m_country[0] != 0; } +#endif protected: @@ -432,11 +457,13 @@ namespace libtorrent extension_list_t m_extensions; #endif +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES // in case the session settings is set // to resolve countries, this is set to // the two character country code this // peer resides in. char m_country[2]; +#endif private: @@ -447,7 +474,7 @@ namespace libtorrent // the time when we last got a part of a // piece packet from this peer - boost::posix_time::ptime m_last_piece; + ptime m_last_piece; int m_packet_size; int m_recv_pos; @@ -474,18 +501,15 @@ namespace libtorrent int m_write_pos; // timeouts - boost::posix_time::ptime m_last_receive; - boost::posix_time::ptime m_last_sent; + ptime m_last_receive; + ptime m_last_sent; - boost::shared_ptr m_socket; + boost::shared_ptr m_socket; // this is the peer we're actually talking to // it may not necessarily be the peer we're // connected to, in case we use a proxy tcp::endpoint m_remote; - // if we use a proxy, this is the address to it - tcp::endpoint m_remote_proxy; - // this is the torrent this connection is // associated with. If the connection is an // incoming conncetion, this is set to zero @@ -520,6 +544,11 @@ namespace libtorrent // this peer bool m_failed; + // if this is set to true, the peer will not + // request bandwidth from the limiter, but instead + // just send and receive as much as possible. + bool m_ignore_bandwidth_limits; + // the pieces the other end have std::vector m_have_piece; @@ -583,11 +612,11 @@ namespace libtorrent // the time when this peer sent us a not_interested message // the last time. - boost::posix_time::ptime m_became_uninterested; + ptime m_became_uninterested; // the time when we sent a not_interested message to // this peer the last time. - boost::posix_time::ptime m_became_uninteresting; + ptime m_became_uninteresting; // this is true until this socket has become // writable for the first time (i.e. the @@ -616,6 +645,14 @@ namespace libtorrent // are preferred. bool m_prefer_whole_pieces; + // if this is true, the peer has previously participated + // in a piece that failed the piece hash check. This will + // put the peer on parole and only request entire pieces. + // if a piece pass that was partially requested from this + // peer it will leave parole mode and continue download + // pieces as normal peers. + bool m_on_parole; + // if this is true, the blocks picked by the piece // picker will be merged before passed to the // request function. i.e. subsequent blocks are @@ -636,6 +673,21 @@ namespace libtorrent int m_upload_limit; int m_download_limit; + // this peer's peer info struct. This may + // be 0, in case the connection is incoming + // and hasn't been added to a torrent yet. + policy::peer* m_peer_info; + + // this is a measurement of how fast the peer + // it allows some variance without changing + // back and forth between states + peer_speed_t m_speed; + + // the ticket id from the connection queue. + // This is used to identify the connection + // so that it can be removed from the queue + // once the connection completes + int m_connection_ticket; #ifndef NDEBUG public: bool m_in_constructor; diff --git a/libtorrent/include/libtorrent/peer_info.hpp b/libtorrent/include/libtorrent/peer_info.hpp index c293fd345..86480ba11 100644 --- a/libtorrent/include/libtorrent/peer_info.hpp +++ b/libtorrent/include/libtorrent/peer_info.hpp @@ -56,7 +56,20 @@ namespace libtorrent connecting = 0x80, queued = 0x100 }; + unsigned int flags; + + enum peer_source_flags + { + tracker = 0x1, + dht = 0x2, + pex = 0x4, + lsd = 0x8, + resume_data = 0x10 + }; + + int source; + tcp::endpoint ip; float up_speed; float down_speed; @@ -69,12 +82,14 @@ namespace libtorrent bool seed; // true if this is a seed int upload_limit; int download_limit; - + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES // in case the session settings is set // to resolve countries, this is set to // the two character country code this // peer resides in. char country[2]; +#endif size_type load_balancing; @@ -89,6 +104,10 @@ namespace libtorrent // that we haven't sent yet int upload_queue_length; + // the number of times this IP + // has failed to connect + int failcount; + // the currently downloading piece // if piece index is -1 all associated // members are just set to 0 diff --git a/libtorrent/include/libtorrent/piece_picker.hpp b/libtorrent/include/libtorrent/piece_picker.hpp index 429ec34f6..7b8612909 100644 --- a/libtorrent/include/libtorrent/piece_picker.hpp +++ b/libtorrent/include/libtorrent/piece_picker.hpp @@ -36,12 +36,14 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef _MSC_VER #pragma warning(push, 1) #endif #include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -86,28 +88,38 @@ namespace libtorrent { public: - enum { max_blocks_per_piece = 256 }; - struct block_info { - block_info(): num_downloads(0) {} + block_info(): num_downloads(0), requested(0), finished(0) {} // the peer this block was requested or // downloaded from tcp::endpoint peer; // the number of times this block has been downloaded - int num_downloads; + unsigned num_downloads:14; + unsigned requested:1; + unsigned finished:1; }; + // the peers that are downloading this piece + // are considered fast peers or slow peers. + // none is set if the blocks were downloaded + // in a previous session + enum piece_state_t + { none, slow, medium, fast }; + struct downloading_piece { + downloading_piece(): finished(0), requested(0) {} + piece_state_t state; + + // the index of the piece int index; - // each bit represents a block in the piece - // set to one if the block has been requested - std::bitset requested_blocks; - // the bit is set to one if the block has been acquired - std::bitset finished_blocks; // info about each block - block_info info[max_blocks_per_piece]; + // this is a pointer into the m_block_info + // vector owned by the piece_picker + block_info* info; + boost::uint16_t finished; + boost::uint16_t requested; }; piece_picker(int blocks_per_piece @@ -115,8 +127,6 @@ namespace libtorrent void set_sequenced_download_threshold(int sequenced_download_threshold); - // this is called before any other method is called - // after the local files has been checked. // the vector tells which pieces we already have // and which we don't have. void files_checked( @@ -130,6 +140,12 @@ namespace libtorrent // decreases the peer count for the given piece // (used when a peer disconnects) void dec_refcount(int index); + + // these will increase and decrease the peer count + // of all pieces. They are used when seeds join + // or leave the swarm. + void inc_refcount_all(); + void dec_refcount_all(); // This indicates that we just received this piece // it means that the refcounter will indicate that @@ -137,23 +153,22 @@ namespace libtorrent // (i.e. we don't have to maintain a refcount) void we_have(int index); - // This will mark a piece as unfiltered, and if it was - // previously marked as filtered, it will be considered - // interesting again and be placed in the piece list available - // for downloading. - void mark_as_unfiltered(int index); + // sets the priority of a piece. + void set_piece_priority(int index, int prio); - // This will mark a piece as filtered. The piece will be - // removed from the list of pieces avalable for downloading - // and hence, will not be downloaded. - void mark_as_filtered(int index); + // returns the priority for the piece at 'index' + int piece_priority(int index) const; - // returns true if the pieces at 'index' is marked as filtered - bool is_filtered(int index) const; + // returns the current piece priorities for all pieces + void piece_priorities(std::vector& pieces) const; + + // ========== start deprecation ============== // fills the bitmask with 1's for pieces that are filtered void filtered_pieces(std::vector& mask) const; + // ========== end deprecation ============== + // pieces should be the vector that represents the pieces a // client has. It returns a list of all pieces that this client // has and that are interesting to download. It returns them in @@ -168,7 +183,7 @@ namespace libtorrent void pick_pieces(const std::vector& pieces , std::vector& interesting_blocks , int num_pieces, bool prefer_whole_pieces - , tcp::endpoint peer) const; + , tcp::endpoint peer, piece_state_t speed) const; // returns true if any client is currently downloading this // piece-block, or if it's queued for downloading by some client @@ -177,7 +192,8 @@ namespace libtorrent bool is_finished(piece_block block) const; // marks this piece-block as queued for downloading - void mark_as_downloading(piece_block block, tcp::endpoint const& peer); + void mark_as_downloading(piece_block block, tcp::endpoint const& peer + , piece_state_t s); void mark_as_finished(piece_block block, tcp::endpoint const& peer); // if a piece had a hash-failure, it must be restored and @@ -209,6 +225,7 @@ namespace libtorrent // the number of filtered pieces we already have int num_have_filtered() const { return m_num_have_filtered; } + #ifndef NDEBUG // used in debug mode void check_invariant(const torrent* t = 0) const; @@ -236,7 +253,7 @@ namespace libtorrent piece_pos(int peer_count_, int index_) : peer_count(peer_count_) , downloading(0) - , filtered(0) + , piece_priority(1) , index(index_) { assert(peer_count_ >= 0); @@ -244,26 +261,59 @@ namespace libtorrent } // selects which vector to look in - unsigned peer_count : 11; + unsigned peer_count : 10; // is 1 if the piece is marked as being downloaded unsigned downloading : 1; - // is 1 if the piece is filtered (not to be downloaded) - unsigned filtered : 1; + // is 0 if the piece is filtered (not to be downloaded) + // 1 is normal priority (default) + // 2 is higher priority than pieces at the same availability level + // 3 is same priority as partial pieces + // 4 is higher priority than partial pieces + // 5 and 6 same priority as availability 1 (ignores availability) + // 7 is maximum priority (ignores availability) + unsigned piece_priority : 3; // index in to the piece_info vector - unsigned index : 19; + unsigned index : 18; - enum { we_have_index = 0x3ffff }; + enum + { + // index is set to this to indicate that we have the + // piece. There is no entry for the piece in the + // buckets if this is the case. + we_have_index = 0x3ffff, + // the priority value that means the piece is filtered + filter_priority = 0, + // the max number the peer count can hold + max_peer_count = 0x3ff + }; + + bool have() const { return index == we_have_index; } + void set_have() { index = we_have_index; assert(have()); } + + bool filtered() const { return piece_priority == filter_priority; } + void filtered(bool f) { piece_priority = f ? filter_priority : 0; } int priority(int limit) const { - return peer_count >= (unsigned)limit ? limit : peer_count; + if (filtered() || have()) return 0; + // pieces we are currently downloading have high priority + int prio = downloading ? (std::min)(1, int(peer_count)) : peer_count * 2; + // if the peer_count is 0 or 1, the priority cannot be higher + if (prio <= 1) return prio; + if (prio >= limit * 2) prio = limit * 2; + // the different priority levels + switch (piece_priority) + { + case 2: return prio - 1; + case 3: return (std::max)(prio / 2, 1); + case 4: return (std::max)(prio / 2 - 1, 1); + case 5: + case 6: return (std::min)(prio / 2 - 1, 2); + case 7: return 1; + } + return prio; } - - bool ordered(int limit) const - { - return peer_count >= (unsigned)limit; - } - + bool operator!=(piece_pos p) const { return index != p.index || peer_count != p.peer_count; } @@ -272,42 +322,36 @@ namespace libtorrent }; + BOOST_STATIC_ASSERT(sizeof(piece_pos) == sizeof(char) * 4); + + bool is_ordered(int priority) const + { + return priority >= m_sequenced_download_threshold * 2; + } void add(int index); - void move(bool downloading, bool filtered, int vec_index, int elem_index); - void remove(bool downloading, bool filtered, int vec_index, int elem_index); - std::vector >& pick_piece_info_vector(bool downloading - , bool filtered); + void move(int vec_index, int elem_index); - std::vector > const& pick_piece_info_vector( - bool downloading, bool filtered) const; - - int add_interesting_blocks_free(const std::vector& piece_list - , const std::vector& pieces - , std::vector& interesting_blocks - , int num_blocks, bool prefer_whole_pieces) const; - - int add_interesting_blocks_partial(const std::vector& 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; + int add_interesting_blocks(const std::vector& piece_list + , const std::vector& pieces + , std::vector& interesting_blocks + , std::vector& backup_blocks + , int num_blocks, bool prefer_whole_pieces + , tcp::endpoint peer, piece_state_t speed) const; + downloading_piece& add_download_piece(); + void erase_download_piece(std::vector::iterator i); // this vector contains all pieces we don't have. // in the first entry (index 0) is a vector of all pieces // that no peer have, the vector at index 1 contains // all pieces that exactly one peer have, index 2 contains // all pieces exactly two peers have and so on. + // this is not entirely true. The availibility of a piece + // is adjusted depending on its priority. But the principle + // is that the higher index, the lower priority a piece has. std::vector > m_piece_info; - // this vector has the same structure as m_piece_info - // but only contains pieces we are currently downloading - // they have higher priority than pieces we aren't downloading - // during piece picking - std::vector > m_downloading_piece_info; - // this maps indices to number of peers that has this piece and // index into the m_piece_info vectors. // piece_pos::we_have_index means that we have the piece, so it @@ -322,6 +366,15 @@ namespace libtorrent // is being downloaded std::vector m_downloads; + // this holds the information of the + // blocks in partially downloaded pieces. + // the first m_blocks_per_piece entries + // in the vector belongs to the first + // entry in m_downloads, the second + // m_blocks_per_piece entries to the + // second entry in m_downloads and so on. + std::vector m_block_info; + int m_blocks_per_piece; int m_blocks_in_last_piece; @@ -333,6 +386,9 @@ namespace libtorrent // the number of pieces we have that also are filtered int m_num_have_filtered; + + // the number of pieces we have + int m_num_have; // the required popularity of a piece in order to download // it in sequence instead of random order. diff --git a/libtorrent/include/libtorrent/policy.hpp b/libtorrent/include/libtorrent/policy.hpp index 82d04eecc..4fa04cb32 100644 --- a/libtorrent/include/libtorrent/policy.hpp +++ b/libtorrent/include/libtorrent/policy.hpp @@ -40,8 +40,6 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(push, 1) #endif -#include - #ifdef _MSC_VER #pragma warning(pop) #endif @@ -52,6 +50,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/size_type.hpp" #include "libtorrent/invariant_check.hpp" #include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" namespace libtorrent { @@ -85,24 +84,16 @@ namespace libtorrent void pulse(); // this is called once for every peer we get from - // the tracker - void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid); + // the tracker, pex, lsd or dht. + void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid + , int source, char flags); // called when an incoming connection is accepted void new_connection(peer_connection& c); - // this is called if a peer timed-out or - // forcefully closed the connection. This - // will mark the connection as non-reconnectale - void peer_failed(peer_connection const& c); - // the given connection was just closed void connection_closed(const peer_connection& c); - // is called when a peer is believed to have - // sent invalid data - void ban_peer(const peer_connection& c); - // the peer has got at least one interesting piece void peer_is_interesting(peer_connection& c); @@ -132,7 +123,7 @@ namespace libtorrent { enum connection_type { not_connectable,connectable }; - peer(const tcp::endpoint& ip, connection_type t); + peer(const tcp::endpoint& ip, connection_type t, int src); size_type total_download() const; size_type total_upload() const; @@ -144,13 +135,19 @@ namespace libtorrent tcp::endpoint ip; connection_type type; + // the number of failed connection attempts this peer has + int failcount; + + // this is true if the peer is a seed + bool seed; + // the time when this peer was optimistically unchoked // the last time. - boost::posix_time::ptime last_optimistically_unchoked; + libtorrent::ptime last_optimistically_unchoked; // the time when the peer connected to us // or disconnected if it isn't connected right now - boost::posix_time::ptime connected; + libtorrent::ptime connected; // this is the accumulated amount of // uploaded and downloaded data to this @@ -167,6 +164,10 @@ namespace libtorrent // is set to true if this peer has been banned bool banned; + // a bitmap combining the peer_source flags + // from peer_info. + int source; + // if the peer is connected now, this // will refer to a valid peer_connection peer_connection* connection; @@ -182,49 +183,32 @@ namespace libtorrent return m_num_unchoked; } - typedef std::vector::iterator iterator; + typedef std::list::iterator iterator; + typedef std::list::const_iterator const_iterator; iterator begin_peer() { return m_peers.begin(); } iterator end_peer() { return m_peers.end(); } + bool connect_one_peer(); + private: bool unchoke_one_peer(); void choke_one_peer(); - peer* find_choke_candidate(); - peer* find_unchoke_candidate(); + iterator find_choke_candidate(); + iterator find_unchoke_candidate(); // the seed prefix means that the // function is used while seeding. bool seed_unchoke_one_peer(); void seed_choke_one_peer(); - peer* find_seed_choke_candidate(); - peer* find_seed_unchoke_candidate(); + iterator find_seed_choke_candidate(); + iterator find_seed_unchoke_candidate(); - bool connect_peer(peer *); - bool connect_one_peer(); bool disconnect_one_peer(); - peer* find_disconnect_candidate(); - peer* find_connect_candidate(); + iterator find_disconnect_candidate(); + iterator find_connect_candidate(); - // a functor that identifies peers that have disconnected and that - // are too old for still being saved. - struct old_disconnected_peer - { - bool operator()(const peer& p) - { - using namespace boost::posix_time; - - ptime not_tried_yet(boost::gregorian::date(1970,boost::gregorian::Jan,1)); - - // this timeout has to be customizable! - return p.connection == 0 - && p.connected != not_tried_yet - && second_clock::universal_time() - p.connected > minutes(30); - } - }; - - - std::vector m_peers; + std::list m_peers; torrent* m_torrent; @@ -239,7 +223,7 @@ namespace libtorrent // if there is a connection limit, // we disconnect one peer every minute in hope of // establishing a connection with a better peer - boost::posix_time::ptime m_last_optimistic_disconnect; + ptime m_last_optimistic_disconnect; }; } diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index 454b34f7e..7cd9961bd 100644 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -61,6 +61,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/fingerprint.hpp" #include "libtorrent/resource_request.hpp" +#include "libtorrent/storage.hpp" #ifdef _MSC_VER # include @@ -71,7 +72,7 @@ namespace libtorrent struct torrent_plugin; class torrent; class ip_filter; - + class connection_queue; namespace aux { @@ -137,7 +138,8 @@ namespace libtorrent , boost::filesystem::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true - , int block_size = 16 * 1024); + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor); // TODO: deprecated, this is for backwards compatibility only torrent_handle add_torrent( @@ -145,10 +147,11 @@ namespace libtorrent , boost::filesystem::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true - , int block_size = 16 * 1024) + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor) { return add_torrent(torrent_info(e), save_path, resume_data - , compact_mode, block_size); + , compact_mode, block_size, sc); } torrent_handle add_torrent( @@ -158,7 +161,8 @@ namespace libtorrent , boost::filesystem::path const& save_path , entry const& resume_data = entry() , bool compact_mode = true - , int block_size = 16 * 1024); + , int block_size = 16 * 1024 + , storage_constructor_type sc = default_storage_constructor); session_proxy abort() { return session_proxy(m_impl); } @@ -174,9 +178,7 @@ namespace libtorrent #endif #ifndef TORRENT_DISABLE_EXTENSIONS - void add_extension(boost::function(torrent*)> ext); - #endif void set_ip_filter(ip_filter const& f); @@ -215,6 +217,19 @@ namespace libtorrent void set_settings(session_settings const& s); session_settings const& settings(); + void set_peer_proxy(proxy_settings const& s); + void set_web_seed_proxy(proxy_settings const& s); + void set_tracker_proxy(proxy_settings const& s); + + proxy_settings const& peer_proxy() const; + proxy_settings const& web_seed_proxy() const; + proxy_settings const& tracker_proxy() const; + +#ifndef TORRENT_DISABLE_DHT + void set_dht_proxy(proxy_settings const& s); + proxy_settings const& dht_proxy() const; +#endif + int upload_rate_limit() const; int download_rate_limit() const; @@ -227,6 +242,8 @@ namespace libtorrent std::auto_ptr pop_alert(); void set_severity_level(alert::severity_t s); + connection_queue& get_connection_queue(); + // Resource management used for global limits. resource_request m_ul_bandwidth_quota; resource_request m_dl_bandwidth_quota; diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index 7f6418d74..363384a70 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -34,16 +34,53 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_SESSION_SETTINGS_HPP_INCLUDED #include "libtorrent/version.hpp" +#include "libtorrent/config.hpp" namespace libtorrent { + struct TORRENT_EXPORT proxy_settings + { + proxy_settings() : port(0), type(none) {} + + std::string hostname; + int port; + + std::string username; + std::string password; + + enum proxy_type + { + // a plain tcp socket is used, and + // the other settings are ignored. + none, + // the hostname and port settings are + // used to connect to the proxy. No + // username or password is sent. + socks5, + // the hostname and port are used to + // connect to the proxy. the username + // and password are used to authenticate + // with the proxy server. + socks5_pw, + // the http proxy is only available for + // tracker and web seed traffic + // assumes anonymous access to proxy + http, + // http proxy with basic authentication + // uses username and password + http_pw + }; + + proxy_type type; + + }; + struct TORRENT_EXPORT session_settings { session_settings(std::string const& user_agent_ = "libtorrent/" LIBTORRENT_VERSION) - : proxy_port(0) - , user_agent(user_agent_) + : user_agent(user_agent_) , tracker_completion_timeout(60) , tracker_receive_timeout(20) , stop_tracker_timeout(10) @@ -58,16 +95,15 @@ namespace libtorrent , urlseed_pipeline_size(5) , file_pool_size(40) , allow_multiple_connections_per_ip(false) + , max_failcount(3) + , min_reconnect_time(60) + , peer_connect_timeout(10) + , ignore_limits_on_local_network(true) #ifndef TORRENT_DISABLE_DHT , use_dht_as_fallback(true) #endif {} - std::string proxy_ip; - int proxy_port; - std::string proxy_login; - std::string proxy_password; - // this is the user agent that will be sent to the tracker // when doing requests. It is used to identify the client. // It cannot contain \r or \n @@ -154,6 +190,23 @@ namespace libtorrent // IP address. true will allow it. bool allow_multiple_connections_per_ip; + // the number of times we can fail to connect to a peer + // before we stop retrying it. + int max_failcount; + + // the number of seconds to wait to reconnect to a peer. + // this time is multiplied with the failcount. + int min_reconnect_time; + + // this is the timeout for a connection attempt. If + // the connect does not succeed within this time, the + // connection is dropped. The time is specified in seconds. + int peer_connect_timeout; + + // if set to true, upload, download and unchoke limits + // are ignored for peers on the local network + bool ignore_limits_on_local_network; + #ifndef TORRENT_DISABLE_DHT // while this is true, the dht will note be used unless the // tracker is online @@ -167,7 +220,7 @@ namespace libtorrent dht_settings() : max_peers_reply(50) , search_branching(5) - , service_port(6881) + , service_port(0) , max_fail_count(20) {} @@ -180,6 +233,7 @@ namespace libtorrent int search_branching; // the listen port for the dht. This is a UDP port. + // zero means use the same as the tcp interface int service_port; // the maximum number of times a node can fail diff --git a/libtorrent/include/libtorrent/session_status.hpp b/libtorrent/include/libtorrent/session_status.hpp index 834044e36..adbb1b57d 100644 --- a/libtorrent/include/libtorrent/session_status.hpp +++ b/libtorrent/include/libtorrent/session_status.hpp @@ -59,6 +59,7 @@ namespace libtorrent int dht_nodes; int dht_node_cache; int dht_torrents; + size_type dht_global_nodes; #endif }; diff --git a/libtorrent/include/libtorrent/socket.hpp b/libtorrent/include/libtorrent/socket.hpp index 5df7a4579..c478a92ef 100644 --- a/libtorrent/include/libtorrent/socket.hpp +++ b/libtorrent/include/libtorrent/socket.hpp @@ -51,12 +51,15 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include +#include #ifdef __OBJC__ #undef Protocol #endif #include "libtorrent/io.hpp" +#include "libtorrent/time.hpp" #ifdef _MSC_VER #pragma warning(pop) @@ -64,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + /* namespace asio = boost::asio; @@ -91,7 +95,8 @@ namespace libtorrent typedef asio::io_service io_service; using asio::async_write; - using asio::deadline_timer; + + typedef asio::basic_deadline_timer deadline_timer; namespace detail { diff --git a/miniupnpc/LICENCE b/libtorrent/include/libtorrent/socket_type.hpp similarity index 57% rename from miniupnpc/LICENCE rename to libtorrent/include/libtorrent/socket_type.hpp index 80d8e0cb5..f81d12342 100644 --- a/miniupnpc/LICENCE +++ b/libtorrent/include/libtorrent/socket_type.hpp @@ -1,16 +1,20 @@ -Copyright (c) 2005-2006, Thomas BERNARD +/* + +Copyright (c) 2007, 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: +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. - * The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. + * 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 @@ -24,3 +28,19 @@ 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_SOCKET_TYPE +#define TORRENT_SOCKET_TYPE + +#include "libtorrent/socks5_stream.hpp" +#include "libtorrent/http_stream.hpp" +#include "libtorrent/variant_stream.hpp" + +namespace libtorrent +{ + typedef variant_stream socket_type; +} + +#endif + diff --git a/libtorrent/include/libtorrent/socks5_stream.hpp b/libtorrent/include/libtorrent/socks5_stream.hpp new file mode 100644 index 000000000..9e8a0d04b --- /dev/null +++ b/libtorrent/include/libtorrent/socks5_stream.hpp @@ -0,0 +1,195 @@ +#include "libtorrent/io.hpp" +#include "libtorrent/socket.hpp" +#include +#include +#include +#include +#include + + +namespace libtorrent { + +class socks5_stream : boost::noncopyable +{ +public: + + typedef stream_socket::lowest_layer_type lowest_layer_type; + typedef stream_socket::endpoint_type endpoint_type; + typedef stream_socket::protocol_type protocol_type; + + explicit socks5_stream(asio::io_service& io_service) + : m_sock(io_service) + , m_resolver(io_service) + {} + + void set_proxy(std::string hostname, int port) + { + m_hostname = hostname; + m_port = port; + } + + void set_username(std::string const& user + , std::string const& password) + { + m_user = user; + m_password = password; + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + m_sock.async_read_some(buffers, handler); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) + { + return m_sock.read_some(buffers, ec); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + return m_sock.read_some(buffers); + } + + template + void io_control(IO_Control_Command& ioc) + { + m_sock.io_control(ioc); + } + + template + void io_control(IO_Control_Command& ioc, asio::error_code& ec) + { + m_sock.io_control(ioc, ec); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + m_sock.async_write_some(buffers, handler); + } + + void bind(endpoint_type const& endpoint) + { + m_sock.bind(endpoint); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + m_sock.bind(endpoint, error_handler); + } + + void open(protocol_type const& p) + { + m_sock.open(p); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + m_sock.open(p, error_handler); + } + + void close() + { + m_remote_endpoint = endpoint_type(); + m_sock.close(); + } + + template + void close(Error_Handler const& error_handler) + { + m_sock.close(error_handler); + } + + endpoint_type remote_endpoint() + { + return m_remote_endpoint; + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + return m_remote_endpoint; + } + + endpoint_type local_endpoint() + { + return m_sock.local_endpoint(); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + return m_sock.local_endpoint(error_handler); + } + + asio::io_service& io_service() + { + return m_sock.io_service(); + } + + lowest_layer_type& lowest_layer() + { + return m_sock.lowest_layer(); + } + + typedef boost::function handler_type; + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + m_remote_endpoint = endpoint; + + // the connect is split up in the following steps: + // 1. resolve name of proxy server + // 2. connect to proxy server + // 3. send SOCKS5 authentication method message + // 4. read SOCKS5 authentication response + // 5. send username+password + // 6. send SOCKS5 CONNECT message + + // to avoid unnecessary copying of the handler, + // store it in a shaed_ptr + boost::shared_ptr h(new handler_type(handler)); + + tcp::resolver::query q(m_hostname + , boost::lexical_cast(m_port)); + m_resolver.async_resolve(q, boost::bind( + &socks5_stream::name_lookup, this, _1, _2, h)); + } + +private: + + void name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h); + void connected(asio::error_code const& e, boost::shared_ptr h); + void handshake1(asio::error_code const& e, boost::shared_ptr h); + void handshake2(asio::error_code const& e, boost::shared_ptr h); + void handshake3(asio::error_code const& e, boost::shared_ptr h); + void handshake4(asio::error_code const& e, boost::shared_ptr h); + void socks_connect(boost::shared_ptr h); + void connect1(asio::error_code const& e, boost::shared_ptr h); + void connect2(asio::error_code const& e, boost::shared_ptr h); + void connect3(asio::error_code const& e, boost::shared_ptr h); + + stream_socket m_sock; + // the socks5 proxy + std::string m_hostname; + int m_port; + // send and receive buffer + std::vector m_buffer; + // proxy authentication + std::string m_user; + std::string m_password; + + endpoint_type m_remote_endpoint; + + tcp::resolver m_resolver; +}; + +} + diff --git a/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index 808e90a1a..ccb17cb71 100644 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/include/libtorrent/storage.hpp @@ -51,6 +51,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent_info.hpp" +#include "libtorrent/piece_picker.hpp" #include "libtorrent/config.hpp" namespace libtorrent @@ -77,6 +78,7 @@ namespace libtorrent torrent_info const& t , boost::filesystem::path p , std::vector > const& sizes + , bool compact_mode , std::string* error = 0); struct TORRENT_EXPORT file_allocation_failed: std::exception @@ -87,40 +89,56 @@ namespace libtorrent std::string m_msg; }; - class TORRENT_EXPORT storage + struct TORRENT_EXPORT storage_interface { - public: - storage( - const torrent_info& info - , const boost::filesystem::path& path - , file_pool& fp); - - void swap(storage&); + // create directories and set file sizes + // if allocate_files is true. + // allocate_files is true if allocation mode + // is set to full and sparse files are supported + virtual void initialize(bool allocate_files) = 0; // may throw file_error if storage for slot does not exist - size_type read(char* buf, int slot, int offset, int size); + virtual size_type read(char* buf, int slot, int offset, int size) = 0; // may throw file_error if storage for slot hasn't been allocated - void write(const char* buf, int slot, int offset, int size); + virtual void write(const char* buf, int slot, int offset, int size) = 0; - bool move_storage(boost::filesystem::path save_path); + virtual bool move_storage(boost::filesystem::path save_path) = 0; + + // verify storage dependent fast resume entries + virtual bool verify_resume_data(entry& rd, std::string& error) = 0; + + // write storage dependent fast resume entries + virtual void write_resume_data(entry& rd) const = 0; + + // moves (or copies) the content in src_slot to dst_slot + virtual void move_slot(int src_slot, int dst_slot) = 0; + + // swaps the data in slot1 and slot2 + virtual void swap_slots(int slot1, int slot2) = 0; + + // swaps the puts the data in slot1 in slot2, the data in slot2 + // in slot3 and the data in slot3 in slot1 + virtual void swap_slots3(int slot1, int slot2, int slot3) = 0; // this will close all open files that are opened for // writing. This is called when a torrent has finished // downloading. - void release_files(); - -#ifndef NDEBUG - // overwrites some slots with the - // contents of others - void shuffle(); -#endif - - private: - class impl; - boost::shared_ptr m_pimpl; + virtual void release_files() = 0; + virtual ~storage_interface() {} }; + typedef storage_interface* (&storage_constructor_type)( + torrent_info const&, boost::filesystem::path const& + , file_pool&); + + TORRENT_EXPORT storage_interface* default_storage_constructor(torrent_info const& ti + , boost::filesystem::path const& path, file_pool& fp); + + // returns true if the filesystem the path relies on supports + // sparse files or automatic zero filling of files. + TORRENT_EXPORT bool supports_sparse_files(boost::filesystem::path const& p); + class TORRENT_EXPORT piece_manager : boost::noncopyable { public: @@ -128,7 +146,8 @@ namespace libtorrent piece_manager( const torrent_info& info , const boost::filesystem::path& path - , file_pool& fp); + , file_pool& fp + , storage_constructor_type sc); ~piece_manager(); @@ -139,14 +158,17 @@ namespace libtorrent void release_files(); + void write_resume_data(entry& rd) const; + bool verify_resume_data(entry& rd, std::string& error); + bool is_allocating() const; - void allocate_slots(int num_slots); + bool allocate_slots(int num_slots, bool abort_on_disk = false); void mark_failed(int index); unsigned long piece_crc( int slot_index , int block_size - , const std::bitset<256>& bitmask); + , piece_picker::block_info const* bi); int slot_for_piece(int piece_index) const; size_type read( @@ -170,6 +192,8 @@ namespace libtorrent // of unassigned pieces and -1 is unallocated void export_piece_map(std::vector& pieces) const; + bool compact_allocation() const; + private: class impl; std::auto_ptr m_pimpl; diff --git a/libtorrent/include/libtorrent/time.hpp b/libtorrent/include/libtorrent/time.hpp new file mode 100644 index 000000000..2470522c0 --- /dev/null +++ b/libtorrent/include/libtorrent/time.hpp @@ -0,0 +1,383 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_TIME_HPP_INCLUDED +#define TORRENT_TIME_HPP_INCLUDED + +#include + +#ifndef _WIN32 +#include +#endif + +namespace libtorrent +{ + inline char const* time_now_string() + { + time_t t = std::time(0); + tm* timeinfo = std::localtime(&t); + static char str[200]; + std::strftime(str, 200, "%b %d %X", timeinfo); + return str; + } +} + +#if (!defined (__MACH__) && !defined (_WIN32) && !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0) || defined (TORRENT_USE_BOOST_DATE_TIME) + +#include + +namespace libtorrent +{ + typedef boost::posix_time::ptime ptime; + typedef boost::posix_time::time_duration time_duration; + inline ptime time_now() + { return boost::posix_time::microsec_clock::universal_time(); } + inline ptime min_time() + { return boost::posix_time::ptime(boost::posix_time::min_date_time); } + inline ptime max_time() + { return boost::posix_time::ptime(boost::posix_time::max_date_time); } + inline time_duration seconds(int s) { return boost::posix_time::seconds(s); } + inline time_duration milliseconds(int s) { return boost::posix_time::milliseconds(s); } + inline time_duration microsec(int s) { return boost::posix_time::microsec(s); } + inline time_duration minutes(int s) { return boost::posix_time::minutes(s); } + inline time_duration hours(int s) { return boost::posix_time::hours(s); } + + inline int total_seconds(time_duration td) + { return td.total_seconds(); } + inline int total_milliseconds(time_duration td) + { return td.total_milliseconds(); } + inline boost::int64_t total_microseconds(time_duration td) + { return td.total_microseconds(); } + +} + +#else + +#include +#include + +namespace libtorrent +{ + // libtorrent time_duration type + struct time_duration + { + time_duration() {} + time_duration operator/(int rhs) const { return time_duration(diff / rhs); } + explicit time_duration(boost::int64_t d) : diff(d) {} + boost::int64_t diff; + }; + + inline bool is_negative(time_duration dt) { return dt.diff < 0; } + inline bool operator<(time_duration lhs, time_duration rhs) + { return lhs.diff < rhs.diff; } + inline bool operator>(time_duration lhs, time_duration rhs) + { return lhs.diff > rhs.diff; } + + // libtorrent time type + struct ptime + { + ptime() {} + explicit ptime(boost::int64_t t): time(t) {} + boost::int64_t time; + }; + + inline bool operator>(ptime lhs, ptime rhs) + { return lhs.time > rhs.time; } + inline bool operator>=(ptime lhs, ptime rhs) + { return lhs.time >= rhs.time; } + inline bool operator<=(ptime lhs, ptime rhs) + { return lhs.time <= rhs.time; } + inline bool operator<(ptime lhs, ptime rhs) + { return lhs.time < rhs.time; } + inline bool operator!=(ptime lhs, ptime rhs) + { return lhs.time != rhs.time;} + inline bool operator==(ptime lhs, ptime rhs) + { return lhs.time == rhs.time;} + inline time_duration operator-(ptime lhs, ptime rhs) + { return time_duration(lhs.time - rhs.time); } + inline ptime operator+(ptime lhs, time_duration rhs) + { return ptime(lhs.time + rhs.diff); } + inline ptime operator+(time_duration lhs, ptime rhs) + { return ptime(rhs.time + lhs.diff); } + inline ptime operator-(ptime lhs, time_duration rhs) + { return ptime(lhs.time - rhs.diff); } + + ptime time_now(); + inline ptime min_time() { return ptime(0); } + inline ptime max_time() { return ptime((std::numeric_limits::max)()); } + int total_seconds(time_duration td); + int total_milliseconds(time_duration td); + boost::int64_t total_microseconds(time_duration td); +} + +// asio time_traits +namespace asio +{ + template<> + struct time_traits + { + typedef libtorrent::ptime time_type; + typedef libtorrent::time_duration duration_type; + static time_type now() + { return time_type(libtorrent::time_now()); } + static time_type add(time_type t, duration_type d) + { return time_type(t.time + d.diff);} + static duration_type subtract(time_type t1, time_type t2) + { return duration_type(t1 - t2); } + static bool less_than(time_type t1, time_type t2) + { return t1 < t2; } + static boost::posix_time::time_duration to_posix_duration( + duration_type d) + { return boost::posix_time::microseconds(libtorrent::total_microseconds(d)); } + }; +} + +#if defined(__MACH__) + +#include +#include + +// high precision timer for darwin intel and ppc + +namespace libtorrent +{ + namespace aux + { + inline boost::int64_t absolutetime_to_microseconds(boost::int64_t at) + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + mach_timebase_info(&timebase_info); + // make sure we don't overflow + assert((at >= 0 && at >= at / 1000 * timebase_info.numer / timebase_info.denom) + || (at < 0 && at < at / 1000 * timebase_info.numer / timebase_info.denom)); + return at / 1000 * timebase_info.numer / timebase_info.denom; + } + + inline boost::int64_t microseconds_to_absolutetime(boost::int64_t ms) + { + static mach_timebase_info_data_t timebase_info = {0,0}; + if (timebase_info.denom == 0) + { + mach_timebase_info(&timebase_info); + assert(timebase_info.numer > 0); + assert(timebase_info.denom > 0); + } + // make sure we don't overflow + assert((ms >= 0 && ms <= ms * timebase_info.denom / timebase_info.numer * 1000) + || (ms < 0 && ms > ms * timebase_info.denom / timebase_info.numer * 1000)); + return ms * timebase_info.denom / timebase_info.numer * 1000; + } + } + + inline int total_seconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff) + / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff) + / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return aux::absolutetime_to_microseconds(td.diff); + } + + inline ptime time_now() { return ptime(mach_absolute_time()); } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s)); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000)); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000)); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000 * 60)); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(aux::microseconds_to_absolutetime(s * 1000000 * 60 * 60)); + } + +} +#elif defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +namespace libtorrent +{ + namespace aux + { + inline boost::int64_t performance_counter_to_microseconds(boost::int64_t pc) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); + +#ifndef NDEBUG + // make sure we don't overflow + boost::int64_t ret = (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + assert((pc >= 0 && pc >= ret) || (pc < 0 && pc < ret)); +#endif + return (pc * 1000 / performace_counter_frequency.QuadPart) * 1000; + } + + inline boost::int64_t microseconds_to_performance_counter(boost::int64_t ms) + { + static LARGE_INTEGER performace_counter_frequency = {0,0}; + if (performace_counter_frequency.QuadPart == 0) + QueryPerformanceFrequency(&performace_counter_frequency); +#ifndef NDEBUG + // make sure we don't overflow + boost::int64_t ret = (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + assert((ms >= 0 && ms <= ret) + || (ms < 0 && ms > ret)); +#endif + return (ms / 1000) * performace_counter_frequency.QuadPart / 1000; + } + } + + inline int total_seconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff) + / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff) + / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return aux::performance_counter_to_microseconds(td.diff); + } + + inline ptime time_now() + { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return ptime(now.QuadPart); + } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter(s)); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000)); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000)); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000 * 60)); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(aux::microseconds_to_performance_counter( + s * 1000000 * 60 * 60)); + } + +} + +#elif defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 + +#include + +namespace libtorrent +{ + inline int total_seconds(time_duration td) + { + return td.diff / 1000000; + } + inline int total_milliseconds(time_duration td) + { + return td.diff / 1000; + } + inline boost::int64_t total_microseconds(time_duration td) + { + return td.diff; + } + + inline ptime time_now() + { + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ptime(boost::int64_t(ts.tv_sec) * 1000000 + ts.tv_nsec / 1000); + } + + inline time_duration microsec(boost::int64_t s) + { + return time_duration(s); + } + inline time_duration milliseconds(boost::int64_t s) + { + return time_duration(s * 1000); + } + inline time_duration seconds(boost::int64_t s) + { + return time_duration(s * 1000000); + } + inline time_duration minutes(boost::int64_t s) + { + return time_duration(s * 1000000 * 60); + } + inline time_duration hours(boost::int64_t s) + { + return time_duration(s * 1000000 * 60 * 60); + } + +} + +#endif + +#endif +#endif + diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 56a4f54be..7194463e9 100644 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -45,7 +45,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include #include #include @@ -68,6 +67,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/escape_string.hpp" #include "libtorrent/bandwidth_manager.hpp" +#include "libtorrent/storage.hpp" namespace libtorrent { @@ -100,7 +100,8 @@ namespace libtorrent , tcp::endpoint const& net_interface , bool compact_mode , int block_size - , session_settings const& s); + , session_settings const& s + , storage_constructor_type sc); // used with metadata-less torrents // (the metadata is downloaded from the peers) @@ -114,10 +115,14 @@ namespace libtorrent , tcp::endpoint const& net_interface , bool compact_mode , int block_size - , session_settings const& s); + , session_settings const& s + , storage_constructor_type sc); ~torrent(); + // starts the announce timer + void start(); + #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::shared_ptr); #endif @@ -142,7 +147,10 @@ namespace libtorrent aux::session_impl& session() { return m_ses; } void set_sequenced_download_threshold(int threshold); - + + bool verify_resume_data(entry& rd, std::string& error) + { assert(m_storage); return m_storage->verify_resume_data(rd, error); } + // is called every second by session. This will // caclulate the upload/download and number // of connections this torrent needs. And prepare @@ -168,12 +176,21 @@ namespace libtorrent void resume(); bool is_paused() const { return m_paused; } + // ============ start deprecation ============= void filter_piece(int index, bool filter); void filter_pieces(std::vector const& bitmask); bool is_piece_filtered(int index) const; void filtered_pieces(std::vector& bitmask) const; - void filter_files(std::vector const& files); + // ============ end deprecation ============= + + void set_piece_priority(int index, int priority); + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces); + void piece_priorities(std::vector&) const; + + void prioritize_files(std::vector const& files); torrent_status status() const; void file_progress(std::vector& fp) const; @@ -182,7 +199,7 @@ namespace libtorrent tcp::endpoint const& get_interface() const { return m_net_interface; } void connect_to_url_seed(std::string const& url); - peer_connection& connect_to_peer(tcp::endpoint const& a); + peer_connection* connect_to_peer(policy::peer* peerinfo); void set_ratio(float ratio) { assert(ratio >= 0.0f); m_ratio = ratio; } @@ -190,10 +207,12 @@ namespace libtorrent float ratio() const { return m_ratio; } +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void resolve_countries(bool r) { m_resolve_countries = r; } bool resolving_countries() const { return m_resolve_countries; } +#endif // -------------------------------------------- // BANDWIDTH MANAGEMENT @@ -230,6 +249,9 @@ namespace libtorrent // decreased in the piece_picker void remove_peer(peer_connection* p); + bool want_more_peers() const; + void try_connect_peer(); + peer_connection* connection_for(tcp::endpoint const& a) { peer_iterator i = m_connections.find(a); @@ -282,7 +304,7 @@ namespace libtorrent // returns the absolute time when the next tracker // announce will take place. - boost::posix_time::ptime next_announce() const; + ptime next_announce() const; // returns true if it is time for this torrent to make another // tracker request @@ -290,7 +312,7 @@ namespace libtorrent // forcefully sets next_announce to the current time void force_tracker_request(); - void force_tracker_request(boost::posix_time::ptime); + void force_tracker_request(ptime); // sets the username and password that will be sent to // the tracker @@ -332,6 +354,21 @@ namespace libtorrent } #endif } + + void peer_has_all() + { + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->inc_refcount_all(); + } +#ifndef NDEBUG + else + { + assert(is_seed()); + } +#endif + } // when peer disconnects, this is called for every piece it had void peer_lost(int index) @@ -394,19 +431,34 @@ namespace libtorrent void received_redundant_data(int num_bytes) { assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; } + // this is true if we have all the pieces bool is_seed() const { return valid_metadata() && m_num_pieces == m_torrent_file.num_pieces(); } + // this is true if we have all the pieces that we want + bool is_finished() const + { + if (is_seed()) return true; + return valid_metadata() && m_torrent_file.num_pieces() + - m_num_pieces - m_picker->num_filtered() == 0; + } + boost::filesystem::path save_path() const; alert_manager& alerts() const; piece_picker& picker() { + assert(!is_seed()); assert(m_picker.get()); return *m_picker; } + bool has_picker() const + { + assert((valid_metadata() && !is_seed()) == bool(m_picker.get() != 0)); + return m_picker.get() != 0; + } policy& get_policy() { assert(m_policy); @@ -445,7 +497,10 @@ namespace libtorrent void set_peer_download_limit(tcp::endpoint ip, int limit); void set_upload_limit(int limit); + int upload_limit() const; void set_download_limit(int limit); + int download_limit() const; + void set_max_uploads(int limit); void set_max_connections(int limit); bool move_storage(boost::filesystem::path const& save_path); @@ -471,6 +526,8 @@ namespace libtorrent , boost::intrusive_ptr p) const; bool request_bandwidth_from_session(int channel) const; + void update_peer_interest(); + torrent_info m_torrent_file; // is set to true when the torrent has @@ -498,7 +555,7 @@ namespace libtorrent boost::scoped_ptr m_storage; // the time of next tracker request - boost::posix_time::ptime m_next_request; + ptime m_next_request; // ----------------------------- // DATA FROM TRACKER RESPONSE @@ -531,33 +588,44 @@ namespace libtorrent // used to resolve the names of web seeds mutable tcp::resolver m_host_resolver; +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES // this is true while there is a country // resolution in progress. To avoid flodding - // the DNS request queue, only one ip is reolved + // the DNS request queue, only one ip is resolved // at a time. mutable bool m_resolving_country; // this is true if the user has enabled // country resolution in this torrent bool m_resolve_countries; +#endif + + // this announce timer is used both + // by Local service discovery and + // by the DHT. + deadline_timer m_announce_timer; + + static void on_announce_disp(boost::weak_ptr p + , asio::error_code const& e); + + // this is called once per announce interval + void on_announce(); #ifndef TORRENT_DISABLE_DHT static void on_dht_announce_response_disp(boost::weak_ptr t , std::vector const& peers); - deadline_timer m_dht_announce_timer; - void on_dht_announce(asio::error_code const& e); void on_dht_announce_response(std::vector const& peers); bool should_announce_dht() const; + + // the time when the DHT was last announced of our + // presence on this torrent + ptime m_last_dht_announce; #endif // this is the upload and download statistics for the whole torrent. // it's updated from all its peers once every second. libtorrent::stat m_stat; - // this is the stats for web seeds in this torrent only. It is updated - // once every second. - libtorrent::stat m_web_stat; - // ----------------------------- boost::shared_ptr m_policy; @@ -594,6 +662,11 @@ namespace libtorrent // m_have_pieces.end(), 0) int m_num_pieces; + // in case the piece picker hasn't been constructed + // when this settings is set, this variable will keep + // its value until the piece picker is created + int m_sequenced_download_threshold; + // is false by default and set to // true when the first tracker reponse // is received @@ -643,6 +716,8 @@ namespace libtorrent boost::scoped_ptr m_name; session_settings const& m_settings; + + storage_constructor_type m_storage_constructor; #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > extension_list_t; @@ -667,20 +742,18 @@ namespace libtorrent #endif }; - inline boost::posix_time::ptime torrent::next_announce() const + inline ptime torrent::next_announce() const { return m_next_request; } inline void torrent::force_tracker_request() { - using boost::posix_time::second_clock; - m_next_request = second_clock::universal_time(); + m_next_request = time_now(); } - inline void torrent::force_tracker_request(boost::posix_time::ptime t) + inline void torrent::force_tracker_request(ptime t) { - namespace time = boost::posix_time; m_next_request = t; } diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index 92dbdf34b..cb4b892d2 100644 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -39,7 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(push, 1) #endif -#include +#include #ifdef _MSC_VER #pragma warning(pop) @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_info.hpp" #include "libtorrent/piece_picker.hpp" #include "libtorrent/torrent_info.hpp" +#include "libtorrent/time.hpp" #include "libtorrent/config.hpp" namespace libtorrent @@ -188,7 +189,7 @@ namespace libtorrent // the number of distributed copies of the file. // note that one copy may be spread out among many peers. // - // the whole number part tells how many copies + // the integer part tells how many copies // there are of the rarest piece(s) // // the fractional part tells the fraction of pieces that @@ -203,13 +204,15 @@ namespace libtorrent struct TORRENT_EXPORT partial_piece_info { - enum { max_blocks_per_piece = piece_picker::max_blocks_per_piece }; + enum { max_blocks_per_piece = 256 }; int piece_index; int blocks_in_piece; std::bitset requested_blocks; std::bitset finished_blocks; tcp::endpoint peer[max_blocks_per_piece]; int num_downloads[max_blocks_per_piece]; + enum state_t { none, slow, medium, fast }; + state_t piece_state; }; struct TORRENT_EXPORT torrent_handle @@ -243,9 +246,16 @@ namespace libtorrent bool is_paused() const; void pause() const; void resume() const; - + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void resolve_countries(bool r); bool resolve_countries() const; +#endif + + // all these are deprecated, use piece + // priority functions instead + + // ================ start deprecation ============ // marks the piece with the given index as filtered // it will not be downloaded @@ -253,11 +263,22 @@ namespace libtorrent void filter_pieces(std::vector const& pieces) const; bool is_piece_filtered(int index) const; std::vector filtered_pieces() const; - // marks the file with the given index as filtered // it will not be downloaded void filter_files(std::vector const& files) const; + // ================ end deprecation ============ + + // priority must be within the range [0, 7] + void piece_priority(int index, int priority) const; + int piece_priority(int index) const; + + void prioritize_pieces(std::vector const& pieces) const; + std::vector piece_priorities() const; + + void prioritize_files(std::vector const& files) const; + + // set the interface to bind outgoing connections // to. void use_interface(const char* net_interface) const; @@ -284,14 +305,17 @@ namespace libtorrent // abort the torrent. void set_upload_limit(int limit) const; + int upload_limit() const; void set_download_limit(int limit) const; + int download_limit() const; + void set_sequenced_download_threshold(int threshold) const; void set_peer_upload_limit(tcp::endpoint ip, int limit) const; void set_peer_download_limit(tcp::endpoint ip, int limit) const; // manually connect a peer - void connect_peer(tcp::endpoint const& adr) const; + void connect_peer(tcp::endpoint const& adr, int source = 0) const; // valid ratios are 0 (infinite ratio) or [ 1.0 , inf ) // the ratio is uploaded / downloaded. less than 1 is not allowed @@ -349,3 +373,4 @@ namespace libtorrent } #endif // TORRENT_TORRENT_HANDLE_HPP_INCLUDED + diff --git a/libtorrent/include/libtorrent/torrent_info.hpp b/libtorrent/include/libtorrent/torrent_info.hpp index 4e6e20d9e..d26f9f1f2 100644 --- a/libtorrent/include/libtorrent/torrent_info.hpp +++ b/libtorrent/include/libtorrent/torrent_info.hpp @@ -41,8 +41,6 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(push, 1) #endif -#include -#include #include #include #include @@ -57,9 +55,12 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/size_type.hpp" #include "libtorrent/peer_request.hpp" #include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" namespace libtorrent { + namespace pt = boost::posix_time; + namespace gr = boost::gregorian; struct TORRENT_EXPORT file_entry { @@ -114,7 +115,11 @@ namespace libtorrent std::vector map_block(int piece, size_type offset, int size) const; peer_request map_file(int file, size_type offset, int size) const; - std::vector const& url_seeds() const { return m_url_seeds; } + std::vector const& url_seeds() const + { + assert(!m_half_metadata); + return m_url_seeds; + } typedef std::vector::const_iterator file_iterator; typedef std::vector::const_reverse_iterator reverse_file_iterator; @@ -134,10 +139,14 @@ namespace libtorrent size_type total_size() const { assert(m_piece_length > 0); return m_total_size; } size_type piece_length() const { assert(m_piece_length > 0); return m_piece_length; } - int num_pieces() const { assert(m_piece_length > 0); return (int)m_piece_hash.size(); } + int num_pieces() const { assert(m_piece_length > 0); return m_num_pieces; } const sha1_hash& info_hash() const { return m_info_hash; } const std::string& name() const { assert(m_piece_length > 0); return m_name; } + +// ------- start deprecation ------- void print(std::ostream& os) const; +// ------- end deprecation ------- + bool is_valid() const { return m_piece_length > 0; } bool priv() const { return m_private; } @@ -151,11 +160,11 @@ namespace libtorrent { assert(index >= 0); assert(index < (int)m_piece_hash.size()); + assert(!m_half_metadata); return m_piece_hash[index]; } - boost::optional - creation_date() const; + boost::optional creation_date() const; const std::string& creator() const { return m_created_by; } @@ -167,12 +176,22 @@ namespace libtorrent typedef std::vector > nodes_t; nodes_t const& nodes() const - { return m_nodes; } + { + assert(!m_half_metadata); + return m_nodes; + } void add_node(std::pair const& node); void parse_info_section(entry const& e); + entry extra(char const* key) const + { return m_extra_info[key]; } + + // frees parts of the metadata that isn't + // used by seeds + void seed_free(); + private: void read_torrent_info(const entry& libtorrent); @@ -198,6 +217,9 @@ namespace libtorrent // the sum of all filesizes size_type m_total_size; + // the number of pieces in the torrent + int m_num_pieces; + // the hash that identifies this torrent // is mutable because it's calculated // lazily @@ -208,7 +230,7 @@ namespace libtorrent // if a creation date is found in the torrent file // this will be set to that, otherwise it'll be // 1970, Jan 1 - boost::posix_time::ptime m_creation_date; + pt::ptime m_creation_date; // if a comment is found in the torrent file // this will be set to that comment @@ -234,6 +256,11 @@ namespace libtorrent // reproduce the info-section when sending the metadata // to peers. entry m_extra_info; + +#ifndef NDEBUG + // this is set to true when seed_free() is called + bool m_half_metadata; +#endif }; } diff --git a/libtorrent/include/libtorrent/tracker_manager.hpp b/libtorrent/include/libtorrent/tracker_manager.hpp index 5bac57e6e..a4d24f751 100644 --- a/libtorrent/include/libtorrent/tracker_manager.hpp +++ b/libtorrent/include/libtorrent/tracker_manager.hpp @@ -43,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #include #include @@ -61,6 +60,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer_id.hpp" #include "libtorrent/peer.hpp" #include "libtorrent/config.hpp" +#include "libtorrent/time.hpp" +#include "libtorrent/connection_queue.hpp" namespace libtorrent { @@ -75,14 +76,13 @@ namespace libtorrent // returns -1 if gzip header is invalid or the header size in bytes TORRENT_EXPORT int gzip_header(const char* buf, int size); - TORRENT_EXPORT boost::tuple + TORRENT_EXPORT boost::tuple parse_url_components(std::string url); struct TORRENT_EXPORT tracker_request { tracker_request() : kind(announce_request) - , web_downloaded(0) , event(none) , key(0) , num_want(0) @@ -107,7 +107,6 @@ namespace libtorrent size_type downloaded; size_type uploaded; size_type left; - size_type web_downloaded; unsigned short listen_port; event_t event; std::string url; @@ -155,8 +154,8 @@ namespace libtorrent struct TORRENT_EXPORT timeout_handler : boost::noncopyable { - friend void intrusive_ptr_add_ref(timeout_handler const*); - friend void intrusive_ptr_release(timeout_handler const*); + friend TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*); + friend TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*); timeout_handler(asio::strand& str); @@ -177,11 +176,11 @@ namespace libtorrent asio::strand& m_strand; // used for timeouts // this is set when the request has been sent - boost::posix_time::ptime m_start_time; + ptime m_start_time; // this is set every time something is received - boost::posix_time::ptime m_read_time; + ptime m_read_time; // the asio async operation - asio::deadline_timer m_timeout; + deadline_timer m_timeout; int m_completion_timeout; int m_read_timeout; @@ -223,11 +222,14 @@ namespace libtorrent { public: - tracker_manager(const session_settings& s) - : m_settings(s) {} + tracker_manager(session_settings const& s, proxy_settings const& ps) + : m_settings(s) + , m_proxy(ps) + , m_abort(false) {} void queue_request( asio::strand& str + , connection_queue& cc , tracker_request r , std::string const& auth , address bind_infc @@ -247,6 +249,8 @@ namespace libtorrent tracker_connections_t; tracker_connections_t m_connections; session_settings const& m_settings; + proxy_settings const& m_proxy; + bool m_abort; }; } diff --git a/libtorrent/include/libtorrent/udp_tracker_connection.hpp b/libtorrent/include/libtorrent/udp_tracker_connection.hpp index bf87bea77..c0e6853b9 100644 --- a/libtorrent/include/libtorrent/udp_tracker_connection.hpp +++ b/libtorrent/include/libtorrent/udp_tracker_connection.hpp @@ -43,7 +43,6 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #ifdef _MSC_VER diff --git a/libtorrent/include/libtorrent/upnp.hpp b/libtorrent/include/libtorrent/upnp.hpp new file mode 100644 index 000000000..d4b701aad --- /dev/null +++ b/libtorrent/include/libtorrent/upnp.hpp @@ -0,0 +1,237 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_UPNP_HPP +#define TORRENT_UPNP_HPP + +#include "libtorrent/socket.hpp" +#include "libtorrent/http_connection.hpp" +#include "libtorrent/connection_queue.hpp" + +#include +#include +#include +#include +#include +#include + + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined (TORRENT_UPNP_LOGGING) +#define TORRENT_UPNP_LOGGING +#endif + +#if defined(TORRENT_UPNP_LOGGING) +#include +#endif + +namespace libtorrent +{ + +bool is_local(address const& a); +address_v4 guess_local_address(asio::io_service&); + +// int: external tcp port +// int: external udp port +// std::string: error message +typedef boost::function portmap_callback_t; + +class upnp : boost::noncopyable +{ +public: + upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb); + ~upnp(); + + void rebind(address const& listen_interface); + + // maps the ports, if a port is set to 0 + // it will not be mapped + void set_mappings(int tcp, int udp); + + void close(); + +private: + + static address_v4 upnp_multicast_address; + static udp::endpoint upnp_multicast_endpoint; + + enum { num_mappings = 2 }; + enum { default_lease_time = 3600 }; + + void update_mapping(int i, int port); + void resend_request(asio::error_code const& e); + void on_reply(asio::error_code const& e + , std::size_t bytes_transferred); + void discover_device(); + + struct rootdevice; + + void on_upnp_xml(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d); + void on_upnp_map_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping); + void on_upnp_unmap_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d + , int mapping); + void on_expire(asio::error_code const& e); + + void post(rootdevice& d, std::stringstream const& s + , std::string const& soap_action); + void map_port(rootdevice& d, int i); + void unmap_port(rootdevice& d, int i); + void disable(); + + struct mapping_t + { + mapping_t() + : need_update(false) + , local_port(0) + , external_port(0) + , protocol(1) + {} + + // the time the port mapping will expire + ptime expires; + + bool need_update; + + // the local port for this mapping. If this is set + // to 0, the mapping is not in use + int local_port; + + // the external (on the NAT router) port + // for the mapping. This is the port we + // should announce to others + int external_port; + + // 1 = udp, 0 = tcp + int protocol; + }; + + struct rootdevice + { + rootdevice(): service_namespace(0) + , lease_duration(default_lease_time) + , supports_specific_external(true) + , disabled(false) + { + mapping[0].protocol = 0; + mapping[1].protocol = 1; + } + + // the interface url, through which the list of + // supported interfaces are fetched + std::string url; + + // the url to the WANIP or WANPPP interface + std::string control_url; + // either the WANIP namespace or the WANPPP namespace + char const* service_namespace; + + mapping_t mapping[num_mappings]; + + std::string hostname; + int port; + std::string path; + + int lease_duration; + // true if the device supports specifying a + // specific external port, false if it doesn't + bool supports_specific_external; + + bool disabled; + + mutable boost::shared_ptr upnp_connection; + + void close() const + { + if (!upnp_connection) return; + upnp_connection->close(); + upnp_connection.reset(); + } + + bool operator<(rootdevice const& rhs) const + { return url < rhs.url; } + }; + + int m_udp_local_port; + int m_tcp_local_port; + + std::string const& m_user_agent; + + // the set of devices we've found + std::set m_devices; + + portmap_callback_t m_callback; + + // current retry count + int m_retry_count; + + // used to receive responses in + char m_receive_buffer[1024]; + + // the endpoint we received the message from + udp::endpoint m_remote; + + // the local address we're listening on + address_v4 m_local_ip; + + // the udp socket used to send and receive + // multicast messages on the network + datagram_socket m_socket; + + // used to resend udp packets in case + // they time out + deadline_timer m_broadcast_timer; + + // timer used to refresh mappings + deadline_timer m_refresh_timer; + + asio::strand m_strand; + + bool m_disabled; + bool m_closing; + + connection_queue& m_cc; + +#ifdef TORRENT_UPNP_LOGGING + std::ofstream m_log; +#endif +}; + +} + + +#endif + diff --git a/libtorrent/include/libtorrent/variant_stream.hpp b/libtorrent/include/libtorrent/variant_stream.hpp new file mode 100644 index 000000000..8f9888519 --- /dev/null +++ b/libtorrent/include/libtorrent/variant_stream.hpp @@ -0,0 +1,716 @@ +// Copyright Daniel Wallin and Arvid Norberg 2007. +// Use, modification and distribution is +// subject to the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef VARIANT_STREAM_070211_HPP +# define VARIANT_STREAM_070211_HPP + +# include + +# include +# include +# include +# include +# include + +# include +# include +# include + +# include +# include + +#include + +# define NETWORK_VARIANT_STREAM_LIMIT 5 + +namespace libtorrent { + +namespace aux +{ + + struct delete_visitor + : boost::static_visitor<> + { + template + void operator()(T* p) const + { + delete p; + } + + void operator()(boost::blank) const + {} + }; + +// -------------- io_control ----------- + + template + struct io_control_visitor_ec: boost::static_visitor<> + { + io_control_visitor_ec(IO_Control_Command& ioc, asio::error_code& ec) + : ioc(ioc), ec(ec) {} + + template + void operator()(T* p) const + { + p->io_control(ioc, ec); + } + + void operator()(boost::blank) const + {} + + IO_Control_Command& ioc; + asio::error_code& ec; + }; + + template + struct io_control_visitor + : boost::static_visitor<> + { + io_control_visitor(IO_Control_Command& ioc) + : ioc(ioc) {} + + template + void operator()(T* p) const + { + p->io_control(ioc); + } + + void operator()(boost::blank) const + {} + + IO_Control_Command& ioc; + }; +// -------------- async_connect ----------- + + template + struct async_connect_visitor + : boost::static_visitor<> + { + async_connect_visitor(EndpointType const& endpoint, Handler const& handler) + : endpoint(endpoint) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_connect(endpoint, handler); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + Handler const& handler; + }; + +// -------------- bind ----------- + + template + struct bind_visitor + : boost::static_visitor<> + { + bind_visitor(EndpointType const& ep, Error_Handler const& error_handler) + : endpoint(ep) + , error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->bind(endpoint, error_handler); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + Error_Handler const& error_handler; + }; + + template + struct bind_visitor + : boost::static_visitor<> + { + bind_visitor(EndpointType const& ep) + : endpoint(ep) + {} + + template + void operator()(T* p) const + { + p->bind(endpoint); + } + + void operator()(boost::blank) const + {} + + EndpointType const& endpoint; + }; + +// -------------- open ----------- + + template + struct open_visitor + : boost::static_visitor<> + { + open_visitor(Protocol const& p, Error_Handler const& error_handler) + : proto(p) + , error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->open(proto, error_handler); + } + + void operator()(boost::blank) const + {} + + Protocol const& proto; + Error_Handler const& error_handler; + }; + + template + struct open_visitor + : boost::static_visitor<> + { + open_visitor(Protocol const& p) + : proto(p) + {} + + template + void operator()(T* p) const + { + p->open(proto); + } + + void operator()(boost::blank) const + {} + + Protocol const& proto; + }; + +// -------------- close ----------- + + template + struct close_visitor + : boost::static_visitor<> + { + close_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + void operator()(T* p) const + { + p->close(error_handler); + } + + void operator()(boost::blank) const + {} + + Error_Handler const& error_handler; + }; + + template <> + struct close_visitor + : boost::static_visitor<> + { + template + void operator()(T* p) const + { + p->close(); + } + + void operator()(boost::blank) const + {} + }; + +// -------------- remote_endpoint ----------- + + template + struct remote_endpoint_visitor + : boost::static_visitor + { + remote_endpoint_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + EndpointType operator()(T* p) const + { + return p->remote_endpoint(error_handler); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + + Error_Handler const& error_handler; + }; + + template + struct remote_endpoint_visitor + : boost::static_visitor + { + template + EndpointType operator()(T* p) const + { + return p->remote_endpoint(); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + }; + +// -------------- local_endpoint ----------- + + template + struct local_endpoint_visitor + : boost::static_visitor + { + local_endpoint_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + EndpointType operator()(T* p) const + { + return p->local_endpoint(error_handler); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + + Error_Handler const& error_handler; + }; + + template + struct local_endpoint_visitor + : boost::static_visitor + { + template + EndpointType operator()(T* p) const + { + return p->local_endpoint(); + } + + EndpointType operator()(boost::blank) const + { + return EndpointType(); + } + }; + +// -------------- async_read_some ----------- + + template + struct async_read_some_visitor + : boost::static_visitor<> + { + async_read_some_visitor(Mutable_Buffers const& buffers, Handler const& handler) + : buffers(buffers) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_read_some(buffers, handler); + } + void operator()(boost::blank) const + {} + + Mutable_Buffers const& buffers; + Handler const& handler; + }; + +// -------------- read_some ----------- + + template + struct read_some_visitor + : boost::static_visitor + { + read_some_visitor(Mutable_Buffers const& buffers) + : buffers(buffers) + {} + + template + std::size_t operator()(T* p) const + { return p->read_some(buffers); } + + std::size_t operator()(boost::blank) const + { return 0; } + + Mutable_Buffers const& buffers; + }; + + template + struct read_some_visitor_ec + : boost::static_visitor + { + read_some_visitor_ec(Mutable_Buffers const& buffers, asio::error_code& ec) + : buffers(buffers) + , ec(ec) + {} + + template + std::size_t operator()(T* p) const + { return p->read_some(buffers, ec); } + + std::size_t operator()(boost::blank) const + { return 0; } + + Mutable_Buffers const& buffers; + asio::error_code& ec; + }; + +// -------------- async_write_some ----------- + + template + struct async_write_some_visitor + : boost::static_visitor<> + { + async_write_some_visitor(Const_Buffers const& buffers, Handler const& handler) + : buffers(buffers) + , handler(handler) + {} + + template + void operator()(T* p) const + { + p->async_write_some(buffers, handler); + } + + void operator()(boost::blank) const + {} + + Const_Buffers const& buffers; + Handler const& handler; + }; + +// -------------- in_avail ----------- + + template + struct in_avail_visitor + : boost::static_visitor + { + in_avail_visitor(Error_Handler const& error_handler) + : error_handler(error_handler) + {} + + template + std::size_t operator()(T* p) const + { + return p->in_avail(error_handler); + } + + std::size_t operator()(boost::blank) const + { + return 0; + } + + Error_Handler const& error_handler; + }; + + template <> + struct in_avail_visitor + : boost::static_visitor + { + template + std::size_t operator()(T* p) const + { + return p->in_avail(); + } + + void operator()(boost::blank) const + {} + }; + +// -------------- io_service ----------- + + template + struct io_service_visitor + : boost::static_visitor + { + template + IOService& operator()(T* p) const + { + return p->io_service(); + } + + IOService& operator()(boost::blank) const + { + return *(IOService*)0; + } + }; + +// -------------- lowest_layer ----------- + + template + struct lowest_layer_visitor + : boost::static_visitor + { + template + LowestLayer& operator()(T* p) const + { + return p->lowest_layer(); + } + + LowestLayer& operator()(boost::blank) const + { + return *(LowestLayer*)0; + } + }; + +} // namespace aux + +template < + BOOST_PP_ENUM_BINARY_PARAMS( + NETWORK_VARIANT_STREAM_LIMIT, class S, = boost::mpl::void_ BOOST_PP_INTERCEPT + ) +> +class variant_stream : boost::noncopyable +{ +public: + typedef BOOST_PP_CAT(boost::mpl::vector, NETWORK_VARIANT_STREAM_LIMIT)< + BOOST_PP_ENUM_PARAMS(NETWORK_VARIANT_STREAM_LIMIT, S) + > types0; + + typedef typename boost::mpl::remove::type types; + + typedef typename boost::make_variant_over< + typename boost::mpl::push_back< + typename boost::mpl::transform< + types + , boost::add_pointer + >::type + , boost::blank + >::type + >::type variant_type; + + typedef typename S0::lowest_layer_type lowest_layer_type; + typedef typename S0::endpoint_type endpoint_type; + typedef typename S0::protocol_type protocol_type; + + explicit variant_stream(asio::io_service& io_service) + : m_io_service(io_service) + , m_variant(boost::blank()) + {} + + template + void instantiate() + { + std::auto_ptr owned(new S(m_io_service)); + boost::apply_visitor(aux::delete_visitor(), m_variant); + m_variant = owned.get(); + owned.release(); + } + + template + S& get() + { + return *boost::get(m_variant); + } + + bool instantiated() const + { + return m_variant.which() != boost::mpl::size::value; + } + + ~variant_stream() + { + boost::apply_visitor(aux::delete_visitor(), m_variant); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers, asio::error_code& ec) + { + assert(instantiated()); + return boost::apply_visitor( + aux::read_some_visitor_ec(buffers, ec) + , m_variant + ); + } + + template + std::size_t read_some(Mutable_Buffers const& buffers) + { + assert(instantiated()); + return boost::apply_visitor( + aux::read_some_visitor(buffers) + , m_variant + ); + } + + template + void async_read_some(Mutable_Buffers const& buffers, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_read_some_visitor(buffers, handler) + , m_variant + ); + } + + template + void async_write_some(Const_Buffers const& buffers, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_write_some_visitor(buffers, handler) + , m_variant + ); + } + + template + void async_connect(endpoint_type const& endpoint, Handler const& handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::async_connect_visitor(endpoint, handler), m_variant + ); + } + + template + void io_control(IO_Control_Command& ioc) + { + assert(instantiated()); + boost::apply_visitor( + aux::io_control_visitor(ioc), m_variant + ); + } + + template + void io_control(IO_Control_Command& ioc, asio::error_code& ec) + { + assert(instantiated()); + boost::apply_visitor( + aux::io_control_visitor_ec(ioc, ec) + , m_variant + ); + } + + void bind(endpoint_type const& endpoint) + { + assert(instantiated()); + boost::apply_visitor(aux::bind_visitor(endpoint), m_variant); + } + + template + void bind(endpoint_type const& endpoint, Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::bind_visitor(endpoint, error_handler), m_variant + ); + } + + void open(protocol_type const& p) + { + assert(instantiated()); + boost::apply_visitor(aux::open_visitor(p), m_variant); + } + + template + void open(protocol_type const& p, Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::open_visitor(p, error_handler), m_variant + ); + } + + void close() + { + assert(instantiated()); + boost::apply_visitor(aux::close_visitor<>(), m_variant); + } + + template + void close(Error_Handler const& error_handler) + { + assert(instantiated()); + boost::apply_visitor( + aux::close_visitor(error_handler), m_variant + ); + } + + std::size_t in_avail() + { + assert(instantiated()); + return boost::apply_visitor(aux::in_avail_visitor<>(), m_variant); + } + + template + std::size_t in_avail(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::in_avail_visitor(error_handler), m_variant + ); + } + + endpoint_type remote_endpoint() + { + assert(instantiated()); + return boost::apply_visitor(aux::remote_endpoint_visitor(), m_variant); + } + + template + endpoint_type remote_endpoint(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::remote_endpoint_visitor(error_handler), m_variant + ); + } + + endpoint_type local_endpoint() + { + assert(instantiated()); + return boost::apply_visitor(aux::local_endpoint_visitor(), m_variant); + } + + template + endpoint_type local_endpoint(Error_Handler const& error_handler) + { + assert(instantiated()); + return boost::apply_visitor( + aux::local_endpoint_visitor(error_handler), m_variant + ); + } + + asio::io_service& io_service() + { + assert(instantiated()); + return boost::apply_visitor( + aux::io_service_visitor(), m_variant + ); + } + + lowest_layer_type& lowest_layer() + { + assert(instantiated()); + return boost::apply_visitor( + aux::lowest_layer_visitor(), m_variant + ); + } + +private: + asio::io_service& m_io_service; + variant_type m_variant; +}; + +} // namespace libtorrent + +#endif // VARIANT_STREAM_070211_HPP + diff --git a/libtorrent/include/libtorrent/version.hpp b/libtorrent/include/libtorrent/version.hpp index a96a4d7a4..de1b8bcc8 100644 --- a/libtorrent/include/libtorrent/version.hpp +++ b/libtorrent/include/libtorrent/version.hpp @@ -34,8 +34,8 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_VERSION_HPP_INCLUDED #define LIBTORRENT_VERSION_MAJOR 0 -#define LIBTORRENT_VERSION_MINOR 12 +#define LIBTORRENT_VERSION_MINOR 13 -#define LIBTORRENT_VERSION "0.12.0.0" +#define LIBTORRENT_VERSION "0.13.0.0" #endif diff --git a/libtorrent/include/libtorrent/web_peer_connection.hpp b/libtorrent/include/libtorrent/web_peer_connection.hpp index a8ed22820..690066fb2 100644 --- a/libtorrent/include/libtorrent/web_peer_connection.hpp +++ b/libtorrent/include/libtorrent/web_peer_connection.hpp @@ -49,7 +49,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include @@ -96,10 +95,10 @@ namespace libtorrent web_peer_connection( aux::session_impl& ses , boost::weak_ptr t - , boost::shared_ptr s + , boost::shared_ptr s , tcp::endpoint const& remote - , tcp::endpoint const& proxy - , std::string const& url); + , std::string const& url + , policy::peer* peerinfo); ~web_peer_connection(); @@ -149,6 +148,7 @@ namespace libtorrent std::string m_server_string; http_parser m_parser; + std::string m_auth; std::string m_host; int m_port; std::string m_path; @@ -164,6 +164,14 @@ namespace libtorrent std::vector m_piece; // the mapping of the data in the m_piece buffer peer_request m_intermediate_piece; + + // the number of bytes into the receive buffer where + // current read cursor is. + int m_body_start; + // the number of bytes received in the current HTTP + // response. used to know where in the buffer the + // next response starts + int m_received_body; }; } diff --git a/libtorrent/include/libtorrent/xml_parse.hpp b/libtorrent/include/libtorrent/xml_parse.hpp new file mode 100644 index 000000000..c3aeddad7 --- /dev/null +++ b/libtorrent/include/libtorrent/xml_parse.hpp @@ -0,0 +1,99 @@ +/* + +Copyright (c) 2007, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_XML_PARSE_HPP +#define TORRENT_XML_PARSE_HPP + +namespace libtorrent +{ + const int xml_start_tag = 0; + const int xml_end_tag = 1; + const int xml_string = 2; + + template + void xml_parse(char* p, char* end, CallbackType callback) + { + for(;p != end; ++p) + { + char const* start = p; + // look for tag start + for(; *p != '<' && p != end; ++p); + + if (p != start) + { + if (p != end) + { + assert(*p == '<'); + *p = 0; + } + callback(xml_string, start); + if (p != end) *p = '<'; + } + + if (p == end) break; + + // skip '<' + ++p; + + // parse the name of the tag. Ignore attributes + for (start = p; p != end && *p != '>'; ++p) + { + // terminate the string at the first space + // to ignore tag attributes + if (*p == ' ') *p = 0; + } + + // parse error + if (p == end) break; + + assert(*p == '>'); + *p = 0; + + if (*start == '/') + { + ++start; + callback(xml_end_tag, start); + } + else + { + callback(xml_start_tag, start); + } + *p = '>'; + } + + } + +} + + +#endif + diff --git a/libtorrent/src/Makefile.am b/libtorrent/src/Makefile.am index 6cd77798e..5df13cde7 100644 --- a/libtorrent/src/Makefile.am +++ b/libtorrent/src/Makefile.am @@ -15,12 +15,13 @@ endif libtorrent_la_SOURCES = allocate_resources.cpp \ bandwidth_manager.cpp entry.cpp escape_string.cpp \ peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ -piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp stat.cpp \ -storage.cpp torrent.cpp torrent_handle.cpp \ -torrent_info.cpp tracker_manager.cpp \ +natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \ +stat.cpp storage.cpp torrent.cpp torrent_handle.cpp \ +torrent_info.cpp tracker_manager.cpp http_connection.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 ut_pex.cpp $(kademlia_sources) +logger.cpp file_pool.cpp ut_pex.cpp lsd.cpp upnp.cpp instantiate_connection.cpp \ +socks5_stream.cpp http_stream.cpp connection_queue.cpp $(kademlia_sources) noinst_HEADERS = \ $(top_srcdir)/include/libtorrent/alert.hpp \ @@ -31,6 +32,7 @@ $(top_srcdir)/include/libtorrent/aux_/session_impl.hpp \ $(top_srcdir)/include/libtorrent/bandwidth_manager.hpp \ $(top_srcdir)/include/libtorrent/bencode.hpp \ $(top_srcdir)/include/libtorrent/buffer.hpp \ +$(top_srcdir)/include/libtorrent/connection_queue.hpp \ $(top_srcdir)/include/libtorrent/debug.hpp \ $(top_srcdir)/include/libtorrent/entry.hpp \ $(top_srcdir)/include/libtorrent/escape_string.hpp \ @@ -42,16 +44,22 @@ $(top_srcdir)/include/libtorrent/file.hpp \ $(top_srcdir)/include/libtorrent/file_pool.hpp \ $(top_srcdir)/include/libtorrent/fingerprint.hpp \ $(top_srcdir)/include/libtorrent/hasher.hpp \ +$(top_srcdir)/include/libtorrent/http_connection.hpp \ +$(top_srcdir)/include/libtorrent/http_stream.hpp \ $(top_srcdir)/include/libtorrent/session_settings.hpp \ $(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \ $(top_srcdir)/include/libtorrent/identify_client.hpp \ +$(top_srcdir)/include/libtorrent/instantiate_connection.hpp \ $(top_srcdir)/include/libtorrent/invariant_check.hpp \ $(top_srcdir)/include/libtorrent/io.hpp \ $(top_srcdir)/include/libtorrent/ip_filter.hpp \ +$(top_srcdir)/include/libtorrent/lsd.hpp \ $(top_srcdir)/include/libtorrent/peer.hpp \ $(top_srcdir)/include/libtorrent/peer_connection.hpp \ $(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \ $(top_srcdir)/include/libtorrent/web_peer_connection.hpp \ +$(top_srcdir)/include/libtorrent/natpmp.hpp \ +$(top_srcdir)/include/libtorrent/pch.hpp \ $(top_srcdir)/include/libtorrent/peer_id.hpp \ $(top_srcdir)/include/libtorrent/peer_info.hpp \ $(top_srcdir)/include/libtorrent/peer_request.hpp \ @@ -62,18 +70,23 @@ $(top_srcdir)/include/libtorrent/resource_request.hpp \ $(top_srcdir)/include/libtorrent/session.hpp \ $(top_srcdir)/include/libtorrent/size_type.hpp \ $(top_srcdir)/include/libtorrent/socket.hpp \ +$(top_srcdir)/include/libtorrent/socket_type.hpp \ +$(top_srcdir)/include/libtorrent/socks5_stream.hpp \ $(top_srcdir)/include/libtorrent/stat.hpp \ $(top_srcdir)/include/libtorrent/storage.hpp \ +$(top_srcdir)/include/libtorrent/time.hpp \ $(top_srcdir)/include/libtorrent/torrent.hpp \ $(top_srcdir)/include/libtorrent/torrent_handle.hpp \ $(top_srcdir)/include/libtorrent/torrent_info.hpp \ $(top_srcdir)/include/libtorrent/tracker_manager.hpp \ $(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \ $(top_srcdir)/include/libtorrent/utf8.hpp \ +$(top_srcdir)/include/libtorrent/xml_parse.hpp \ +$(top_srcdir)/include/libtorrent/variant_stream.hpp \ $(top_srcdir)/include/libtorrent/version.hpp -libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1 +libtorrent_la_LDFLAGS = $(LDFLAGS) -release @VERSION@ libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@ AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/include/libtorrent @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@ diff --git a/libtorrent/src/alert.cpp b/libtorrent/src/alert.cpp index 781265b92..b990db7e1 100644 --- a/libtorrent/src/alert.cpp +++ b/libtorrent/src/alert.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include "libtorrent/alert.hpp" namespace libtorrent { @@ -37,7 +39,7 @@ 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()) + , m_timestamp(time_now()) { } @@ -45,7 +47,7 @@ namespace libtorrent { { } - boost::posix_time::ptime alert::timestamp() const + ptime alert::timestamp() const { return m_timestamp; } diff --git a/libtorrent/src/allocate_resources.cpp b/libtorrent/src/allocate_resources.cpp index 7e342ee95..deef06dc4 100644 --- a/libtorrent/src/allocate_resources.cpp +++ b/libtorrent/src/allocate_resources.cpp @@ -42,6 +42,8 @@ POSSIBILITY OF SUCH DAMAGE. //generation of the min and max macros in Visual C++, #define //NOMINMAX before #including . +#include "libtorrent/pch.hpp" + #ifdef _WIN32 //support boost1.32.0(2004-11-19 18:47) //now all libs can be compiled and linked with static module diff --git a/libtorrent/src/bandwidth_manager.cpp b/libtorrent/src/bandwidth_manager.cpp index a5868d439..19dc6fcf7 100644 --- a/libtorrent/src/bandwidth_manager.cpp +++ b/libtorrent/src/bandwidth_manager.cpp @@ -30,9 +30,13 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include "libtorrent/invariant_check.hpp" #include "libtorrent/bandwidth_manager.hpp" #include "libtorrent/peer_connection.hpp" +#include "libtorrent/time.hpp" + #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING #include "libtorrent/aux_/session_impl.hpp" #endif @@ -41,11 +45,11 @@ namespace libtorrent { namespace { - const pt::time_duration window_size = pt::seconds(1); + const time_duration window_size = seconds(1); } history_entry::history_entry(intrusive_ptr p - , weak_ptr t, int a, pt::ptime exp) + , weak_ptr t, int a, ptime exp) : expires_at(exp), amount(a), peer(p), tor(t) {} @@ -67,6 +71,8 @@ namespace libtorrent { INVARIANT_CHECK; + assert(!peer->ignore_bandwidth_limits()); + // make sure this peer isn't already in line // waiting for bandwidth #ifndef NDEBUG @@ -143,7 +149,7 @@ namespace libtorrent assert(!m_history.empty()); - pt::ptime now(pt::microsec_clock::universal_time()); + ptime now(time_now()); while (!m_history.empty() && m_history.back().expires_at <= now) { history_entry e = m_history.back(); @@ -180,7 +186,7 @@ namespace libtorrent // (*m_ses->m_logger) << "hand out bw [" << m_channel << "]\n"; #endif - pt::ptime now(pt::microsec_clock::universal_time()); + ptime now(time_now()); mutex_t::scoped_lock l(m_mutex); int limit = m_limit; @@ -240,3 +246,4 @@ namespace libtorrent { assert(false); }; } + diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index d96eea92a..082d856aa 100644 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -48,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_impl.hpp" -using namespace boost::posix_time; using boost::bind; using boost::shared_ptr; using libtorrent::aux::session_impl; @@ -77,9 +78,11 @@ namespace libtorrent 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, tcp::endpoint()) + , shared_ptr s + , tcp::endpoint const& remote + , policy::peer* peerinfo) + : peer_connection(ses, tor, s, remote + , peerinfo) , m_state(read_protocol_length) #ifndef TORRENT_DISABLE_EXTENSIONS , m_supports_extensions(false) @@ -116,8 +119,9 @@ namespace libtorrent bt_peer_connection::bt_peer_connection( session_impl& ses - , boost::shared_ptr s) - : peer_connection(ses, s) + , boost::shared_ptr s + , policy::peer* peerinfo) + : peer_connection(ses, s, peerinfo) , m_state(read_protocol_length) #ifndef TORRENT_DISABLE_EXTENSIONS , m_supports_extensions(false) @@ -163,7 +167,10 @@ namespace libtorrent void bt_peer_connection::write_dht_port(int listen_port) { INVARIANT_CHECK; - +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << time_now_string() + << " ==> DHT_PORT [ " << listen_port << " ]\n"; +#endif buffer::interval packet = allocate_send_buffer(7); detail::write_uint32(3, packet.begin); detail::write_uint8(msg_dht_port, packet.begin); @@ -183,8 +190,10 @@ namespace libtorrent p.pid = pid(); p.ip = remote(); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES p.country[0] = m_country[0]; p.country[1] = m_country[1]; +#endif p.total_download = statistics().total_payload_download(); p.total_upload = statistics().total_payload_upload(); @@ -236,6 +245,18 @@ namespace libtorrent p.client = m_client_version; p.connection_type = peer_info::standard_bittorrent; + + if (peer_info_struct()) + { + p.source = peer_info_struct()->source; + p.failcount = peer_info_struct()->failcount; + } + else + { + assert(!is_local()); + p.source = 0; + p.failcount = 0; + } } bool bt_peer_connection::in_handshake() const @@ -295,9 +316,7 @@ namespace libtorrent 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"; + (*m_logger) << time_now_string() << " ==> HANDSHAKE\n"; #endif setup_send(); } @@ -346,9 +365,7 @@ namespace libtorrent INVARIANT_CHECK; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== KEEPALIVE\n"; + (*m_logger) << time_now_string() << " <== KEEPALIVE\n"; #endif incoming_keepalive(); } @@ -367,14 +384,6 @@ namespace libtorrent m_statistics.received_bytes(0, received); if (!packet_finished()) return; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_choke()) return; - } -#endif - incoming_choke(); } @@ -392,14 +401,6 @@ namespace libtorrent m_statistics.received_bytes(0, received); if (!packet_finished()) return; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_unchoke()) return; - } -#endif - incoming_unchoke(); } @@ -417,14 +418,6 @@ namespace libtorrent m_statistics.received_bytes(0, received); if (!packet_finished()) return; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_interested()) return; - } -#endif - incoming_interested(); } @@ -442,14 +435,6 @@ namespace libtorrent m_statistics.received_bytes(0, received); if (!packet_finished()) return; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_not_interested()) return; - } -#endif - incoming_not_interested(); } @@ -472,14 +457,6 @@ namespace libtorrent const char* ptr = recv_buffer.begin + 1; int index = detail::read_int32(ptr); -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_have(index)) return; - } -#endif - incoming_have(index); } @@ -521,14 +498,6 @@ namespace libtorrent for (int i = 0; i < (int)bitfield.size(); ++i) bitfield[i] = (recv_buffer[1 + (i>>3)] & (1 << (7 - (i&7)))) != 0; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_bitfield(bitfield)) return; - } -#endif - incoming_bitfield(bitfield); } @@ -554,14 +523,6 @@ namespace libtorrent r.start = detail::read_int32(ptr); r.length = detail::read_int32(ptr); -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_request(r)) return; - } -#endif - incoming_request(r); } @@ -606,14 +567,6 @@ namespace libtorrent p.start = detail::read_int32(ptr); p.length = packet_size() - 9; -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_piece(p, recv_buffer.begin + 9)) return; - } -#endif - incoming_piece(p, recv_buffer.begin + 9); } @@ -639,14 +592,6 @@ namespace libtorrent r.start = detail::read_int32(ptr); r.length = detail::read_int32(ptr); -#ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end; ++i) - { - if ((*i)->on_cancel(r)) return; - } -#endif - incoming_cancel(r); } @@ -764,7 +709,7 @@ namespace libtorrent { tcp::endpoint adr(remote().address() , (unsigned short)listen_port->integer()); - t->get_policy().peer_from_tracker(adr, pid()); + t->get_policy().peer_from_tracker(adr, pid(), 0, 0); } } // there should be a version too @@ -891,16 +836,16 @@ namespace libtorrent assert(t->valid_metadata()); #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> BITFIELD "; + (*m_logger) << time_now_string() << " ==> BITFIELD "; + std::stringstream bitfield_string; for (int i = 0; i < (int)get_bitfield().size(); ++i) { - if (bitfield[i]) (*m_logger) << "1"; - else (*m_logger) << "0"; + if (bitfield[i]) bitfield_string << "1"; + else bitfield_string << "0"; } - (*m_logger) << "\n"; + bitfield_string << "\n"; + (*m_logger) << bitfield_string.str(); #endif const int packet_size = ((int)bitfield.size() + 7) / 8 + 5; @@ -928,9 +873,7 @@ namespace libtorrent INVARIANT_CHECK; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> EXTENSIONS\n"; + (*m_logger) << time_now_string() << " ==> EXTENSIONS\n"; #endif assert(m_supports_extensions); @@ -1132,16 +1075,6 @@ namespace libtorrent || !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 @@ -1282,12 +1215,16 @@ namespace libtorrent throw protocol_error("duplicate peer-id, connection closed"); } } - + } + + if (pid == m_ses.get_peer_id()) + { + throw protocol_error("closing connection to ourself"); } #ifndef TORRENT_DISABLE_DHT if (m_supports_dht_port && m_ses.m_dht) - write_dht_port(m_ses.kad_settings().service_port); + write_dht_port(m_ses.get_dht_settings().service_port); #endif m_client_version = identify_client(pid); @@ -1320,6 +1257,8 @@ namespace libtorrent if (m_supports_extensions) write_extensions(); #endif + // consider this a successful connection, reset the failcount + if (peer_info_struct()) peer_info_struct()->failcount = 0; m_state = read_packet_size; reset_recv_buffer(4); } diff --git a/libtorrent/src/connection_queue.cpp b/libtorrent/src/connection_queue.cpp new file mode 100644 index 000000000..f83baa196 --- /dev/null +++ b/libtorrent/src/connection_queue.cpp @@ -0,0 +1,169 @@ + +/* + +Copyright (c) 2007, 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/invariant_check.hpp" +#include "libtorrent/connection_queue.hpp" + +namespace libtorrent +{ + + connection_queue::connection_queue(io_service& ios): m_next_ticket(0) + , m_num_connecting(0) + , m_half_open_limit(0) + , m_timer(ios) + {} + + bool connection_queue::free_slots() const + { return m_num_connecting < m_half_open_limit || m_half_open_limit <= 0; } + + void connection_queue::enqueue(boost::function const& on_connect + , boost::function const& on_timeout + , time_duration timeout) + { + INVARIANT_CHECK; + + m_queue.push_back(entry()); + entry& e = m_queue.back(); + e.on_connect = on_connect; + e.on_timeout = on_timeout; + e.ticket = m_next_ticket; + e.timeout = timeout; + ++m_next_ticket; + try_connect(); + } + + void connection_queue::done(int ticket) + { + INVARIANT_CHECK; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::ticket, _1) == ticket); + if (i == m_queue.end()) + { + // this might not be here in case on_timeout calls remove + return; + } + if (i->connecting) --m_num_connecting; + m_queue.erase(i); + try_connect(); + } + + void connection_queue::limit(int limit) + { m_half_open_limit = limit; } + + int connection_queue::limit() const + { return m_half_open_limit; } + +#ifndef NDEBUG + + void connection_queue::check_invariant() const + { + int num_connecting = 0; + for (std::list::const_iterator i = m_queue.begin(); + i != m_queue.end(); ++i) + { + if (i->connecting) ++num_connecting; + } + assert(num_connecting == m_num_connecting); + } + +#endif + + void connection_queue::try_connect() + { + INVARIANT_CHECK; + + if (!free_slots() || m_queue.empty()) + return; + + std::list::iterator i = std::find_if(m_queue.begin() + , m_queue.end(), boost::bind(&entry::connecting, _1) == false); + while (i != m_queue.end()) + { + assert(i->connecting == false); + ptime expire = time_now() + i->timeout; + if (m_num_connecting == 0) + { + m_timer.expires_at(expire); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + } + i->connecting = true; + ++m_num_connecting; + i->expires = expire; + + INVARIANT_CHECK; + + entry& ent = *i; + ++i; + try { ent.on_connect(ent.ticket); } catch (std::exception&) {} + + if (!free_slots()) break; + i = std::find_if(i, m_queue.end(), boost::bind(&entry::connecting, _1) == false); + } + } + + void connection_queue::on_timeout(asio::error_code const& e) + { + INVARIANT_CHECK; + + assert(!e || e == asio::error::operation_aborted); + if (e) return; + + ptime next_expire = max_time(); + ptime now = time_now(); + for (std::list::iterator i = m_queue.begin(); + i != m_queue.end();) + { + if (i->connecting && i->expires < now) + { + boost::function on_timeout = i->on_timeout; + m_queue.erase(i++); + --m_num_connecting; + try { on_timeout(); } catch (std::exception&) {} + continue; + } + if (i->expires < next_expire) + next_expire = i->expires; + ++i; + } + if (next_expire < max_time()) + { + m_timer.expires_at(next_expire); + m_timer.async_wait(boost::bind(&connection_queue::on_timeout, this, _1)); + } + try_connect(); + } + +} + diff --git a/libtorrent/src/entry.cpp b/libtorrent/src/entry.cpp index 55cbc3649..6506ed4c2 100644 --- a/libtorrent/src/entry.cpp +++ b/libtorrent/src/entry.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include "libtorrent/entry.hpp" diff --git a/libtorrent/src/escape_string.cpp b/libtorrent/src/escape_string.cpp index 80d134ed7..02a4fa085 100644 --- a/libtorrent/src/escape_string.cpp +++ b/libtorrent/src/escape_string.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include diff --git a/libtorrent/src/file.cpp b/libtorrent/src/file.cpp index 2542f31de..71bc795a4 100644 --- a/libtorrent/src/file.cpp +++ b/libtorrent/src/file.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #ifdef _WIN32 // windows part #include "libtorrent/utf8.hpp" @@ -243,6 +245,17 @@ namespace libtorrent return ret; } + void set_size(size_type s) + { + size_type pos = tell(); + seek(1, 0); + char dummy = 0; + read(&dummy, 1); + seek(1, 0); + write(&dummy, 1); + seek(pos, 1); + } + size_type seek(size_type offset, int m) { assert(m_open_mode); @@ -316,6 +329,11 @@ namespace libtorrent return m_impl->read(buf, num_bytes); } + void file::set_size(size_type s) + { + m_impl->set_size(s); + } + size_type file::seek(size_type pos, file::seek_mode m) { return m_impl->seek(pos, m.m_val); diff --git a/libtorrent/src/file_pool.cpp b/libtorrent/src/file_pool.cpp index 2a4ad4972..ab4ea8d6c 100644 --- a/libtorrent/src/file_pool.cpp +++ b/libtorrent/src/file_pool.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include "libtorrent/file_pool.hpp" #include @@ -43,6 +45,7 @@ namespace libtorrent { assert(st != 0); assert(p.is_complete()); + assert(m == file::in || m == (file::in | file::out)); boost::mutex::scoped_lock l(m_mutex); typedef nth_index::type path_view; path_view& pt = get<0>(m_files); @@ -50,11 +53,15 @@ namespace libtorrent if (i != pt.end()) { lru_file_entry e = *i; - e.last_use = pt::second_clock::universal_time(); + e.last_use = time_now(); - // 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); + if (e.key != st) + { + // this means that another instance of the storage + // is using the exact same file. + throw file_error("torrent uses the same file as another torrent " + "(" + p.string() + ")"); + } e.key = st; if ((e.mode & m) != m) diff --git a/libtorrent/src/file_win.cpp b/libtorrent/src/file_win.cpp index 833c2124a..9d2c2f4bf 100644 --- a/libtorrent/src/file_win.cpp +++ b/libtorrent/src/file_win.cpp @@ -39,6 +39,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include +#include namespace { @@ -168,9 +169,13 @@ namespace libtorrent #endif if (new_handle == INVALID_HANDLE_VALUE) - { - std::stringstream s; throw_exception(file_name); + // try to make the file sparse if supported + if (access_mask & GENERIC_WRITE) + { + DWORD temp; + ::DeviceIoControl(new_handle, FSCTL_SET_SPARSE, 0, 0 + , 0, 0, &temp, 0); } // will only close old file if the open succeeded close(); @@ -233,6 +238,16 @@ namespace libtorrent return bytes_read; } + void set_size(size_type s) + { + size_type pos = tell(); + seek(s, seek_begin); + if (FALSE == ::SetEndOfFile(m_file_handle)) + throw_exception("file::set_size"); + + seek(pos, seek_begin); + } + size_type seek(size_type pos, seek_mode from_where) { assert(pos >= 0 || from_where != seek_begin); @@ -334,6 +349,11 @@ namespace libtorrent return m_impl->read(buffer, num_bytes); } + void file::set_size(size_type s) + { + m_impl->set_size(s); + } + size_type file::seek(size_type pos, seek_mode m) { return m_impl->seek(pos,impl::seek_mode(m.m_val)); diff --git a/libtorrent/src/http_connection.cpp b/libtorrent/src/http_connection.cpp new file mode 100644 index 000000000..ba1592898 --- /dev/null +++ b/libtorrent/src/http_connection.cpp @@ -0,0 +1,384 @@ +/* + +Copyright (c) 2007, 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/http_connection.hpp" + +#include +#include +#include +#include + +using boost::bind; + +namespace libtorrent +{ + +void http_connection::get(std::string const& url, time_duration timeout + , bool handle_redirect) +{ + m_redirect = handle_redirect; + std::string protocol; + std::string auth; + std::string hostname; + std::string path; + int port; + boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); + std::stringstream headers; + headers << "GET " << path << " HTTP/1.0\r\n" + "Host:" << hostname << + "\r\nConnection: close\r\n"; + if (!auth.empty()) + headers << "Authorization: Basic " << base64encode(auth) << "\r\n"; + headers << "\r\n"; + sendbuffer = headers.str(); + start(hostname, boost::lexical_cast(port), timeout); +} + +void http_connection::start(std::string const& hostname, std::string const& port + , time_duration timeout, bool handle_redirect) +{ + m_redirect = handle_redirect; + m_timeout = timeout; + m_timer.expires_from_now(m_timeout); + m_timer.async_wait(bind(&http_connection::on_timeout + , boost::weak_ptr(shared_from_this()), _1)); + m_called = false; + if (m_sock.is_open() && m_hostname == hostname && m_port == port) + { + m_parser.reset(); + asio::async_write(m_sock, asio::buffer(sendbuffer) + , bind(&http_connection::on_write, shared_from_this(), _1)); + } + else + { + m_sock.close(); + tcp::resolver::query query(hostname, port); + m_resolver.async_resolve(query, bind(&http_connection::on_resolve + , shared_from_this(), _1, _2)); + m_hostname = hostname; + m_port = port; + } +} + +void http_connection::on_connect_timeout() +{ + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; + + if (m_bottled && m_called) return; + m_called = true; + m_handler(asio::error::timed_out, m_parser, 0, 0); + close(); +} + +void http_connection::on_timeout(boost::weak_ptr p + , asio::error_code const& e) +{ + boost::shared_ptr c = p.lock(); + if (!c) return; + if (c->m_connection_ticket > -1) c->m_cc.done(c->m_connection_ticket); + c->m_connection_ticket = -1; + + if (e == asio::error::operation_aborted) return; + + if (c->m_bottled && c->m_called) return; + + if (c->m_last_receive + c->m_timeout < time_now()) + { + c->m_called = true; + c->m_handler(asio::error::timed_out, c->m_parser, 0, 0); + return; + } + + c->m_timer.expires_at(c->m_last_receive + c->m_timeout); + c->m_timer.async_wait(bind(&http_connection::on_timeout, p, _1)); +} + +void http_connection::close() +{ + m_timer.cancel(); + m_limiter_timer.cancel(); + m_sock.close(); + m_hostname.clear(); + m_port.clear(); + + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; +} + +void http_connection::on_resolve(asio::error_code const& e + , tcp::resolver::iterator i) +{ + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + assert(i != tcp::resolver::iterator()); + m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) + , bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_timeout); +} + +void http_connection::connect(int ticket, tcp::endpoint target_address) +{ + m_connection_ticket = ticket; + m_sock.async_connect(target_address, boost::bind(&http_connection::on_connect + , shared_from_this(), _1/*, ++i*/)); +} + +void http_connection::on_connect(asio::error_code const& e + /*, tcp::resolver::iterator i*/) +{ + if (!e) + { + m_last_receive = time_now(); + asio::async_write(m_sock, asio::buffer(sendbuffer) + , bind(&http_connection::on_write, shared_from_this(), _1)); + } +/* else if (i != tcp::resolver::iterator()) + { + // The connection failed. Try the next endpoint in the list. + m_sock.close(); + m_cc.enqueue(bind(&http_connection::connect, shared_from_this(), _1, *i) + , bind(&http_connection::on_connect_timeout, shared_from_this()) + , m_timeout); + } +*/ else + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + } +} + +void http_connection::on_write(asio::error_code const& e) +{ + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + std::string().swap(sendbuffer); + m_recvbuffer.resize(4096); + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + on_assign_bandwidth(asio::error_code()); + return; + } + } + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_read(asio::error_code const& e + , std::size_t bytes_transferred) +{ + if (m_rate_limit) + { + m_download_quota -= bytes_transferred; + assert(m_download_quota >= 0); + } + + if (e == asio::error::eof) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + char const* data = 0; + std::size_t size = 0; + if (m_bottled) + { + data = m_parser.get_body().begin; + size = m_parser.get_body().left(); + } + m_handler(asio::error_code(), m_parser, data, size); + return; + } + + if (e) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + m_read_pos += bytes_transferred; + assert(m_read_pos <= int(m_recvbuffer.size())); + + // having a nonempty path means we should handle redirects + if (m_redirect && m_parser.header_finished()) + { + int code = m_parser.status_code(); + if (code >= 300 && code < 400) + { + // attempt a redirect + std::string url = m_parser.header("location"); + if (url.empty()) + { + // missing location header + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, 0, 0); + return; + } + + m_limiter_timer_active = false; + close(); + + get(url, m_timeout); + return; + } + + m_redirect = false; + } + + if (m_bottled || !m_parser.header_finished()) + { + libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0] + , &m_recvbuffer[0] + m_read_pos); + m_parser.incoming(rcv_buf); + if (!m_bottled && m_parser.header_finished()) + { + if (m_read_pos > m_parser.body_start()) + m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start() + , m_read_pos - m_parser.body_start()); + m_read_pos = 0; + m_last_receive = time_now(); + } + else if (m_bottled && m_parser.finished()) + { + m_timer.cancel(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(e, m_parser, m_parser.get_body().begin, m_parser.get_body().left()); + } + } + else + { + assert(!m_bottled); + m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos); + m_read_pos = 0; + m_last_receive = time_now(); + } + + if (int(m_recvbuffer.size()) == m_read_pos) + m_recvbuffer.resize((std::min)(m_read_pos + 2048, 1024*500)); + if (m_read_pos == 1024 * 500) + { + close(); + if (m_bottled && m_called) return; + m_called = true; + m_handler(asio::error::eof, m_parser, 0, 0); + return; + } + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + on_assign_bandwidth(asio::error_code()); + return; + } + } + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); +} + +void http_connection::on_assign_bandwidth(asio::error_code const& e) +{ + if ((e == asio::error::operation_aborted + && m_limiter_timer_active) + || !m_sock.is_open()) + { + if (!m_bottled || !m_called) + m_handler(e, m_parser, 0, 0); + return; + } + m_limiter_timer_active = false; + if (e) return; + + if (m_download_quota > 0) return; + + m_download_quota = m_rate_limit / 4; + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (amount_to_read > m_download_quota) + amount_to_read = m_download_quota; + + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); + + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); +} + +void http_connection::rate_limit(int limit) +{ + if (!m_limiter_timer_active) + { + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(milliseconds(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); + } + m_rate_limit = limit; +} + +} + diff --git a/libtorrent/src/http_stream.cpp b/libtorrent/src/http_stream.cpp new file mode 100644 index 000000000..0973af798 --- /dev/null +++ b/libtorrent/src/http_stream.cpp @@ -0,0 +1,162 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/http_stream.hpp" +#include "libtorrent/tracker_manager.hpp" // for base64encode + +namespace libtorrent +{ + + void http_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &http_stream::connected, this, _1, h)); + } + + void http_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + if (m_no_connect) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + + // send CONNECT + std::back_insert_iterator > p(m_buffer); + write_string("CONNECT " + boost::lexical_cast(m_remote_endpoint) + + " HTTP/1.0\r\n", p); + if (!m_user.empty()) + { + write_string("Proxy-Authorization: Basic " + base64encode( + m_user + ":" + m_password) + "\r\n", p); + } + write_string("\r\n", p); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake1, this, _1, h)); + } + + void http_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + // read one byte from the socket + m_buffer.resize(1); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + + void http_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + int read_pos = m_buffer.size(); + // look for \n\n and \r\n\r\n + // both of which means end of http response header + bool found_end = false; + if (m_buffer[read_pos - 1] == '\n' && read_pos > 2) + { + if (m_buffer[read_pos - 2] == '\n') + { + found_end = true; + } + else if (read_pos > 4 + && m_buffer[read_pos - 2] == '\r' + && m_buffer[read_pos - 3] == '\n' + && m_buffer[read_pos - 4] == '\r') + { + found_end = true; + } + } + + if (found_end) + { + m_buffer.push_back(0); + char* status = strchr(&m_buffer[0], ' '); + if (status == 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + status++; + int code = atoi(status); + if (code != 200) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + (*h)(e); + std::vector().swap(m_buffer); + return; + } + + // read another byte from the socket + m_buffer.resize(read_pos + 1); + asio::async_read(m_sock, asio::buffer(&m_buffer[0] + read_pos, 1) + , boost::bind(&http_stream::handshake2, this, _1, h)); + } + +} + diff --git a/libtorrent/src/http_tracker_connection.cpp b/libtorrent/src/http_tracker_connection.cpp index 43f93d4c7..94b1e422a 100644 --- a/libtorrent/src/http_tracker_connection.cpp +++ b/libtorrent/src/http_tracker_connection.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -56,6 +58,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/bencode.hpp" #include "libtorrent/torrent.hpp" #include "libtorrent/io.hpp" +#include "libtorrent/instantiate_connection.hpp" using namespace libtorrent; using boost::bind; @@ -84,8 +87,6 @@ namespace } -using namespace boost::posix_time; - namespace { bool url_has_argument(std::string const& url, std::string argument) @@ -170,20 +171,17 @@ namespace libtorrent char const* line_end = newline; if (pos != line_end && *(line_end - 1) == '\r') --line_end; line.assign(pos, line_end); + ++newline; m_recv_pos += newline - pos; boost::get<1>(ret) += newline - pos; pos = newline; - std::string::size_type separator = line.find(": "); + std::string::size_type separator = line.find(':'); if (separator == std::string::npos) { // this means we got a blank line, // the header is finished and the body // starts. - ++pos; - ++m_recv_pos; - boost::get<1>(ret) += 1; - m_state = read_body; m_body_start_pos = m_recv_pos; break; @@ -191,7 +189,12 @@ namespace libtorrent std::string name = line.substr(0, separator); std::transform(name.begin(), name.end(), name.begin(), &to_lower); - std::string value = line.substr(separator + 2, std::string::npos); + ++separator; + // skip whitespace + while (separator < line.size() + && (line[separator] == ' ' || line[separator] == '\t')) + ++separator; + std::string value = line.substr(separator, std::string::npos); m_header.insert(std::make_pair(name, value)); if (name == "content-length") @@ -217,9 +220,6 @@ namespace libtorrent m_content_length = range_end - range_start + 1; } - // 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'); } @@ -272,6 +272,7 @@ namespace libtorrent http_tracker_connection::http_tracker_connection( asio::strand& str + , connection_queue& cc , tracker_manager& man , tracker_request const& req , std::string const& hostname @@ -280,6 +281,7 @@ namespace libtorrent , address bind_infc , boost::weak_ptr c , session_settings const& stn + , proxy_settings const& ps , std::string const& auth) : tracker_connection(man, req, str, bind_infc, c) , m_man(man) @@ -289,19 +291,18 @@ namespace libtorrent , m_recv_pos(0) , m_buffer(http_buffer_size) , m_settings(stn) + , m_proxy(ps) , m_password(auth) , m_timed_out(false) + , m_connection_ticket(-1) + , m_cc(cc) { - 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()) + if (m_proxy.type == proxy_settings::http + || m_proxy.type == proxy_settings::http_pw) { - connect_to_host = &m_settings.proxy_ip; - using_proxy = true; m_send_buffer += "http://"; m_send_buffer += hostname; if (port != 80) @@ -309,12 +310,6 @@ namespace libtorrent 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) @@ -441,12 +436,12 @@ namespace libtorrent m_send_buffer += ':'; m_send_buffer += boost::lexical_cast(port); } - if (using_proxy && !m_settings.proxy_login.empty()) + if (m_proxy.type == proxy_settings::http_pw) { m_send_buffer += "\r\nProxy-Authorization: Basic "; - m_send_buffer += base64encode(m_settings.proxy_login + ":" + m_settings.proxy_password); + m_send_buffer += base64encode(m_proxy.username + ":" + m_proxy.password); } - if (auth != "") + if (!auth.empty()) { m_send_buffer += "\r\nAuthorization: Basic "; m_send_buffer += base64encode(auth); @@ -461,11 +456,11 @@ namespace libtorrent info_hash_str << req.info_hash; requester().debug_log("info_hash: " + boost::lexical_cast(req.info_hash)); - requester().debug_log("name lookup: " + *connect_to_host); + requester().debug_log("name lookup: " + hostname); } #endif - tcp::resolver::query q(*connect_to_host + tcp::resolver::query q(hostname , boost::lexical_cast(m_port)); m_name_lookup.async_resolve(q, m_strand.wrap( boost::bind(&http_tracker_connection::name_lookup, self(), _1, _2))); @@ -478,6 +473,8 @@ namespace libtorrent m_timed_out = true; m_socket.reset(); m_name_lookup.cancel(); + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; fail_timeout(); } @@ -527,19 +524,37 @@ namespace libtorrent } if (has_requester()) requester().m_tracker_address = target_address; - m_socket.reset(new stream_socket(m_name_lookup.io_service())); + m_socket = instantiate_connection(m_name_lookup.io_service(), m_proxy); + + if (m_proxy.type == proxy_settings::http + || m_proxy.type == proxy_settings::http_pw) + { + // the tracker connection will talk immediately to + // the proxy, without requiring CONNECT support + m_socket->get().set_no_connect(true); + } + m_socket->open(target_address.protocol()); m_socket->bind(tcp::endpoint(bind_interface(), 0)); - m_socket->async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1)); + m_cc.enqueue(bind(&http_tracker_connection::connect, self(), _1, target_address) + , bind(&http_tracker_connection::on_timeout, self()) + , seconds(m_settings.tracker_receive_timeout)); } catch (std::exception& e) { - assert(false); fail(-1, e.what()); }; + void http_tracker_connection::connect(int ticket, tcp::endpoint target_address) + { + m_connection_ticket = ticket; + m_socket->async_connect(target_address, bind(&http_tracker_connection::connected, self(), _1)); + } + void http_tracker_connection::connected(asio::error_code const& error) try { + if (m_connection_ticket > -1) m_cc.done(m_connection_ticket); + m_connection_ticket = -1; if (error == asio::error::operation_aborted) return; if (m_timed_out) return; if (error) @@ -559,7 +574,6 @@ namespace libtorrent } catch (std::exception& e) { - assert(false); fail(-1, e.what()); } @@ -584,7 +598,6 @@ namespace libtorrent } catch (std::exception& e) { - assert(false); fail(-1, e.what()); }; // msvc 7.1 seems to require this semi-colon @@ -704,11 +717,18 @@ namespace libtorrent req.url = location; - m_man.queue_request(m_strand, req + m_man.queue_request(m_strand, m_cc, req , m_password, bind_interface(), m_requester); close(); return; } + + if (m_parser.status_code() != 200) + { + fail(m_parser.status_code(), m_parser.message().c_str()); + close(); + return; + } buffer::const_interval buf(&m_buffer[0] + m_parser.body_start(), &m_buffer[0] + m_recv_pos); diff --git a/libtorrent/src/identify_client.cpp b/libtorrent/src/identify_client.cpp index bfc06363c..cf837a05b 100644 --- a/libtorrent/src/identify_client.cpp +++ b/libtorrent/src/identify_client.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include @@ -133,63 +135,77 @@ namespace return boost::optional(ret); } - typedef std::pair map_entry; + struct map_entry + { + char const* id; + char const* name; + }; // 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("BF", "Bitflu") - , map_entry("BG", "btgdaemon") - , 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("HL", "Halite") - , map_entry("KT", "KTorrent") - , map_entry("LK", "Linkage") - , map_entry("LP", "lphant") - , map_entry("LT", "libtorrent") - , map_entry("M", "Mainline") - , map_entry("ML", "MLDonkey") - , map_entry("MO", "Mono Torrent") - , map_entry("MP", "MooPolice") - , map_entry("MT", "Moonlight Torrent") - , map_entry("O", "Osprey Permaseed") - , map_entry("QT", "Qt 4") - , 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("XX", "Xtorrent") - , map_entry("ZT", "ZipTorrent") - , map_entry("lt", "libTorrent (libtorrent.rakshasa.no/)") - , map_entry("pX", "pHoeniX") - , map_entry("qB", "qBittorrent") + {"A", "ABC"} + , {"AG", "Ares"} + , {"AR", "Arctic Torrent"} + , {"AV", "Avicora"} + , {"AX", "BitPump"} + , {"AZ", "Azureus"} + , {"A~", "Ares"} + , {"BB", "BitBuddy"} + , {"BC", "BitComet"} + , {"BF", "Bitflu"} + , {"BG", "BTG"} + , {"BR", "BitRocket"} + , {"BS", "BTSlave"} + , {"BX", "BittorrentX"} + , {"CD", "Enhanced CTorrent"} + , {"CT", "CTorrent"} + , {"DE", "Deluge Torrent"} + , {"EB", "EBit"} + , {"ES", "electric sheep"} + , {"HL", "Halite"} + , {"HN", "Hydranode"} + , {"KT", "KTorrent"} + , {"LK", "Linkage"} + , {"LP", "lphant"} + , {"LT", "libtorrent"} + , {"M", "Mainline"} + , {"ML", "MLDonkey"} + , {"MO", "Mono Torrent"} + , {"MP", "MooPolice"} + , {"MT", "Moonlight Torrent"} + , {"O", "Osprey Permaseed"} + , {"PD", "Pando"} + , {"Q", "BTQueue"} + , {"QT", "Qt 4"} + , {"R", "Tribler"} + , {"S", "Shadow"} + , {"SB", "Swiftbit"} + , {"SN", "ShareNet"} + , {"SS", "SwarmScope"} + , {"SZ", "Shareaza"} + , {"S~", "Shareaza (beta)"} + , {"T", "BitTornado"} + , {"TN", "Torrent.NET"} + , {"TR", "Transmission"} + , {"TS", "TorrentStorm"} + , {"TT", "TuoTu"} + , {"U", "UPnP"} + , {"UL", "uLeecher"} + , {"UT", "MicroTorrent"} + , {"XT", "XanTorrent"} + , {"XX", "Xtorrent"} + , {"ZT", "ZipTorrent"} + , {"lt", "libTorrent (libtorrent.rakshasa.no/}"} + , {"pX", "pHoeniX"} + , {"qB", "qBittorrent"} }; - bool compare_first_string(map_entry const& lhs, map_entry const& rhs) + bool compare_id(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])); + return lhs.id[0] < rhs.id[0] + || ((lhs.id[0] == rhs.id[0]) && (lhs.id[1] < rhs.id[1])); } std::string lookup(fingerprint const& f) @@ -197,20 +213,21 @@ namespace std::stringstream identity; const int size = sizeof(name_map)/sizeof(name_map[0]); + map_entry tmp = {f.name, ""}; map_entry* i = std::lower_bound(name_map, name_map + size - , map_entry(f.name, ""), &compare_first_string); + , tmp, &compare_id); #ifndef NDEBUG for (int i = 1; i < size; ++i) { - assert(compare_first_string(name_map[i-1] + assert(compare_id(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; + if (i < name_map + size && std::equal(f.name, f.name + 2, i->id)) + identity << i->name; else { identity << f.name[0]; diff --git a/libtorrent/src/instantiate_connection.cpp b/libtorrent/src/instantiate_connection.cpp new file mode 100644 index 000000000..ff4efbc59 --- /dev/null +++ b/libtorrent/src/instantiate_connection.cpp @@ -0,0 +1,78 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/session_settings.hpp" +#include "libtorrent/socket_type.hpp" +#include +#include +#include + +namespace libtorrent +{ + + boost::shared_ptr instantiate_connection( + asio::io_service& ios, proxy_settings const& ps) + { + boost::shared_ptr s(new socket_type(ios)); + + if (ps.type == proxy_settings::none) + { + s->instantiate(); + } + else if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + s->get().set_username(ps.username, ps.password); + } + else if (ps.type == proxy_settings::socks5 + || ps.type == proxy_settings::socks5_pw) + { + s->instantiate(); + s->get().set_proxy(ps.hostname, ps.port); + if (ps.type == proxy_settings::socks5_pw) + s->get().set_username(ps.username, ps.password); + } + else + { + throw std::runtime_error("unsupported proxy type"); + } + return s; + } + +} + diff --git a/libtorrent/src/ip_filter.cpp b/libtorrent/src/ip_filter.cpp index 0afb5a346..92ea711c7 100644 --- a/libtorrent/src/ip_filter.cpp +++ b/libtorrent/src/ip_filter.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include "libtorrent/ip_filter.hpp" #include //#include diff --git a/libtorrent/src/logger.cpp b/libtorrent/src/logger.cpp index be101d02c..6881c5e7b 100644 --- a/libtorrent/src/logger.cpp +++ b/libtorrent/src/logger.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #ifdef _MSC_VER #pragma warning(push, 1) #endif @@ -38,7 +40,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #ifdef _MSC_VER #pragma warning(pop) @@ -70,9 +71,7 @@ namespace libtorrent { namespace void log_timestamp() { - using namespace boost::posix_time; - std::string now(to_simple_string(second_clock::universal_time())); - m_file << now << ": "; + m_file << time_now_string() << ": "; } // can add entries to the extension handshake diff --git a/libtorrent/src/lsd.cpp b/libtorrent/src/lsd.cpp new file mode 100644 index 000000000..b73e407bc --- /dev/null +++ b/libtorrent/src/lsd.cpp @@ -0,0 +1,250 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/lsd.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/xml_parse.hpp" +#include +#include +#include +#include +#include +#include +#include + +using boost::bind; +using namespace libtorrent; + +namespace libtorrent +{ + // defined in upnp.cpp + address_v4 guess_local_address(asio::io_service&); +} + +address_v4 lsd::lsd_multicast_address; +udp::endpoint lsd::lsd_multicast_endpoint; + +lsd::lsd(io_service& ios, address const& listen_interface + , peer_callback_t const& cb) + : m_callback(cb) + , m_retry_count(0) + , m_socket(ios) + , m_broadcast_timer(ios) + , m_disabled(false) +{ + // Bittorrent Local discovery multicast address and port + lsd_multicast_address = address_v4::from_string("239.192.152.143"); + lsd_multicast_endpoint = udp::endpoint(lsd_multicast_address, 6771); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + assert(lsd_multicast_address.is_multicast()); + rebind(listen_interface); +} + +lsd::~lsd() {} + +void lsd::rebind(address const& listen_interface) +{ + address_v4 local_ip = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + local_ip = listen_interface.to_v4(); + } + + try + { + // the local interface hasn't changed + if (m_socket.is_open() + && m_socket.local_endpoint().address() == local_ip) + return; + + m_socket.close(); + + using namespace asio::ip::multicast; + + m_socket.open(udp::v4()); + m_socket.set_option(datagram_socket::reuse_address(true)); + m_socket.bind(udp::endpoint(local_ip, 6771)); + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "local ip: " << local_ip << std::endl; +#endif + + m_socket.set_option(join_group(lsd_multicast_address)); + m_socket.set_option(outbound_interface(local_ip)); + m_socket.set_option(enable_loopback(true)); + m_socket.set_option(hops(255)); + } + catch (std::exception& e) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "socket multicast error " << e.what() + << ". disabling local service discovery" << std::endl; +#endif + m_disabled = true; + return; + } + m_disabled = false; + + setup_receive(); +} + +void lsd::announce(sha1_hash const& ih, int listen_port) +{ + if (m_disabled) return; + + std::stringstream btsearch; + btsearch << "BT-SEARCH * HTTP/1.1\r\n" + "Host: 239.192.152.143:6771\r\n" + "Port: " << listen_port << "\r\n" + "Infohash: " << ih << "\r\n" + "\r\n\r\n"; + std::string const& msg = btsearch.str(); + + m_retry_count = 0; + asio::error_code ec; + m_socket.send_to(asio::buffer(msg.c_str(), msg.size() - 1) + , lsd_multicast_endpoint, 0, ec); + if (ec) + { + m_disabled = true; + return; + } + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " ==> announce: ih: " << ih << " port: " << listen_port << std::endl; +#endif + + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg)); +} + +void lsd::resend_announce(asio::error_code const& e, std::string msg) try +{ + if (e) return; + + m_socket.send_to(asio::buffer(msg, msg.size() - 1) + , lsd_multicast_endpoint); + + ++m_retry_count; + if (m_retry_count >= 5) + return; + + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(bind(&lsd::resend_announce, this, _1, msg)); +} +catch (std::exception&) +{} + +void lsd::on_announce(asio::error_code const& e + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + if (e) return; + + char* p = m_receive_buffer; + char* end = m_receive_buffer + bytes_transferred; + char* line = std::find(p, end, '\n'); + for (char* i = p; i < line; ++i) *i = std::tolower(*i); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " <== announce: " << std::string(p, line) << std::endl; +#endif + if (line == end || (line - p >= 9 && std::memcmp("bt-search", p, 9))) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " *** assumed 'bt-search', ignoring" << std::endl; +#endif + setup_receive(); + return; + } + p = line + 1; + int port = 0; + sha1_hash ih(0); + while (p != end) + { + line = std::find(p, end, '\n'); + if (line == end) break; + *line = 0; + for (char* i = p; i < line; ++i) *i = std::tolower(*i); + if (line - p >= 5 && memcmp(p, "port:", 5) == 0) + { + p += 5; + while (*p == ' ') ++p; + port = atoi(p); + } + else if (line - p >= 9 && memcmp(p, "infohash:", 9) == 0) + { + p += 9; + while (*p == ' ') ++p; + if (line - p > 40) p[40] = 0; + try { ih = boost::lexical_cast(p); } + catch (std::exception&) {} + } + p = line + 1; + } + + if (!ih.is_all_zeros() && port != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " *** incoming local announce " << m_remote.address() + << ":" << port << " ih: " << ih << std::endl; +#endif + // we got an announce, pass it on through the callback + try { m_callback(tcp::endpoint(m_remote.address(), port), ih); } + catch (std::exception&) {} + } + setup_receive(); +} + +void lsd::setup_receive() try +{ + assert(m_socket.is_open()); + m_socket.async_receive_from(asio::buffer(m_receive_buffer + , sizeof(m_receive_buffer)), m_remote, bind(&lsd::on_announce, this, _1, _2)); +} +catch (std::exception&) +{} + +void lsd::close() +{ + m_socket.close(); +} + diff --git a/libtorrent/src/metadata_transfer.cpp b/libtorrent/src/metadata_transfer.cpp index 3777cd71e..fe308f926 100644 --- a/libtorrent/src/metadata_transfer.cpp +++ b/libtorrent/src/metadata_transfer.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #ifdef _MSC_VER #pragma warning(push, 1) #endif @@ -38,7 +40,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #ifdef _MSC_VER #pragma warning(pop) @@ -56,8 +57,6 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/extensions.hpp" #include "libtorrent/extensions/metadata_transfer.hpp" -using boost::posix_time::second_clock; - namespace libtorrent { namespace { int div_round_up(int numerator, int denominator) @@ -107,7 +106,15 @@ namespace libtorrent { namespace { m_requested_metadata.resize(256, 0); } - + + virtual void on_files_checked() + { + // if the torrent is a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + virtual boost::shared_ptr new_connection( peer_connection* pc); @@ -212,6 +219,14 @@ namespace libtorrent { namespace m_metadata_size = total_size; } + void piece_pass(int) + { + // if we became a seed, copy the metadata from + // the torrent before it is deallocated + if (m_torrent.is_seed()) + metadata(); + } + private: torrent& m_torrent; @@ -243,12 +258,8 @@ namespace libtorrent { namespace : m_waiting_metadata_request(false) , m_message_index(0) , m_metadata_progress(0) - , 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_no_metadata(min_time()) + , m_metadata_request(min_time()) , m_torrent(t) , m_pc(pc) , m_tp(tp) @@ -411,7 +422,7 @@ namespace libtorrent { namespace } break; case 2: // have no data - m_no_metadata = second_clock::universal_time(); + m_no_metadata = time_now(); if (m_waiting_metadata_request) m_tp.cancel_metadata_request(m_last_metadata_request); m_waiting_metadata_request = false; @@ -437,14 +448,13 @@ namespace libtorrent { namespace m_last_metadata_request = m_tp.metadata_request(); write_metadata_request(m_last_metadata_request); m_waiting_metadata_request = true; - m_metadata_request = second_clock::universal_time(); + m_metadata_request = time_now(); } } bool has_metadata() const { - using namespace boost::posix_time; - return second_clock::universal_time() - m_no_metadata > minutes(5); + return time_now() - m_no_metadata > minutes(5); } private: @@ -467,11 +477,11 @@ namespace libtorrent { namespace // this is set to the current time each time we get a // "I don't have metadata" message. - boost::posix_time::ptime m_no_metadata; + ptime m_no_metadata; // this is set to the time when we last sent // a request for metadata to this peer - boost::posix_time::ptime m_metadata_request; + ptime m_metadata_request; // if we're waiting for a metadata request // this was the request we sent @@ -485,6 +495,8 @@ namespace libtorrent { namespace boost::shared_ptr metadata_plugin::new_connection( peer_connection* pc) { + bt_peer_connection* c = dynamic_cast(pc); + if (!c) return boost::shared_ptr(); return boost::shared_ptr(new metadata_peer_plugin(m_torrent, *pc, *this)); } diff --git a/libtorrent/src/natpmp.cpp b/libtorrent/src/natpmp.cpp new file mode 100644 index 000000000..0a5932a56 --- /dev/null +++ b/libtorrent/src/natpmp.cpp @@ -0,0 +1,393 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include +#include +#include +#include + +using boost::bind; +using namespace libtorrent; + +enum { num_mappings = 2 }; + +namespace libtorrent +{ + // defined in upnp.cpp + bool is_local(address const& a); + address_v4 guess_local_address(asio::io_service&); +} + +natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb) + : m_callback(cb) + , m_currently_mapping(-1) + , m_retry_count(0) + , m_socket(ios) + , m_send_timer(ios) + , m_refresh_timer(ios) + , m_disabled(false) +{ + m_mappings[0].protocol = 2; // tcp + m_mappings[1].protocol = 1; // udp + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log.open("natpmp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + rebind(listen_interface); +} + +void natpmp::rebind(address const& listen_interface) try +{ + address_v4 local = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + local = listen_interface.to_v4(); + } + else + { + local = guess_local_address(m_socket.io_service()); + + if (local == address_v4::any()) + { + throw std::runtime_error("local host is probably not on a NATed " + "network. disabling NAT-PMP"); + } + } + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " local ip: " << local.to_string() << std::endl; +#endif + + if (!is_local(local)) + { + // the local address seems to be an external + // internet address. Assume it is not behind a NAT + throw std::runtime_error("local IP is not on a local network"); + } + + m_disabled = false; + + // assume the router is located on the local + // network as x.x.x.1 + udp::endpoint nat_endpoint( + address_v4((local.to_ulong() & 0xffffff00) | 1), 5351); + + if (nat_endpoint == m_nat_endpoint) return; + + // TODO: find a better way to figure out the router IP + m_nat_endpoint = nat_endpoint; + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl; +#endif + + m_socket.open(udp::v4()); + m_socket.bind(udp::endpoint(address_v4::any(), 0)); + + for (int i = 0; i < num_mappings; ++i) + { + if (m_mappings[i].local_port == 0) + continue; + refresh_mapping(i); + } +} +catch (std::exception& e) +{ + m_disabled = true; + std::stringstream msg; + msg << "NAT-PMP disabled: " << e.what(); +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << msg.str() << std::endl; +#endif + m_callback(0, 0, msg.str()); +}; + +void natpmp::set_mappings(int tcp, int udp) +{ + if (m_disabled) return; + update_mapping(0, tcp); + update_mapping(1, udp); +} + +void natpmp::update_mapping(int i, int port) +{ + natpmp::mapping& m = m_mappings[i]; + if (port <= 0) return; + if (m.local_port != port) + m.need_update = true; + + m.local_port = port; + // prefer the same external port as the local port + if (m.external_port == 0) m.external_port = port; + + if (m_currently_mapping == -1) + { + // the socket is not currently in use + // send out a mapping request + m_retry_count = 0; + send_map_request(i); + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + } +} + +void natpmp::send_map_request(int i) try +{ + using namespace libtorrent::detail; + + assert(m_currently_mapping == -1 + || m_currently_mapping == i); + m_currently_mapping = i; + mapping& m = m_mappings[i]; + char buf[12]; + char* out = buf; + write_uint8(0, out); // NAT-PMP version + write_uint8(m.protocol, out); // map "protocol" + write_uint16(0, out); // reserved + write_uint16(m.local_port, out); // private port + write_uint16(m.external_port, out); // requested public port + int ttl = m.external_port == 0 ? 0 : 3600; + write_uint32(ttl, out); // port mapping lifetime + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " ==> port map request: " << (m.protocol == 1 ? "udp" : "tcp") + << " local: " << m.local_port << " external: " << m.external_port + << " ttl: " << ttl << std::endl; +#endif + + m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint); + // linear back-off instead of exponential + ++m_retry_count; + m_send_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_send_timer.async_wait(bind(&natpmp::resend_request, this, i, _1)); +} +catch (std::exception& e) +{ + std::string err = e.what(); +}; + +void natpmp::resend_request(int i, asio::error_code const& e) +{ + if (e) return; + if (m_currently_mapping != i) return; + if (m_retry_count >= 9) + { + m_mappings[i].need_update = false; + // try again in two hours + m_mappings[i].expires = time_now() + hours(2); + return; + } + send_map_request(i); +} + +void natpmp::on_reply(asio::error_code const& e + , std::size_t bytes_transferred) +{ + using namespace libtorrent::detail; + if (e) return; + + try + { + + if (m_remote != m_nat_endpoint) + { + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + return; + } + + m_send_timer.cancel(); + + assert(m_currently_mapping >= 0); + int i = m_currently_mapping; + mapping& m = m_mappings[i]; + + char* in = m_response_buffer; + int version = read_uint8(in); + int cmd = read_uint8(in); + int result = read_uint16(in); + int time = read_uint32(in); + int private_port = read_uint16(in); + int public_port = read_uint16(in); + int lifetime = read_uint32(in); + + (void)time; // to remove warning + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << time_now_string() + << " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp") + << " local: " << private_port << " external: " << public_port + << " ttl: " << lifetime << std::endl; +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (version != 0) + { + m_log << "*** unexpected version: " << version << std::endl; + } +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (private_port != m.local_port) + { + m_log << "*** unexpected local port: " << private_port << std::endl; + } +#endif + +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + if (cmd != 128 + m.protocol) + { + m_log << "*** unexpected protocol: " << (cmd - 128) << std::endl; + } +#endif + + if (public_port == 0 || lifetime == 0) + { + // this means the mapping was + // successfully closed + m.local_port = 0; + } + else + { + m.expires = time_now() + seconds(int(lifetime * 0.7f)); + m.external_port = public_port; + } + + if (result != 0) + { +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "*** ERROR: " << result << std::endl; +#endif + std::stringstream errmsg; + errmsg << "NAT router reports error (" << result << ") "; + switch (result) + { + case 1: errmsg << "Unsupported protocol version"; break; + case 2: errmsg << "Not authorized to create port map (enable NAT-PMP on your router)"; break; + case 3: errmsg << "Network failure"; break; + case 4: errmsg << "Out of resources"; break; + case 5: errmsg << "Unsupported opcode"; break; + } + throw std::runtime_error(errmsg.str()); + } + + // don't report when we remove mappings + if (m.local_port != 0) + { + int tcp_port = 0; + int udp_port = 0; + if (m.protocol == 1) udp_port = m.external_port; + else tcp_port = public_port; + m_callback(tcp_port, udp_port, ""); + } + } + catch (std::exception& e) + { + // try again in two hours + m_mappings[m_currently_mapping].expires = time_now() + hours(2); + m_callback(0, 0, e.what()); + } + int i = m_currently_mapping; + m_currently_mapping = -1; + m_mappings[i].need_update = false; + m_send_timer.cancel(); + update_expiration_timer(); + try_next_mapping(i); +} + +void natpmp::update_expiration_timer() +{ + ptime now = time_now(); + ptime min_expire = now + seconds(3600); + int min_index = -1; + for (int i = 0; i < num_mappings; ++i) + if (m_mappings[i].expires < min_expire + && m_mappings[i].local_port != 0) + { + min_expire = m_mappings[i].expires; + min_index = i; + } + + if (min_index >= 0) + { + m_refresh_timer.expires_from_now(min_expire - now); + m_refresh_timer.async_wait(bind(&natpmp::mapping_expired, this, _1, min_index)); + } +} + +void natpmp::mapping_expired(asio::error_code const& e, int i) +{ + if (e) return; +#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) + m_log << "*** mapping " << i << " expired, updating" << std::endl; +#endif + refresh_mapping(i); +} + +void natpmp::refresh_mapping(int i) +{ + m_mappings[i].need_update = true; + if (m_currently_mapping == -1) + { + // the socket is not currently in use + // send out a mapping request + m_retry_count = 0; + send_map_request(i); + m_socket.async_receive_from(asio::buffer(&m_response_buffer, 16) + , m_remote, bind(&natpmp::on_reply, this, _1, _2)); + } +} + +void natpmp::try_next_mapping(int i) +{ + ++i; + if (i >= num_mappings) i = 0; + if (m_mappings[i].need_update) + refresh_mapping(i); +} + +void natpmp::close() +{ + if (m_disabled) return; + for (int i = 0; i < num_mappings; ++i) + { + if (m_mappings[i].local_port == 0) + continue; + m_mappings[i].external_port = 0; + refresh_mapping(i); + } +} + diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index def1aeb22..7ba83e889 100644 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -47,8 +49,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/version.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/policy.hpp" +#include "libtorrent/socket_type.hpp" -using namespace boost::posix_time; using boost::bind; using boost::shared_ptr; using libtorrent::aux::session_impl; @@ -71,31 +74,30 @@ namespace libtorrent delete c; } + // outbound connection peer_connection::peer_connection( session_impl& ses , boost::weak_ptr tor - , shared_ptr s + , shared_ptr s , tcp::endpoint const& remote - , tcp::endpoint const& proxy) + , policy::peer* peerinfo) : #ifndef NDEBUG - m_last_choke(boost::posix_time::second_clock::universal_time() - - hours(1)) + m_last_choke(time_now() - 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_last_piece(time_now()) , 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_last_receive(time_now()) + , m_last_sent(time_now()) , m_socket(s) , m_remote(remote) - , m_remote_proxy(proxy) , m_torrent(tor) , m_active(true) , m_peer_interested(false) @@ -103,6 +105,7 @@ namespace libtorrent , m_interesting(false) , m_choked(true) , m_failed(false) + , m_ignore_bandwidth_limits(false) , m_num_pieces(0) , m_desired_queue_size(2) , m_free_upload(0) @@ -110,23 +113,29 @@ namespace libtorrent , 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_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) , m_connecting(true) , m_queued(true) , m_writing(false) , m_reading(false) , m_prefer_whole_pieces(false) + , m_on_parole(false) , m_request_large_blocks(false) , m_non_prioritized(false) , m_refs(0) , m_upload_limit(resource_request::inf) , m_download_limit(resource_request::inf) + , m_peer_info(peerinfo) + , m_speed(slow) + , m_connection_ticket(-1) #ifndef NDEBUG , m_in_constructor(true) #endif { +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES std::fill(m_country, m_country + 2, 0); +#endif #ifdef TORRENT_VERBOSE_LOGGING m_logger = m_ses.create_log(m_remote.address().to_string() + "_" + boost::lexical_cast(m_remote.port()), m_ses.listen_port()); @@ -141,25 +150,26 @@ namespace libtorrent init(); } + // incoming connection peer_connection::peer_connection( session_impl& ses - , boost::shared_ptr s) + , boost::shared_ptr s + , policy::peer* peerinfo) : #ifndef NDEBUG - m_last_choke(boost::posix_time::second_clock::universal_time() - - hours(1)) + m_last_choke(time_now() - 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_last_piece(time_now()) , 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_last_receive(time_now()) + , m_last_sent(time_now()) , m_socket(s) , m_active(false) , m_peer_interested(false) @@ -167,6 +177,7 @@ namespace libtorrent , m_interesting(false) , m_choked(true) , m_failed(false) + , m_ignore_bandwidth_limits(false) , m_num_pieces(0) , m_desired_queue_size(2) , m_free_upload(0) @@ -174,23 +185,30 @@ namespace libtorrent , 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_became_uninterested(time_now()) + , m_became_uninteresting(time_now()) , m_connecting(false) , m_queued(false) , m_writing(false) , m_reading(false) , m_prefer_whole_pieces(false) + , m_on_parole(false) , m_request_large_blocks(false) , m_non_prioritized(false) , m_refs(0) , m_upload_limit(resource_request::inf) , m_download_limit(resource_request::inf) + , m_peer_info(peerinfo) + , m_speed(slow) #ifndef NDEBUG , m_in_constructor(true) #endif { + tcp::socket::non_blocking_io ioc(true); + m_socket->io_control(ioc); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES std::fill(m_country, m_country + 2, 0); +#endif m_remote = m_socket->remote_endpoint(); #ifdef TORRENT_VERBOSE_LOGGING @@ -203,6 +221,38 @@ namespace libtorrent std::fill(m_peer_id.begin(), m_peer_id.end(), 0); } + void peer_connection::update_interest() + { + INVARIANT_CHECK; + + boost::shared_ptr t = m_torrent.lock(); + assert(t); + + bool interested = false; + const std::vector& we_have = t->pieces(); + for (int j = 0; j != (int)we_have.size(); ++j) + { + if (!we_have[j] + && t->piece_priority(j) > 0 + && m_have_piece[j]) + { + interested = true; + break; + } + } + try + { + if (!interested) + send_not_interested(); + else + t->get_policy().peer_is_interesting(*this); + } + // may throw an asio error if socket has disconnected + catch (std::exception& e) {} + + assert(is_interesting() == interested); + } + #ifndef TORRENT_DISABLE_EXTENSIONS void peer_connection::add_extension(boost::shared_ptr ext) { @@ -224,48 +274,45 @@ namespace libtorrent // 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()) + int num_pieces = std::count(m_have_piece.begin(), m_have_piece.end(), true); + if (num_pieces == int(m_have_piece.size())) { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " *** THIS IS A SEED ***\n"; #endif + // if this is a web seed. we don't have a peer_info struct + if (m_peer_info) m_peer_info->seed = true; // 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"); } + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; } - if (interesting) - t->get_policy().peer_is_interesting(*this); + m_num_pieces = num_pieces; + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + bool interesting = false; + for (int i = 0; i < int(m_have_piece.size()); ++i) + { + if (m_have_piece[i]) + { + t->peer_has(i); + // if the peer has a piece and we don't, the peer is interesting + if (!t->have_piece(i) + && t->picker().piece_priority(i) != 0) + interesting = true; + } + } + if (interesting) + t->get_policy().peer_is_interesting(*this); + } } peer_connection::~peer_connection() @@ -274,14 +321,16 @@ namespace libtorrent assert(m_disconnecting); #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; if (m_logger) { - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " *** CONNECTION CLOSED\n"; } #endif #ifndef NDEBUG + if (m_peer_info) + assert(m_peer_info->connection == 0); + boost::shared_ptr t = m_torrent.lock(); if (t) assert(t->connection_for(remote()) != this); #endif @@ -294,8 +343,7 @@ namespace libtorrent if (has_piece(index)) return; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " ==> HAVE [ piece: " << index << "]\n"; #endif write_have(index); @@ -357,6 +405,8 @@ namespace libtorrent } #endif + m_on_parole = false; + m_trust_points++; // TODO: make this limit user settable if (m_trust_points > 20) m_trust_points = 20; @@ -374,6 +424,8 @@ namespace libtorrent } #endif + m_on_parole = true; + // we decrease more than we increase, to keep the // allowed failed/passed ratio low. // TODO: make this limit user settable @@ -495,9 +547,7 @@ namespace libtorrent INVARIANT_CHECK; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== KEEPALIVE\n"; + (*m_logger) << time_now_string() << " <== KEEPALIVE\n"; #endif } @@ -512,10 +562,16 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_choke()) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== CHOKE\n"; + (*m_logger) << time_now_string() << " <== CHOKE\n"; #endif m_peer_choked = true; t->get_policy().choked(*this); @@ -554,10 +610,16 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_unchoke()) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== UNCHOKE\n"; + (*m_logger) << time_now_string() << " <== UNCHOKE\n"; #endif m_peer_choked = false; t->get_policy().unchoked(*this); @@ -574,10 +636,16 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_interested()) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== INTERESTED\n"; + (*m_logger) << time_now_string() << " <== INTERESTED\n"; #endif m_peer_interested = true; t->get_policy().interested(*this); @@ -591,16 +659,22 @@ namespace libtorrent { INVARIANT_CHECK; - m_became_uninterested = second_clock::universal_time(); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_not_interested()) return; + } +#endif + + m_became_uninterested = time_now(); // 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"; + (*m_logger) << time_now_string() << " <== NOT_INTERESTED\n"; #endif boost::shared_ptr t = m_torrent.lock(); @@ -621,9 +695,16 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_have(index)) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== HAVE [ piece: " << index << "]\n"; #endif @@ -654,13 +735,18 @@ namespace libtorrent if (!t->have_piece(index) && !t->is_seed() && !is_interesting() - && !t->picker().is_filtered(index)) + && t->picker().piece_priority(index) != 0) t->get_policy().peer_is_interesting(*this); } - if (t->is_seed() && is_seed()) + if (is_seed()) { - throw protocol_error("seed to seed connection redundant, disconnecting"); + assert(m_peer_info); + m_peer_info->seed = true; + if (t->is_seed()) + { + throw protocol_error("seed to seed connection redundant, disconnecting"); + } } } } @@ -676,10 +762,23 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_bitfield(bitfield)) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " <== BITFIELD\n"; + (*m_logger) << time_now_string() << " <== BITFIELD "; + + for (int i = 0; i < int(bitfield.size()); ++i) + { + if (bitfield[i]) (*m_logger) << "1"; + else (*m_logger) << "0"; + } + (*m_logger) << "\n"; #endif // if we don't have the metedata, we cannot @@ -700,46 +799,79 @@ namespace libtorrent { m_have_piece = bitfield; m_num_pieces = std::count(bitfield.begin(), bitfield.end(), true); + + if (m_peer_info) m_peer_info->seed = true; return; } - // let the torrent know which pieces the - // peer has - bool interesting = false; - 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; - t->peer_has(i); - if (!t->have_piece(i) - && !t->picker().is_filtered(i)) - interesting = true; - } - 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); - } - } - - if (m_num_pieces == int(m_have_piece.size())) + int num_pieces = std::count(bitfield.begin(), bitfield.end(), true); + if (num_pieces == int(m_have_piece.size())) { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " *** THIS IS A SEED ***\n"; #endif + // if this is a web seed. we don't have a peer_info struct + if (m_peer_info) m_peer_info->seed = true; // if we're a seed too, disconnect if (t->is_seed()) { throw protocol_error("seed to seed connection redundant, disconnecting"); } + + std::fill(m_have_piece.begin(), m_have_piece.end(), true); + m_num_pieces = num_pieces; + t->peer_has_all(); + if (!t->is_finished()) + t->get_policy().peer_is_interesting(*this); + return; } - if (interesting) t->get_policy().peer_is_interesting(*this); + // let the torrent know which pieces the + // peer has + // if we're a seed, we don't keep track of piece availability + if (!t->is_seed()) + { + bool interesting = false; + 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; + t->peer_has(i); + if (!t->have_piece(i) && t->picker().piece_priority(i) != 0) + interesting = true; + } + 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); + } + } + + if (interesting) t->get_policy().peer_is_interesting(*this); + } + else + { + 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; + } + else if (!have && m_have_piece[i]) + { + // this should probably not be allowed + m_have_piece[i] = false; + --m_num_pieces; + } + } + } } // ----------------------------- @@ -753,13 +885,20 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_request(r)) return; + } +#endif + 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()) + (*m_logger) << time_now_string() << " <== UNEXPECTED_REQUEST [ " "piece: " << r.piece << " | " "s: " << r.start << " | " @@ -778,8 +917,7 @@ namespace libtorrent // 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()) + (*m_logger) << time_now_string() << " <== TOO MANY REQUESTS [ " "piece: " << r.piece << " | " "s: " << r.start << " | " @@ -804,8 +942,7 @@ namespace libtorrent && m_peer_interested) { #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== REQUEST [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif // if we have choked the client @@ -819,8 +956,7 @@ namespace libtorrent else { #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== INVALID_REQUEST [ " "piece: " << r.piece << " | " "s: " << r.start << " | " @@ -847,7 +983,7 @@ namespace libtorrent void peer_connection::incoming_piece_fragment() { - m_last_piece = second_clock::universal_time(); + m_last_piece = time_now(); } #ifndef NDEBUG @@ -871,7 +1007,7 @@ namespace libtorrent for (std::vector::const_iterator i = dl_queue.begin(); i != dl_queue.end(); ++i) { - assert(int(i->finished_blocks.count()) < blocks_per_piece); + assert(i->finished < blocks_per_piece); } } } @@ -891,13 +1027,22 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); assert(t); + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_piece(p, data)) return; + } +#endif + #ifndef NDEBUG check_postcondition post_checker_(t); t->check_invariant(); #endif #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== PIECE [ piece: " << p.piece << " | " "s: " << p.start << " | " "l: " << p.length << " | " @@ -908,8 +1053,7 @@ namespace libtorrent if (!verify_piece(p)) { #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== INVALID_PIECE [ piece: " << p.piece << " | " "start: " << p.start << " | " "length: " << p.length << " ]\n"; @@ -917,8 +1061,6 @@ namespace libtorrent throw protocol_error("got invalid piece packet"); } - using namespace boost::posix_time; - // if we're already seeding, don't bother, // just ignore it if (t->is_seed()) @@ -955,7 +1097,7 @@ namespace libtorrent i != b; ++i) { #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " *** SKIPPED_PIECE [ piece: " << i->piece_index << " | " "b: " << i->block_index << " ] ***\n"; #endif @@ -1063,7 +1205,7 @@ namespace libtorrent // in case we just became a seed t->announce_piece(p.piece); assert(t->valid_metadata()); - // if we jsut became a seed, picker is now invalid, since it + // if we just became a seed, picker is now invalid, since it // is deallocated by the torrent once it starts seeding if (!was_finished && (t->is_seed() @@ -1075,9 +1217,12 @@ namespace libtorrent // been downloaded. Release the files (they will open // in read only mode if needed) try { t->finished(); } - catch (std::exception&) + catch (std::exception& e) { +#ifndef NDEBUG + std::cerr << e.what() << std::endl; assert(false); +#endif } } } @@ -1086,20 +1231,49 @@ namespace libtorrent t->piece_failed(p.piece); } +#ifndef NDEBUG + try + { +#endif + pol.piece_finished(p.piece, verified); +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + +#ifndef NDEBUG + try + { +#endif + if (!was_seed && t->is_seed()) { assert(verified); t->completed(); } + +#ifndef NDEBUG + } + catch (std::exception const& e) + { + std::cerr << e.what() << std::endl; + assert(false); + } +#endif + } #ifndef NDEBUG } catch (std::exception const& e) { - std::string err = e.what(); + std::cerr << e.what() << std::endl; assert(false); } #endif @@ -1113,9 +1287,16 @@ namespace libtorrent { INVARIANT_CHECK; +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + if ((*i)->on_cancel(r)) return; + } +#endif + #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== CANCEL [ piece: " << r.piece << " | s: " << r.start << " | l: " << r.length << " ]\n"; #endif @@ -1129,9 +1310,7 @@ namespace libtorrent 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"; + (*m_logger) << time_now_string() << " *** GOT CANCEL NOT IN THE QUEUE\n"; #endif } } @@ -1145,8 +1324,7 @@ namespace libtorrent INVARIANT_CHECK; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " <== DHT_PORT [ p: " << listen_port << " ]\n"; #endif #ifndef TORRENT_DISABLE_DHT @@ -1169,7 +1347,14 @@ namespace libtorrent 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); + piece_picker::piece_state_t state; + peer_speed_t speed = peer_speed(); + if (speed == fast) state = piece_picker::fast; + else if (speed == medium) state = piece_picker::medium; + else state = piece_picker::slow; + + t->picker().mark_as_downloading(block, m_remote, state); + m_request_queue.push_back(block); } @@ -1222,8 +1407,7 @@ namespace libtorrent write_cancel(r); #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " ==> CANCEL [ piece: " << block.piece_index << " | s: " << block_offset << " | l: " << block_size << " | " << block.block_index << " ]\n"; #endif @@ -1238,13 +1422,10 @@ namespace libtorrent m_choked = true; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> CHOKE\n"; + (*m_logger) << time_now_string() << " ==> CHOKE\n"; #endif #ifndef NDEBUG - using namespace boost::posix_time; - m_last_choke = second_clock::universal_time(); + m_last_choke = time_now(); #endif m_num_invalid_requests = 0; m_requests.clear(); @@ -1259,8 +1440,7 @@ namespace libtorrent // 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)); + //assert(time_now() - m_last_choke > seconds(9)); #endif if (!m_choked) return; @@ -1268,9 +1448,7 @@ namespace libtorrent m_choked = false; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> UNCHOKE\n"; + (*m_logger) << time_now_string() << " ==> UNCHOKE\n"; #endif } @@ -1283,9 +1461,7 @@ namespace libtorrent m_interesting = true; #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> INTERESTED\n"; + (*m_logger) << time_now_string() << " ==> INTERESTED\n"; #endif } @@ -1297,12 +1473,10 @@ namespace libtorrent write_not_interested(); m_interesting = false; - m_became_uninteresting = second_clock::universal_time(); + m_became_uninteresting = time_now(); #ifdef TORRENT_VERBOSE_LOGGING - using namespace boost::posix_time; - (*m_logger) << to_simple_string(second_clock::universal_time()) - << " ==> NOT_INTERESTED\n"; + (*m_logger) << time_now_string() << " ==> NOT_INTERESTED\n"; #endif } @@ -1339,7 +1513,7 @@ namespace libtorrent m_download_queue.push_back(block); /* #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " *** REQUEST-QUEUE** [ " "piece: " << block.piece_index << " | " "block: " << block.block_index << " ]\n"; @@ -1358,7 +1532,7 @@ namespace libtorrent m_download_queue.push_back(block); /* #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " *** REQUEST-QUEUE** [ " "piece: " << block.piece_index << " | " "block: " << block.block_index << " ]\n"; @@ -1388,10 +1562,8 @@ namespace libtorrent write_request(r); #endif - using namespace boost::posix_time; - #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(second_clock::universal_time()) + (*m_logger) << time_now_string() << " ==> REQUEST [ " "piece: " << r.piece << " | " "s: " << r.start << " | " @@ -1400,14 +1572,23 @@ namespace libtorrent "qs: " << m_desired_queue_size << " ]\n"; #endif } - m_last_piece = second_clock::universal_time(); + m_last_piece = time_now(); } - void close_socket_ignore_error(boost::shared_ptr s) + void close_socket_ignore_error(boost::shared_ptr s) { - asio::error_code e; - s->close(e); + try { s->close(); } catch (std::exception& e) {} + } + + void peer_connection::timed_out() + { + if (m_peer_info) ++m_peer_info->failcount; +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_ses.m_logger) << "CONNECTION TIMED OUT: " << m_remote.address().to_string() + << "\n"; +#endif + m_ses.connection_failed(m_socket, m_remote, "timed out"); } void peer_connection::disconnect() @@ -1418,16 +1599,19 @@ namespace libtorrent if (m_disconnecting) return; m_disconnecting = true; + if (m_connecting) + m_ses.m_half_open.done(m_connection_ticket); + m_ses.m_io_service.post(boost::bind(&close_socket_ignore_error, m_socket)); boost::shared_ptr t = m_torrent.lock(); if (t) { - if (t->valid_metadata() && !t->is_seed()) + if (t->has_picker()) { piece_picker& picker = t->picker(); - + while (!m_download_queue.empty()) { picker.abort_download(m_download_queue.back()); @@ -1485,16 +1669,27 @@ namespace libtorrent - m_statistics.total_payload_upload(); } + // defined in upnp.cpp + bool is_local(address const& a); + + bool peer_connection::on_local_network() const + { + if (libtorrent::is_local(m_remote.address())) return true; + return false; + } + + void peer_connection::cut_receive_buffer(int size, int packet_size) { INVARIANT_CHECK; assert(packet_size > 0); - assert((int)m_recv_buffer.size() >= size); - // TODO: replace with memmov - std::copy(m_recv_buffer.begin() + size, m_recv_buffer.begin() + m_recv_pos, m_recv_buffer.begin()); - + assert(int(m_recv_buffer.size()) >= size); + assert(int(m_recv_buffer.size()) >= m_recv_pos); assert(m_recv_pos >= size); + + std::memmove(&m_recv_buffer[0], &m_recv_buffer[0] + size, m_recv_pos - size); + m_recv_pos -= size; #ifndef NDEBUG @@ -1509,7 +1704,7 @@ namespace libtorrent { INVARIANT_CHECK; - ptime now(second_clock::universal_time()); + ptime now(time_now()); boost::shared_ptr t = m_torrent.lock(); assert(t); @@ -1524,6 +1719,9 @@ namespace libtorrent } #endif + m_ignore_bandwidth_limits = m_ses.settings().ignore_limits_on_local_network + && on_local_network(); + m_statistics.second_tick(tick_interval); if (!t->valid_metadata()) return; @@ -1555,9 +1753,9 @@ namespace libtorrent // in this case we'll clear our download queue and // re-request the blocks. #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << to_simple_string(now) + (*m_logger) << time_now_string() << " *** PIECE_REQUESTS TIMED OUT [ " << (int)m_download_queue.size() - << " " << to_simple_string(now - m_last_piece) << "] ***\n"; + << " " << total_seconds(now - m_last_piece) << "] ***\n"; #endif if (t->is_seed()) @@ -1678,9 +1876,13 @@ namespace libtorrent // 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 + + int buffer_size_watermark = int(m_statistics.upload_rate()) / 2; + if (buffer_size_watermark < 1024) buffer_size_watermark = 1024; + else if (buffer_size_watermark > 80 * 1024) buffer_size_watermark = 80 * 1024; + while (!m_requests.empty() - && (send_buffer_size() < t->block_size() * 6) + && (send_buffer_size() < buffer_size_watermark) && !m_choked) { assert(t->valid_metadata()); @@ -1695,10 +1897,9 @@ namespace libtorrent 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"; + (*m_logger) << time_now_string() + << " ==> PIECE [ piece: " << r.piece << " | s: " << r.start + << " | l: " << r.length << " ]\n"; #endif m_requests.erase(m_requests.begin()); @@ -1763,12 +1964,13 @@ namespace libtorrent if (m_writing) return; shared_ptr t = m_torrent.lock(); - + if (m_bandwidth_limit[upload_channel].quota_left() == 0 && (!m_send_buffer[m_current_send_buffer].empty() || !m_send_buffer[(m_current_send_buffer + 1) & 1].empty()) && !m_connecting - && t) + && t + && !m_ignore_bandwidth_limits) { // in this case, we have data to send, but no // bandwidth. So, we simply request bandwidth @@ -1780,8 +1982,9 @@ namespace libtorrent (*m_logger) << "req bandwidth [ " << upload_channel << " ]\n"; #endif - // the upload queue should not have non-prioritized peers - t->request_bandwidth(upload_channel, self(), false); + // peers that we are not interested in are non-prioritized + t->request_bandwidth(upload_channel, self() + , !(is_interesting() && !has_peer_choked())); m_writing = true; } return; @@ -1803,13 +2006,17 @@ namespace libtorrent // send the actual buffer if (!m_send_buffer[sending_buffer].empty()) { - int amount_to_send - = std::min(m_bandwidth_limit[upload_channel].quota_left() - , (int)m_send_buffer[sending_buffer].size() - m_write_pos); + int amount_to_send = (int)m_send_buffer[sending_buffer].size() - m_write_pos; + int quota_left = m_bandwidth_limit[upload_channel].quota_left(); + if (!m_ignore_bandwidth_limits && amount_to_send > quota_left) + amount_to_send = quota_left; assert(amount_to_send > 0); assert(m_write_pos < (int)m_send_buffer[sending_buffer].size()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "async_write " << amount_to_send << " bytes\n"; +#endif 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)); @@ -1830,9 +2037,9 @@ namespace libtorrent if (m_bandwidth_limit[download_channel].quota_left() == 0 && !m_connecting - && t) + && t + && !m_ignore_bandwidth_limits) { - assert(t); if (m_bandwidth_limit[download_channel].max_assignable() > 0) { #ifdef TORRENT_VERBOSE_LOGGING @@ -1847,9 +2054,11 @@ namespace libtorrent if (!can_read()) return; assert(m_packet_size > 0); - int max_receive = std::min( - m_bandwidth_limit[download_channel].quota_left() - , m_packet_size - m_recv_pos); + int max_receive = m_packet_size - m_recv_pos; + int quota_left = m_bandwidth_limit[download_channel].quota_left(); + if (!m_ignore_bandwidth_limits && max_receive > quota_left) + max_receive = quota_left; + assert(max_receive > 0); assert(m_recv_pos >= 0); @@ -1857,6 +2066,9 @@ namespace libtorrent assert(max_receive > 0); assert(can_read()); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "async_read " << max_receive << " bytes\n"; +#endif 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; @@ -1904,7 +2116,6 @@ namespace libtorrent bool m_cond; }; - // -------------------------- // RECEIVE DATA // -------------------------- @@ -1919,8 +2130,10 @@ namespace libtorrent assert(m_reading); m_reading = false; - // correct the dl quota usage, if not all of the buffer was actually read - m_bandwidth_limit[download_channel].use_quota(bytes_transferred); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "read " << bytes_transferred << " bytes\n"; +#endif if (error) { @@ -1931,21 +2144,52 @@ namespace libtorrent throw std::runtime_error(error.message()); } - 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; - assert(m_recv_pos <= int(m_recv_buffer.size())); - + do { - INVARIANT_CHECK; - on_receive(error, bytes_transferred); - } + // correct the dl quota usage, if not all of the buffer was actually read + if (!m_ignore_bandwidth_limits) + m_bandwidth_limit[download_channel].use_quota(bytes_transferred); - assert(m_packet_size > 0); + if (m_disconnecting) return; + + assert(m_packet_size > 0); + assert(bytes_transferred > 0); + + m_last_receive = time_now(); + m_recv_pos += bytes_transferred; + assert(m_recv_pos <= int(m_recv_buffer.size())); + + { + INVARIANT_CHECK; + on_receive(error, bytes_transferred); + } + + assert(m_packet_size > 0); + + if (m_peer_choked + && m_recv_pos == 0 + && (m_recv_buffer.capacity() - m_packet_size) > 128) + { + std::vector(m_packet_size).swap(m_recv_buffer); + } + + int max_receive = m_packet_size - m_recv_pos; + int quota_left = m_bandwidth_limit[download_channel].quota_left(); + if (!m_ignore_bandwidth_limits && max_receive > quota_left) + max_receive = quota_left; + + if (max_receive == 0) break; + + asio::error_code ec; + bytes_transferred = m_socket->read_some(asio::buffer(&m_recv_buffer[m_recv_pos] + , max_receive), ec); + if (ec && ec != asio::error::would_block) + throw asio::system_error(ec); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "read_some " << bytes_transferred << " bytes\n"; +#endif + } + while (bytes_transferred > 0); setup_receive(); } @@ -1989,7 +2233,8 @@ namespace libtorrent // 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_bandwidth_limit[upload_channel].quota_left() > 0 + && (m_bandwidth_limit[upload_channel].quota_left() > 0 + || m_ignore_bandwidth_limits) && !m_connecting; } @@ -1997,35 +2242,35 @@ namespace libtorrent { INVARIANT_CHECK; - return m_bandwidth_limit[download_channel].quota_left() > 0 + return (m_bandwidth_limit[download_channel].quota_left() > 0 + || m_ignore_bandwidth_limits) && !m_connecting; } - void peer_connection::connect() + void peer_connection::connect(int ticket) { INVARIANT_CHECK; #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() << "\n"; + (*m_ses.m_logger) << "CONNECTING: " << m_remote.address().to_string() + << ":" << m_remote.port() << "\n"; #endif + m_connection_ticket = ticket; boost::shared_ptr t = m_torrent.lock(); assert(t); m_queued = false; assert(m_connecting); m_socket->open(t->get_interface().protocol()); + + // set the socket to non-blocking, so that we can + // read the entire buffer on each read event we get + tcp::socket::non_blocking_io ioc(true); + m_socket->io_control(ioc); m_socket->bind(t->get_interface()); - if (m_remote_proxy != tcp::endpoint()) - { - m_socket->async_connect(m_remote_proxy - , bind(&peer_connection::on_connection_complete, self(), _1)); - } - else - { - m_socket->async_connect(m_remote - , bind(&peer_connection::on_connection_complete, self(), _1)); - } + m_socket->async_connect(m_remote + , bind(&peer_connection::on_connection_complete, self(), _1)); if (t->alerts().should_post(alert::debug)) { @@ -2040,6 +2285,11 @@ namespace libtorrent INVARIANT_CHECK; + if (m_disconnecting) return; + + m_connecting = false; + m_ses.m_half_open.done(m_connection_ticket); + if (e) { #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) @@ -2051,7 +2301,7 @@ namespace libtorrent } if (m_disconnecting) return; - m_last_receive = second_clock::universal_time(); + m_last_receive = time_now(); // this means the connection just succeeded @@ -2059,8 +2309,6 @@ namespace libtorrent (*m_ses.m_logger) << "COMPLETED: " << m_remote.address().to_string() << "\n"; #endif - m_ses.connection_completed(self()); - m_connecting = false; on_connected(); setup_send(); setup_receive(); @@ -2093,7 +2341,13 @@ namespace libtorrent assert(m_writing); m_writing = false; - m_bandwidth_limit[upload_channel].use_quota(bytes_transferred); + if (!m_ignore_bandwidth_limits) + m_bandwidth_limit[upload_channel].use_quota(bytes_transferred); + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "wrote " << bytes_transferred << " bytes\n"; +#endif + m_write_pos += bytes_transferred; if (error) @@ -2117,10 +2371,25 @@ namespace libtorrent m_write_pos = 0; } - m_last_sent = second_clock::universal_time(); + m_last_sent = time_now(); on_sent(error, bytes_transferred); fill_send_buffer(); + + if (m_choked) + { + for (int i = 0; i < 2; ++i) + { + if (int(m_send_buffer[i].size()) < 64 + && int(m_send_buffer[i].capacity()) > 128) + { + std::vector tmp(m_send_buffer[i]); + tmp.swap(m_send_buffer[i]); + assert(m_send_buffer[i].capacity() == m_send_buffer[i].size()); + } + } + } + setup_send(); } catch (std::exception& e) @@ -2140,6 +2409,10 @@ namespace libtorrent #ifndef NDEBUG void peer_connection::check_invariant() const { + if (m_peer_info) + assert(m_peer_info->connection == this + || m_peer_info->connection == 0); + boost::shared_ptr t = m_torrent.lock(); if (!t) { @@ -2153,7 +2426,8 @@ namespace libtorrent return; } - if (!m_in_constructor && t->connection_for(remote()) != this) + if (!m_in_constructor && t->connection_for(remote()) != this + && !m_ses.settings().allow_multiple_connections_per_ip) { assert(false); } @@ -2197,7 +2471,7 @@ namespace libtorrent } else { - assert(i->info[j].peer != m_remote || i->finished_blocks[j]); + assert(i->info[j].peer != m_remote || i->info[j].finished); } } } @@ -2216,9 +2490,7 @@ namespace libtorrent return false; #endif - using namespace boost::posix_time; - - ptime now(second_clock::universal_time()); + ptime now(time_now()); // if the socket is still connecting, don't // consider it timed out. Because Windows XP SP2 @@ -2228,7 +2500,7 @@ namespace libtorrent // 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; + d = time_now() - m_last_receive; if (d > seconds(m_timeout)) return true; // if the peer hasn't become interested and we haven't @@ -2250,14 +2522,33 @@ namespace libtorrent return false; } + peer_connection::peer_speed_t peer_connection::peer_speed() + { + shared_ptr t = m_torrent.lock(); + assert(t); + + int download_rate = int(statistics().download_payload_rate()); + int torrent_download_rate = int(t->statistics().download_payload_rate()); + + if (download_rate > 512 && download_rate > torrent_download_rate / 16) + m_speed = fast; + else if (download_rate > 4096 && download_rate > torrent_download_rate / 64) + m_speed = medium; + else if (download_rate < torrent_download_rate / 15 && m_speed == fast) + m_speed = medium; + else if (download_rate < torrent_download_rate / 63 && m_speed == medium) + m_speed = slow; + + return m_speed; + } 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; + time_duration d; + d = time_now() - m_last_sent; + if (total_seconds(d) < m_timeout / 2) return; if (m_connecting) return; diff --git a/libtorrent/src/piece_picker.cpp b/libtorrent/src/piece_picker.cpp index 593b72d47..cf6eb4a0e 100644 --- a/libtorrent/src/piece_picker.cpp +++ b/libtorrent/src/piece_picker.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -46,18 +48,18 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/torrent.hpp" #endif -//#define TORRENT_PIECE_PICKER_INVARIANT_CHECK INVARIANT_CHECK -#define TORRENT_PIECE_PICKER_INVARIANT_CHECK +#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_num_have(0) , m_sequenced_download_threshold(100) { assert(blocks_per_piece > 0); @@ -75,14 +77,13 @@ namespace libtorrent 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)); + m_num_have = m_piece_map.size(); } // pieces is a bitmask with the pieces we have @@ -93,40 +94,18 @@ namespace libtorrent #ifndef NDEBUG m_files_checked_called = true; #endif - // 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_piece_map[index].index = 0; + --m_num_have; + 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 @@ -139,7 +118,7 @@ namespace libtorrent tcp::endpoint peer; for (int j = 0; j < m_blocks_per_piece; ++j) { - if (i->finished_blocks[j]) + if (i->info[j].finished) mark_as_finished(piece_block(i->index, j), peer); } if (is_piece_finished(i->index)) @@ -160,6 +139,8 @@ namespace libtorrent if (sequenced_download_threshold == m_sequenced_download_threshold) return; + assert(sequenced_download_threshold > 0); + int old_limit = m_sequenced_download_threshold; m_sequenced_download_threshold = sequenced_download_threshold; @@ -169,9 +150,9 @@ namespace libtorrent 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); + if (prev_priority == 0) continue; + move(prev_priority, p.index); } } @@ -211,28 +192,112 @@ namespace libtorrent } } + piece_picker::downloading_piece& piece_picker::add_download_piece() + { + int num_downloads = m_downloads.size(); + int block_index = num_downloads * m_blocks_per_piece; + if (int(m_block_info.size()) < block_index + m_blocks_per_piece) + { + m_block_info.resize(block_index + m_blocks_per_piece); + if (!m_downloads.empty() && &m_block_info[0] != m_downloads.front().info) + { + // this means the memory was reallocated, update the pointers + for (int i = 0; i < int(m_downloads.size()); ++i) + m_downloads[i].info = &m_block_info[i * m_blocks_per_piece]; + } + } + m_downloads.push_back(downloading_piece()); + downloading_piece& ret = m_downloads.back(); + ret.info = &m_block_info[block_index]; + for (int i = 0; i < m_blocks_per_piece; ++i) + { + ret.info[i].num_downloads = 0; + ret.info[i].requested = 0; + ret.info[i].finished = 0; + ret.info[i].peer = tcp::endpoint(); + } + return ret; + } + + void piece_picker::erase_download_piece(std::vector::iterator i) + { + if (i != m_downloads.end() - 1) + { + int remove_index = i - m_downloads.begin(); + int last_index = m_downloads.size() - 1; + assert(remove_index < last_index); + + assert(int(m_block_info.size()) >= last_index * m_blocks_per_piece + m_blocks_per_piece); + std::copy(m_block_info.begin() + (last_index * m_blocks_per_piece) + , m_block_info.begin() + (last_index * m_blocks_per_piece + m_blocks_per_piece) + , m_block_info.begin() + (remove_index * m_blocks_per_piece)); + m_downloads[remove_index] = m_downloads[last_index]; + m_downloads[remove_index].info = &m_block_info[remove_index * m_blocks_per_piece]; + } + + m_downloads.pop_back(); + } #ifndef NDEBUG void piece_picker::check_invariant(const torrent* t) const { assert(sizeof(piece_pos) == 4); + assert(m_piece_info.empty() || m_piece_info[0].empty()); + if (t != 0) assert((int)m_piece_map.size() == t->torrent_file().num_pieces()); + for (int i = m_sequenced_download_threshold * 2 + 1; i < int(m_piece_info.size()); ++i) + assert(m_piece_info[i].empty()); + + for (std::vector::const_iterator i = m_downloads.begin() + , end(m_downloads.end()); i != end; ++i) + { + bool blocks_requested = false; + int num_blocks = blocks_in_piece(i->index); + int num_requested = 0; + int num_finished = 0; + for (int k = 0; k < num_blocks; ++k) + { + if (i->info[k].finished) + { + ++num_finished; + assert(i->info[k].requested); + ++num_requested; + continue; + } + if (i->info[k].requested) + { + ++num_requested; + blocks_requested = true; + } + } + assert(blocks_requested == (i->state != none)); + assert(num_requested == i->requested); + assert(num_finished == i->finished); + assert(num_finished <= num_requested); + } + + int num_filtered = 0; int num_have_filtered = 0; + int num_have = 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->filtered()) { if (i->index != piece_pos::we_have_index) ++num_filtered; else ++num_have_filtered; } + if (i->index == piece_pos::we_have_index) + ++num_have; + +#if 0 if (t != 0) { int actual_peer_count = 0; @@ -265,64 +330,61 @@ namespace libtorrent } */ } +#endif 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 (int i = 0; i < int(m_piece_info.size()); ++i) { - for (std::vector::const_iterator j= i->begin(); - j != i->end(); ++j) + for (int j = 0; j < int(m_piece_info[i].size()); ++j) { - assert(*j != index); + assert(m_piece_info[i][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) + else { 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()) + int prio = i->priority(m_sequenced_download_threshold); + if (prio > 0) { - assert(false); + const std::vector& vec = m_piece_info[prio]; + assert (i->index < vec.size()); + assert(vec[i->index] == index); } - assert(vec[i->index] == index); +/* + for (int k = 0; k < int(m_piece_info.size()); ++k) + { + for (int j = 0; j < int(m_piece_info[k].size()); ++j) + { + assert(int(m_piece_info[k][j]) != index + || (prio > 0 && prio == k && int(i->index) == j)); + } + } +*/ } - std::vector::const_iterator down - = std::find_if(m_downloads.begin(), - m_downloads.end(), - has_index(index)); + int count = std::count_if(m_downloads.begin(), m_downloads.end() + , has_index(index)); if (i->downloading == 1) { - assert(down != m_downloads.end()); + assert(count == 1); } else { - assert(down == m_downloads.end()); + assert(count == 0); } } + assert(num_have == m_num_have); assert(num_filtered == m_num_filtered); assert(num_have_filtered == m_num_have_filtered); } @@ -332,54 +394,57 @@ namespace libtorrent { const float num_pieces = static_cast(m_piece_map.size()); - for (int i = 0; i < (int)m_piece_info.size(); ++i) + int min_availability = piece_pos::max_peer_count; + // find the lowest availability count + // count the number of pieces that have that availability + // and also the number of pieces that have more than that. + int integer_part = 0; + int fraction_part = 0; + for (std::vector::const_iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) { - int p = (int)m_piece_info[i].size(); - assert(num_pieces == 0 || float(p) / num_pieces <= 1.f); - if (p > 0) + int peer_count = int(i->peer_count); + // take ourself into account + if (i->have()) ++peer_count; + if (min_availability > peer_count) { - float fraction_above_count = - 1.f - float(p) / num_pieces; - return i + fraction_above_count; + min_availability = i->peer_count; + fraction_part += integer_part; + integer_part = 1; + } + else if (peer_count == min_availability) + { + ++integer_part; + } + else + { + assert(peer_count > min_availability); + ++fraction_part; } } - 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; + assert(integer_part + fraction_part == num_pieces); + return float(min_availability) + (fraction_part / num_pieces); } void piece_picker::add(int index) { assert(index >= 0); - assert(index < (int)m_piece_map.size()); + 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); + assert(!p.filtered()); + assert(!p.have()); int priority = p.priority(m_sequenced_download_threshold); - if ((int)dst_vec.size() <= priority) - dst_vec.resize(priority + 1); + assert(priority > 0); + if (int(m_piece_info.size()) <= priority) + m_piece_info.resize(priority + 1); - assert((int)dst_vec.size() > priority); + assert(int(m_piece_info.size()) > priority); - if (p.ordered(m_sequenced_download_threshold)) + if (is_ordered(priority)) { // the piece should be inserted ordered, not randomly - std::vector& v = dst_vec[priority]; + std::vector& v = m_piece_info[priority]; // assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); std::vector::iterator i = std::lower_bound(v.begin(), v.end() , index/*, std::greater()*/); @@ -393,75 +458,65 @@ namespace libtorrent } // assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); } - else if (dst_vec[priority].size() < 2) + else if (m_piece_info[priority].size() < 2) { - p.index = dst_vec[priority].size(); - dst_vec[priority].push_back(index); + p.index = m_piece_info[priority].size(); + m_piece_info[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(); + int dst_index = rand() % m_piece_info[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]); + m_piece_map[m_piece_info[priority][dst_index]].index + = m_piece_info[priority].size(); + m_piece_info[priority].push_back(m_piece_info[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; + m_piece_info[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) + // will update the piece with the given properties (priority, elem_index) + // to place it at the correct position in the vectors. + void piece_picker::move(int priority, int elem_index) { - assert(!filtered); - assert(priority >= 0); + 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(m_files_checked_called); - assert((int)src_vec.size() > priority); - assert((int)src_vec[priority].size() > elem_index); + assert(int(m_piece_info.size()) > priority); + assert(int(m_piece_info[priority].size()) > elem_index); - int index = src_vec[priority][elem_index]; + int index = m_piece_info[priority][elem_index]; // update the piece_map piece_pos& p = m_piece_map[index]; + assert(int(p.index) == elem_index || p.have()); + int new_priority = p.priority(m_sequenced_download_threshold); - if (p.downloading == downloading - && p.filtered == filtered - && new_priority == priority) + if (new_priority == priority) return; + + if (int(m_piece_info.size()) <= new_priority + && new_priority > 0) { - assert(p.ordered(m_sequenced_download_threshold)); - return; + m_piece_info.resize(new_priority + 1); + assert(int(m_piece_info.size()) > new_priority); } - 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) + if (new_priority == 0) { - dst_vec.resize(new_priority + 1); - assert((int)dst_vec.size() > new_priority); + // this means the piece should not have an entry } - - if (p.ordered(m_sequenced_download_threshold)) + else if (is_ordered(new_priority)) { // the piece should be inserted ordered, not randomly - std::vector& v = dst_vec[new_priority]; + std::vector& v = m_piece_info[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()*/); @@ -475,35 +530,35 @@ namespace libtorrent } // assert(is_sorted(v.begin(), v.end()/*, std::greater()*/)); } - else if (dst_vec[new_priority].size() < 2) + else if (m_piece_info[new_priority].size() < 2) { - p.index = dst_vec[new_priority].size(); - dst_vec[new_priority].push_back(index); + p.index = m_piece_info[new_priority].size(); + m_piece_info[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(); + int dst_index = rand() % m_piece_info[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]); + m_piece_map[m_piece_info[new_priority][dst_index]].index + = m_piece_info[new_priority].size(); + m_piece_info[new_priority].push_back(m_piece_info[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; + m_piece_info[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); + assert(new_priority == 0 || p.index < m_piece_info[p.priority(m_sequenced_download_threshold)].size()); + assert(new_priority == 0 || m_piece_info[p.priority(m_sequenced_download_threshold)][p.index] == index); - if (priority >= m_sequenced_download_threshold) + if (is_ordered(priority)) { // remove the element from the source vector and preserve the order - std::vector& v = src_vec[priority]; + std::vector& v = m_piece_info[priority]; v.erase(v.begin() + elem_index); for (std::vector::iterator i = v.begin() + elem_index; i != v.end(); ++i) @@ -516,77 +571,25 @@ namespace libtorrent { // 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(); + int replace_index = m_piece_info[priority][elem_index] = m_piece_info[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); + assert(int(m_piece_info[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); + assert(int(m_piece_map[replace_index].index) == elem_index); + assert(m_piece_info[priority][elem_index] == replace_index); } else { - assert((int)src_vec[priority].size() == elem_index+1); + assert(int(m_piece_info[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); - assert(m_files_checked_called); - - 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(); + m_piece_info[priority].pop_back(); } } @@ -605,36 +608,240 @@ namespace libtorrent m_downloads.end(), has_index(index)); assert(i != m_downloads.end()); - m_downloads.erase(i); + erase_download_piece(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); + int prev_priority = p.priority(m_sequenced_download_threshold); + p.downloading = 0; + int new_priority = p.priority(m_sequenced_download_threshold); + + if (new_priority == prev_priority) return; + + if (prev_priority == 0) + { + add(index); + } + else + { + move(prev_priority, p.index); + } + } + + void piece_picker::inc_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + + // in general priority = availability * 2 + // see piece_block::priority() + + // this will insert two empty vectors at the start of the + // piece_info vector. It is done like this as an optimization, + // to swap vectors instead of copying them + while (m_piece_info.size() < 3 + || (!m_piece_info.rbegin()->empty()) + || (!(m_piece_info.rbegin()+1)->empty())) + { + m_piece_info.push_back(std::vector()); + } + assert(m_piece_info.rbegin()->empty()); + assert((m_piece_info.rbegin()+1)->empty()); + typedef std::vector > piece_info_t; + for (piece_info_t::reverse_iterator i = m_piece_info.rbegin(), j(i+1) + , k(j+1), end(m_piece_info.rend()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + assert(m_piece_info.begin()->empty()); + assert((m_piece_info.begin()+1)->empty()); + + // if we have some priorities that are clamped to the + // sequenced download, move that vector back down + int last_index = m_piece_info.size() - 1; + int cap_index = m_sequenced_download_threshold * 2; + if (last_index == cap_index) + { + // this is the case when the top bucket + // was moved up into the sequenced download bucket. + m_piece_info.push_back(std::vector()); + m_piece_info[cap_index].swap(m_piece_info[cap_index+1]); + ++last_index; + } + else if (last_index > cap_index) + { + if (last_index - cap_index == 1) + { + m_piece_info.push_back(std::vector()); + ++last_index; + } + m_piece_info[cap_index+1].swap(m_piece_info[cap_index+2]); + m_piece_info[cap_index].swap(m_piece_info[cap_index+1]); + } + + // now, increase the peer count of all the pieces. + // because of different priorities, some pieces may have + // ended up in the wrong priority bucket. Adjust that. + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + ++i->peer_count; + // if the assumption that the priority would + // increase by 2 when increasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + int new_prio = i->priority(m_sequenced_download_threshold); + assert(new_prio <= cap_index); + if (prev_prio == 0 && new_prio > 0) + { + add(i - m_piece_map.begin()); + continue; + } + if (new_prio == 0) + { + assert(prev_prio == 0); + continue; + } + if (prev_prio == cap_index) + { + assert(new_prio == cap_index); + continue; + } + if (new_prio == prev_prio + 2 && new_prio != cap_index) + { + assert(new_prio != cap_index); + continue; + } + if (prev_prio + 2 >= cap_index) + { + // these two vectors will have moved one extra step + // passed the sequenced download threshold + ++prev_prio; + } + assert(prev_prio + 2 != cap_index); + assert(prev_prio + 2 != new_prio); + move(prev_prio + 2, i->index); + } + } + + void piece_picker::dec_refcount_all() + { + TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(m_files_checked_called); + assert(m_piece_info.size() >= 2); + assert(m_piece_info.front().empty()); + // swap all vectors two steps down + if (m_piece_info.size() > 2) + { + typedef std::vector > piece_info_t; + for (piece_info_t::iterator i = m_piece_info.begin(), j(i+1) + , k(j+1), end(m_piece_info.end()); k != end; ++i, ++j, ++k) + { + k->swap(*i); + } + } + else + { + m_piece_info.resize(3); + } + int last_index = m_piece_info.size() - 1; + if ((m_piece_info.size() & 1) == 0) + { + // if there's an even number of vectors, swap + // the last two to get the same layout in both cases + m_piece_info[last_index].swap(m_piece_info[last_index-1]); + } + assert(m_piece_info.back().empty()); + int pushed_out_index = m_piece_info.size() - 2; + + int cap_index = m_sequenced_download_threshold * 2; + assert(m_piece_info[last_index].empty()); + if (last_index >= cap_index) + { + assert(pushed_out_index == cap_index - 1 + || m_piece_info[cap_index - 1].empty()); + m_piece_info[cap_index].swap(m_piece_info[cap_index - 2]); + if (cap_index == pushed_out_index) + pushed_out_index = cap_index - 2; + } + + // first is the vector that were + // bumped down to 0. The should always be moved + // since they have to be removed or reinserted + std::vector().swap(m_piece_info.front()); + + for (std::vector::iterator i = m_piece_map.begin() + , end(m_piece_map.end()); i != end; ++i) + { + int prev_prio = i->priority(m_sequenced_download_threshold); + assert(i->peer_count > 0); + --i->peer_count; + // if the assumption that the priority would + // decrease by 2 when decreasing the availability + // by one isn't true for this particular piece, correct it. + // that assumption is true for all pieces with priority 0 or 1 + if (prev_prio == 0) + { + assert(i->priority(m_sequenced_download_threshold) == 0); + continue; + } + + int new_prio = i->priority(m_sequenced_download_threshold); + if (prev_prio == cap_index) + { + if (new_prio == cap_index) continue; + prev_prio += 2; + } + else if (new_prio == prev_prio - 2) + { + continue; + } + else if (prev_prio == 2) + { + // if this piece was pushed down to priority 0, it was + // removed + assert(new_prio > 0); + add(i - m_piece_map.begin()); + continue; + } + else if (prev_prio == 1) + { + // if this piece was one of the vectors that was pushed to the + // top, adjust the prev_prio to point to that vector, so that + // the pieces are moved from there + prev_prio = pushed_out_index + 2; + } + move(prev_prio - 2, i->index); + } } void piece_picker::inc_refcount(int i) { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; +// TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(i >= 0); assert(i < (int)m_piece_map.size()); assert(m_files_checked_called); - 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]; + int index = p.index; + int prev_priority = p.priority(m_sequenced_download_threshold); + + assert(p.peer_count < piece_pos::max_peer_count); + p.peer_count++; + assert(p.peer_count != 0); // 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; + if (p.priority(m_sequenced_download_threshold) == prev_priority) return; - move(p.downloading, p.filtered, prev_priority, index); + if (prev_priority == 0) + { + add(i); + } + else + { + move(prev_priority, index); + } #ifndef NDEBUG // integrity_check(); @@ -644,25 +851,23 @@ namespace libtorrent void piece_picker::dec_refcount(int i) { - TORRENT_PIECE_PICKER_INVARIANT_CHECK; +// TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(m_files_checked_called); 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]; + int prev_priority = p.priority(m_sequenced_download_threshold); + int index = p.index; + assert(p.peer_count > 0); - if (index == piece_pos::we_have_index || p.filtered - || p.priority(m_sequenced_download_threshold) == prev_priority) return; + if (p.peer_count > 0) + p.peer_count--; - move(p.downloading, p.filtered, prev_priority, index); + if (p.priority(m_sequenced_download_threshold) == prev_priority) return; + + move(prev_priority, index); } // this is used to indicate that we succesfully have @@ -675,80 +880,106 @@ namespace libtorrent 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) + int info_index = p.index; + int priority = p.priority(m_sequenced_download_threshold); + + assert(p.downloading == 1); + assert(!p.have()); + + std::vector::iterator i + = std::find_if(m_downloads.begin() + , m_downloads.end() + , has_index(index)); + assert(i != m_downloads.end()); + erase_download_piece(i); + p.downloading = 0; + + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(index)) == m_downloads.end()); + + if (p.have()) return; + if (p.filtered()) { --m_num_filtered; ++m_num_have_filtered; } - if (info_index == piece_pos::we_have_index) return; - remove(p.downloading, p.filtered, priority, info_index); - p.index = piece_pos::we_have_index; + ++m_num_have; + p.set_have(); + if (priority == 0) return; + assert(p.priority(m_sequenced_download_threshold) == 0); + move(priority, info_index); } - void piece_picker::mark_as_filtered(int index) + void piece_picker::set_piece_priority(int index, int new_piece_priority) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; + assert(new_piece_priority >= 0); + assert(new_piece_priority <= 7); 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) + // if the priority isn't changed, don't do anything + if (new_piece_priority == int(p.piece_priority)) return; + + int prev_priority = p.priority(m_sequenced_download_threshold); + + if (new_piece_priority == piece_pos::filter_priority + && p.piece_priority != piece_pos::filter_priority) + { + // the piece just got filtered + if (p.have()) ++m_num_have_filtered; + else ++m_num_filtered; + } + else if (new_piece_priority != piece_pos::filter_priority + && p.piece_priority == piece_pos::filter_priority) + { + // the piece just got unfiltered + if (p.have()) --m_num_have_filtered; + else --m_num_filtered; + } + assert(m_num_filtered >= 0); + assert(m_num_have_filtered >= 0); + + p.piece_priority = new_piece_priority; + int new_priority = p.priority(m_sequenced_download_threshold); + + if (new_priority == prev_priority) return; + + if (prev_priority == 0) { - --m_num_filtered; - assert(m_num_filtered >= 0); add(index); } else { - --m_num_have_filtered; - assert(m_num_have_filtered >= 0); + move(prev_priority, p.index); } } - bool piece_picker::is_filtered(int index) const + int piece_picker::piece_priority(int index) const { assert(index >= 0); assert(index < (int)m_piece_map.size()); - return m_piece_map[index].filtered == 1; + return m_piece_map[index].piece_priority; } + void piece_picker::piece_priorities(std::vector& pieces) const + { + pieces.resize(m_piece_map.size()); + std::vector::iterator j = pieces.begin(); + for (std::vector::const_iterator i = m_piece_map.begin(), + end(m_piece_map.end()); i != end; ++i, ++j) + { + *j = i->piece_priority; + } + } + + // ============ start deprecation ============== + void piece_picker::filtered_pieces(std::vector& mask) const { mask.resize(m_piece_map.size()); @@ -756,95 +987,79 @@ namespace libtorrent for (std::vector::const_iterator i = m_piece_map.begin(), end(m_piece_map.end()); i != end; ++i, ++j) { - *j = i->filtered == 1; + *j = i->filtered(); } } - + + // ============ end deprecation ============== + + // pieces describes which pieces the peer we're requesting from + // has. + // interesting_blocks is an out parameter, and will be filled + // with (up to) num_blocks of interesting blocks that the peer has. + // prefer_whole_pieces can be set if this peer should download + // whole pieces rather than trying to download blocks from the + // same piece as other peers. + // the endpoint is the address of the peer we're picking pieces + // from. This is used when downloading whole pieces, to only + // pick from the same piece the same peer is downloading from. + // state is supposed to be set to fast if the peer is downloading + // relatively fast, by some notion. Slow peers will prefer not + // to pick blocks from the same pieces as fast peers, and vice + // versa. Downloading pieces are marked as being fast, medium + // or slow once they're started. void piece_picker::pick_pieces(const std::vector& pieces , std::vector& interesting_blocks , int num_blocks, bool prefer_whole_pieces - , tcp::endpoint peer) const + , tcp::endpoint peer, piece_state_t speed) const { TORRENT_PIECE_PICKER_INVARIANT_CHECK; assert(num_blocks > 0); assert(pieces.size() == m_piece_map.size()); assert(m_files_checked_called); - // 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; + // this will be filled with blocks that we should not request + // unless we can't find num_blocks among the other ones. + // blocks that belong to pieces with a mismatching speed + // category for instance, or if we prefer whole pieces, + // blocks belonging to a piece that others have + // downloaded to std::vector backup_blocks; - // this loop will loop from pieces with 1 peer and up + // this loop will loop from pieces with priority 1 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())) + // +1 is to ignore pieces that no peer has. The bucket with index 0 contains + // pieces that 0 other peers have. bucket will point to a bucket with + // pieces with the same priority. It will be iterated in priority + // order (high priority/rare pices first). The content of each + // bucket is randomized + for (std::vector >::const_iterator bucket + = m_piece_info.begin() + 1; bucket != m_piece_info.end(); + ++bucket) { - 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 (bucket->empty()) continue; + num_blocks = add_interesting_blocks(*bucket, pieces + , interesting_blocks, backup_blocks, num_blocks + , prefer_whole_pieces, peer, speed); + assert(num_blocks >= 0); + if (num_blocks == 0) return; } - 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())); + if (!backup_blocks.empty()) + interesting_blocks.insert(interesting_blocks.end() + , backup_blocks.begin(), backup_blocks.begin() + + (std::min)(num_blocks, (int)backup_blocks.size())); } namespace @@ -854,10 +1069,11 @@ namespace libtorrent { 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()) + piece_picker::block_info const& info = p.info[j]; + if ((info.finished == 1 + || info.requested == 1) + && info.peer != peer + && info.peer != tcp::endpoint()) { return false; } @@ -866,112 +1082,118 @@ namespace libtorrent } } - int piece_picker::add_interesting_blocks_free(std::vector const& piece_list + int piece_picker::add_interesting_blocks(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 + , tcp::endpoint peer, piece_state_t speed) 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; + + // if we have less than 1% of the pieces, ignore speed priorities and just try + // to finish any downloading piece + bool ignore_speed_categories = (m_num_have * 100 / m_piece_map.size()) < 1; - 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 (m_piece_map[*i].downloading == 1) { - if ((int)backup_blocks.size() >= num_blocks) continue; + std::vector::const_iterator p + = std::find_if(m_downloads.begin(), m_downloads.end(), has_index(*i)); + assert(p != m_downloads.end()); + + // is true if all the other pieces that are currently + // requested from this piece are from the same + // peer as 'peer'. + bool only_same_peer = exclusively_requested_from(*p + , num_blocks_in_piece, peer); + + // 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 && !only_same_peer) + { + if (int(backup_blocks.size()) >= num_blocks) continue; + for (int j = 0; j < num_blocks_in_piece; ++j) + { + block_info const& info = p->info[j]; + if (info.finished) continue; + if (info.requested + && info.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; - backup_blocks.push_back(piece_block(*i, j)); + // ignore completed blocks + block_info const& info = p->info[j]; + if (info.finished) continue; + // ignore blocks requested from this peer already + if (info.requested && info.peer == peer) continue; + // if the piece is fast and the peer is slow, or vice versa, + // add the block as a backup. + // override this behavior if all the other blocks + // have been requested from the same peer or + // if the state of the piece is none (the + // piece will in that case change state). + if (p->state != none && p->state != speed + && !only_same_peer + && !ignore_speed_categories) + { + if (int(backup_blocks.size()) >= num_blocks) continue; + backup_blocks.push_back(piece_block(*i, j)); + 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->info[j].requested == 0) + { + // we have found a block that's free to download + num_blocks--; + // if we prefer whole pieces, continue picking from this + // piece even though we have num_blocks + if (prefer_whole_pieces) continue; + assert(num_blocks >= 0); + if (num_blocks == 0) return num_blocks; + } } - continue; + assert(num_blocks >= 0 || prefer_whole_pieces); + if (num_blocks < 0) num_blocks = 0; } - - for (int j = 0; j < num_blocks_in_piece; ++j) + else { - 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) + if (!prefer_whole_pieces && num_blocks_in_piece > num_blocks) + num_blocks_in_piece = num_blocks; + for (int j = 0; j < num_blocks_in_piece; ++j) { - // 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; + interesting_blocks.push_back(piece_block(*i, j)); } + num_blocks -= (std::min)(num_blocks_in_piece, num_blocks); } - assert(num_blocks >= 0 || prefer_whole_pieces); - if (num_blocks < 0) num_blocks = 0; + assert(num_blocks >= 0); if (num_blocks == 0) return num_blocks; } return num_blocks; @@ -984,18 +1206,18 @@ namespace libtorrent if (m_piece_map[index].downloading == 0) { - assert(std::find_if(m_downloads.begin(), m_downloads.end(), has_index(index)) - == m_downloads.end()); + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(index)) == m_downloads.end()); 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); + assert((int)i->finished <= m_blocks_per_piece); int max_blocks = blocks_in_piece(index); - if ((int)i->finished_blocks.count() < max_blocks) return false; + if ((int)i->finished < max_blocks) return false; - assert((int)i->requested_blocks.count() == max_blocks); + assert((int)i->requested == max_blocks); return true; } @@ -1004,7 +1226,6 @@ namespace libtorrent 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 @@ -1014,7 +1235,7 @@ namespace libtorrent , has_index(block.piece_index)); assert(i != m_downloads.end()); - return i->requested_blocks[block.block_index]; + return i->info[block.block_index].requested; } bool piece_picker::is_finished(piece_block block) const @@ -1022,18 +1243,18 @@ namespace libtorrent 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]; + return i->info[block.block_index].finished; } - void piece_picker::mark_as_downloading(piece_block block, const tcp::endpoint& peer) + void piece_picker::mark_as_downloading(piece_block block + , const tcp::endpoint& peer, piece_state_t state) { TORRENT_PIECE_PICKER_INVARIANT_CHECK; @@ -1045,23 +1266,29 @@ namespace libtorrent piece_pos& p = m_piece_map[block.piece_index]; if (p.downloading == 0) { + int prio = p.priority(m_sequenced_download_threshold); p.downloading = 1; - move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + move(prio, p.index); - downloading_piece dp; + downloading_piece& dp = add_download_piece(); + dp.state = state; dp.index = block.piece_index; - dp.requested_blocks[block.block_index] = 1; - dp.info[block.block_index].peer = peer; - m_downloads.push_back(dp); + block_info& info = dp.info[block.block_index]; + info.requested = 1; + info.peer = peer; + ++dp.requested; } 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; + block_info& info = i->info[block.block_index]; + assert(info.requested == 0); + info.peer = peer; + info.requested = 1; + ++i->requested; + if (i->state == none) i->state = state; } } @@ -1075,59 +1302,45 @@ namespace libtorrent 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; + int prio = p.priority(m_sequenced_download_threshold); if (p.downloading == 0) { p.downloading = 1; - move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + if (prio > 0) move(prio, p.index); + else assert(p.priority(m_sequenced_download_threshold) == 0); - downloading_piece dp; + downloading_piece& dp = add_download_piece(); + dp.state = none; dp.index = block.piece_index; - dp.requested_blocks[block.block_index] = 1; - dp.finished_blocks[block.block_index] = 1; + block_info& info = dp.info[block.block_index]; + info.requested = 1; + info.finished = 1; + ++dp.requested; + ++dp.finished; 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; + block_info& info = i->info[block.block_index]; + info.peer = peer; + if (!info.requested) ++i->requested; + info.requested = 1; + if (!info.finished) ++i->finished; + info.finished = 1; + + if (i->requested == i->finished) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; + } } } -/* - 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()); @@ -1152,11 +1365,10 @@ namespace libtorrent 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) + if (i->info[block.block_index].requested == false + || i->info[block.block_index].requested == true) return boost::optional(); return boost::optional(i->info[block.block_index].peer); @@ -1173,38 +1385,49 @@ namespace libtorrent 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()); + 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)); + 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; + if (i->info[block.block_index].finished) + { + assert(i->info[block.block_index].requested); + return; + } assert(block.block_index < blocks_in_piece(block.piece_index)); -#ifndef NDEBUG - if (i->requested_blocks[block.block_index] == false) - { - assert(false); - } -#endif + assert(i->info[block.block_index].requested); // clear this block as being downloaded - i->requested_blocks[block.block_index] = false; + i->info[block.block_index].requested = false; + --i->requested; // clear the downloader of this block i->info[block.block_index].peer = tcp::endpoint(); // if there are no other blocks in this piece // that's being downloaded, remove it from the list - if (i->requested_blocks.count() == 0) + if (i->requested == 0) { - m_downloads.erase(i); - m_piece_map[block.piece_index].downloading = 0; + erase_download_piece(i); piece_pos& p = m_piece_map[block.piece_index]; - move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index); + int prio = p.priority(m_sequenced_download_threshold); + p.downloading = 0; + if (prio > 0) move(prio, p.index); + + assert(std::find_if(m_downloads.begin(), m_downloads.end() + , has_index(block.piece_index)) == m_downloads.end()); + } + else if (i->requested == i->finished) + { + // there are no blocks requested in this piece. + // remove the fast/slow state from it + i->state = none; } } @@ -1214,7 +1437,7 @@ namespace libtorrent for (std::vector::const_iterator i = m_downloads.begin(); i != m_downloads.end(); ++i) { - counter += (int)i->finished_blocks.count(); + counter += (int)i->finished; } return counter; } diff --git a/libtorrent/src/policy.cpp b/libtorrent/src/policy.cpp index 785e21fad..bba04f429 100644 --- a/libtorrent/src/policy.cpp +++ b/libtorrent/src/policy.cpp @@ -30,27 +30,29 @@ POSSIBILITY OF SUCH DAMAGE. */ -#include +#include "libtorrent/pch.hpp" -#include "libtorrent/peer_connection.hpp" +#include #ifdef _MSC_VER #pragma warning(push, 1) #endif -#include #include +#include #ifdef _MSC_VER #pragma warning(pop) #endif +#include "libtorrent/peer_connection.hpp" #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/time.hpp" #include "libtorrent/aux_/session_impl.hpp" namespace libtorrent @@ -58,7 +60,6 @@ namespace libtorrent class peer_connection; } -using namespace boost::posix_time; using boost::bind; namespace @@ -185,7 +186,9 @@ namespace libtorrent std::vector interesting_pieces; interesting_pieces.reserve(100); - bool prefer_whole_pieces = c.prefer_whole_pieces(); + bool prefer_whole_pieces = c.prefer_whole_pieces() + || c.on_parole(); + if (!prefer_whole_pieces) { prefer_whole_pieces = c.statistics().download_payload_rate() @@ -197,8 +200,13 @@ namespace libtorrent // 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. - assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) - || c.proxy() == c.get_socket()->remote_endpoint()); + assert(c.remote() == c.get_socket()->remote_endpoint()); + + piece_picker::piece_state_t state; + peer_connection::peer_speed_t speed = c.peer_speed(); + if (speed == peer_connection::fast) state = piece_picker::fast; + else if (speed == peer_connection::medium) state = piece_picker::medium; + else state = piece_picker::slow; // picks the interesting pieces from this peer // the integer is the number of pieces that @@ -209,7 +217,7 @@ namespace libtorrent // for this peer. If we're downloading one piece in 20 seconds // then use this mode. p.pick_pieces(c.get_bitfield(), interesting_pieces - , num_requests, prefer_whole_pieces, c.remote()); + , num_requests, prefer_whole_pieces, c.remote(), state); // this vector is filled with the interesting pieces // that some other peer is currently downloading @@ -375,16 +383,16 @@ namespace libtorrent : m_torrent(t) , m_num_unchoked(0) , m_available_free_upload(0) - , m_last_optimistic_disconnect(boost::gregorian::date(1970,boost::gregorian::Jan,1)) + , m_last_optimistic_disconnect(min_time()) { 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() + policy::iterator policy::find_choke_candidate() { INVARIANT_CHECK; - peer* worst_peer = 0; + iterator worst_peer = m_peers.end(); size_type min_weight = std::numeric_limits::min(); #ifndef NDEBUG @@ -393,7 +401,7 @@ namespace libtorrent // TODO: make this selection better - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer_connection* c = i->connection; @@ -406,7 +414,7 @@ namespace libtorrent if (c->is_disconnecting()) continue; // if the peer isn't interested, just choke it if (!c->is_peer_interested()) - return &(*i); + return i; size_type diff = i->total_download() - i->total_upload(); @@ -415,35 +423,32 @@ namespace libtorrent + diff + ((c->is_interesting() && c->has_peer_choked())?-10:10)*1024; - if (weight >= min_weight && worst_peer) continue; + if (weight >= min_weight && worst_peer != m_peers.end()) continue; min_weight = weight; - worst_peer = &(*i); + worst_peer = i; continue; } assert(unchoked_counter == 0); return worst_peer; } - policy::peer* policy::find_unchoke_candidate() + policy::iterator 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; + return m_peers.end(); - using namespace boost::posix_time; - using namespace boost::gregorian; - - peer* unchoke_peer = 0; - ptime min_time(date(9999,Jan,1)); + iterator unchoke_peer = m_peers.end(); + ptime min_time = libtorrent::min_time(); float max_down_speed = 0.f; // TODO: make this selection better - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer_connection* c = i->connection; @@ -457,81 +462,93 @@ namespace libtorrent min_time = i->last_optimistically_unchoked; max_down_speed = c->statistics().download_rate(); - unchoke_peer = &(*i); + unchoke_peer = i; } return unchoke_peer; } - policy::peer* policy::find_disconnect_candidate() + policy::iterator policy::find_disconnect_candidate() { - peer *disconnect_peer = 0; + INVARIANT_CHECK; + + iterator disconnect_peer = m_peers.end(); double slowest_transfer_rate = std::numeric_limits::max(); - boost::posix_time::ptime local_time - = second_clock::universal_time(); + ptime now = time_now(); - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer_connection* c = i->connection; - if(c == 0) - continue; - if(c->is_disconnecting()) + if (c == 0) continue; + if (c->is_disconnecting()) continue; + + // never disconnect an interesting peer if we have a candidate that + // isn't interesting + if (disconnect_peer != m_peers.end() + && c->is_interesting() + && !disconnect_peer->connection->is_interesting()) continue; double transferred_amount = (double)c->statistics().total_payload_download(); - boost::posix_time::time_duration connected_time - = local_time - i->connected; + time_duration connected_time = now - i->connected; - double connected_time_in_seconds - = connected_time.seconds() - + connected_time.minutes()*60.0 - + connected_time.hours()*60.0*60.0; + double connected_time_in_seconds = total_seconds(connected_time); double transfer_rate - = transferred_amount / (connected_time_in_seconds+1); + = transferred_amount / (connected_time_in_seconds + 1); - if (transfer_rate <= slowest_transfer_rate) + // prefer to disconnect uninteresting peers, and secondly slow peers + if (transfer_rate <= slowest_transfer_rate + || (disconnect_peer != m_peers.end() + && disconnect_peer->connection->is_interesting() + && !c->is_interesting())) { slowest_transfer_rate = transfer_rate; - disconnect_peer = &(*i); + disconnect_peer = i; } } return disconnect_peer; } - policy::peer *policy::find_connect_candidate() + policy::iterator 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; + INVARIANT_CHECK; - for (std::vector::iterator i = m_peers.begin(); - i != m_peers.end(); ++i) + ptime now = time_now(); + ptime min_connect_time(now); + iterator candidate = m_peers.end(); + + int max_failcount = m_torrent->settings().max_failcount; + int min_reconnect_time = m_torrent->settings().min_reconnect_time; + + for (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; + if (i->connection) continue; + if (i->banned) continue; + if (i->type == peer::not_connectable) continue; + if (i->seed && m_torrent->is_seed()) continue; + if (i->failcount >= max_failcount) continue; + if (now - i->connected < seconds(i->failcount * min_reconnect_time)) + continue; - assert(i->connected <= local_time); + assert(i->connected <= now); - boost::posix_time::ptime next_connect = i->connected; - - if (next_connect <= ptime) + if (i->connected <= min_connect_time) { - ptime = next_connect; - candidate = &(*i); + min_connect_time = i->connected; + candidate = i; } } - assert(ptime <= local_time); + assert(min_connect_time <= now); return candidate; } - policy::peer* policy::find_seed_choke_candidate() + policy::iterator policy::find_seed_choke_candidate() { INVARIANT_CHECK; @@ -539,26 +556,23 @@ namespace libtorrent // 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; + iterator candidate = m_peers.end(); // not valid when candidate == 0 - ptime last_unchoke = ptime(date(1970, Jan, 1)); + ptime last_unchoke = min_time(); // 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; + iterator second_candidate = m_peers.end(); size_type lowest_share_diff = 0; // not valid when secondCandidate==0 - for (std::vector::iterator i = m_peers.begin(); + for (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 + // ignore peers that are choked or + // whose connection is closed if (c == 0) continue; if (c->is_choked()) continue; @@ -568,36 +582,36 @@ namespace libtorrent // select as second candidate the one that we owe the least // to - if (!second_candidate || share_diff <= lowest_share_diff) + if (second_candidate == m_peers.end() + || share_diff <= lowest_share_diff) { lowest_share_diff = share_diff; - second_candidate = &(*i); + 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) + if (candidate == m_peers.end() + || last_unchoke > i->last_optimistically_unchoked) { last_unchoke = i->last_optimistically_unchoked; - candidate = &(*i); + candidate = i; } } - if (candidate) return candidate; - if (second_candidate) return second_candidate; - assert(false); - return 0; + if (candidate != m_peers.end()) return candidate; + assert(second_candidate != m_peers.end()); + return second_candidate; } - policy::peer* policy::find_seed_unchoke_candidate() + policy::iterator policy::find_seed_unchoke_candidate() { INVARIANT_CHECK; - peer* candidate = 0; - boost::posix_time::ptime last_unchoke - = second_clock::universal_time(); + iterator candidate = m_peers.end(); + ptime last_unchoke = time_now(); - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer_connection* c = i->connection; @@ -607,7 +621,7 @@ namespace libtorrent if (c->is_disconnecting()) continue; if (last_unchoke < i->last_optimistically_unchoked) continue; last_unchoke = i->last_optimistically_unchoked; - candidate = &(*i); + candidate = i; } return candidate; } @@ -616,24 +630,23 @@ namespace libtorrent { INVARIANT_CHECK; - peer* p = find_seed_unchoke_candidate(); - if (p != 0) + iterator p = find_seed_unchoke_candidate(); + if (p != m_peers.end()) { assert(p->connection->is_choked()); p->connection->send_unchoke(); - p->last_optimistically_unchoked - = second_clock::universal_time(); + p->last_optimistically_unchoked = time_now(); ++m_num_unchoked; } - return p != 0; + return p != m_peers.end(); } void policy::seed_choke_one_peer() { INVARIANT_CHECK; - peer* p = find_seed_choke_candidate(); - if (p != 0) + iterator p = find_seed_choke_candidate(); + if (p != m_peers.end()) { assert(!p->connection->is_choked()); p->connection->send_choke(); @@ -647,14 +660,22 @@ namespace libtorrent if (m_torrent->is_paused()) return; - using namespace boost::posix_time; - + ptime now = time_now(); // 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()); + for (iterator i = m_peers.begin(); i != m_peers.end();) + { + // this timeout has to be customizable! + if (i->connection == 0 + && i->connected != min_time() + && now - i->connected > minutes(120)) + { + m_peers.erase(i++); + } + else + { + ++i; + } + } // ------------------------------------- // maintain the number of connections @@ -664,8 +685,8 @@ namespace libtorrent // 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) + for (iterator i = m_peers.begin(); + i != m_peers.end(); ++i) { if (i->connection && !i->connection->is_disconnecting()) ++num_connected_peers; @@ -680,8 +701,9 @@ namespace libtorrent { // 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) + ptime local_time = time_now(); + if (m_last_optimistic_disconnect + seconds(120) <= local_time + && find_connect_candidate() != m_peers.end()) { m_last_optimistic_disconnect = local_time; --max_connections; // this will have the effect of disconnecting the worst peer @@ -690,7 +712,7 @@ namespace libtorrent else { // don't do a disconnect earlier than 1 minute after some peer was connected - m_last_optimistic_disconnect = second_clock::universal_time(); + m_last_optimistic_disconnect = time_now(); } while (num_connected_peers > max_connections) @@ -702,13 +724,6 @@ namespace libtorrent } } - while (m_torrent->num_peers() < m_torrent->m_connections_quota.given) - { - if (!connect_one_peer()) - break; - } - - // ------------------------ // upload shift // ------------------------ @@ -749,10 +764,10 @@ namespace libtorrent { do { - peer* p = find_seed_choke_candidate(); + iterator p = find_seed_choke_candidate(); --m_num_unchoked; - assert(p != 0); - if (p == 0) break; + assert(p != m_peers.end()); + if (p == m_peers.end()) break; assert(!p->connection->is_choked()); p->connection->send_choke(); @@ -765,8 +780,8 @@ namespace libtorrent // 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) + iterator p = find_seed_unchoke_candidate(); + if (p != m_peers.end()) { assert(p->connection->is_choked()); seed_choke_one_peer(); @@ -795,7 +810,7 @@ namespace libtorrent if (m_torrent->ratio() != 0) { // choke peers that have leeched too much without giving anything back - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { peer_connection* c = i->connection; @@ -823,9 +838,9 @@ namespace libtorrent { do { - peer* p = find_choke_candidate(); - if (!p) break; - assert(p); + iterator p = find_choke_candidate(); + if (p == m_peers.end()) break; + assert(p != m_peers.end()); assert(!p->connection->is_choked()); p->connection->send_choke(); --m_num_unchoked; @@ -838,8 +853,8 @@ namespace libtorrent // 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) + iterator p = find_unchoke_candidate(); + if (p != m_peers.end()) { assert(p->connection->is_choked()); choke_one_peer(); @@ -856,30 +871,6 @@ namespace libtorrent } } - 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()); @@ -892,8 +883,7 @@ namespace libtorrent // TODO: only allow _one_ connection to use this // override at a time - assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) - || c.proxy() == c.get_socket()->remote_endpoint()); + assert(c.remote() == c.get_socket()->remote_endpoint()); if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given && c.remote().address() != m_torrent->current_tracker().address()) @@ -908,7 +898,7 @@ namespace libtorrent } #endif - std::vector::iterator i; + iterator i; if (m_torrent->settings().allow_multiple_connections_per_ip) { @@ -944,46 +934,43 @@ namespace libtorrent "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 - assert((c.proxy() == tcp::endpoint() && c.remote() == c.get_socket()->remote_endpoint()) - || c.proxy() == c.get_socket()->remote_endpoint()); + assert(c.remote() == c.get_socket()->remote_endpoint()); - peer p(c.remote(), peer::not_connectable); + peer p(c.remote(), peer::not_connectable, 0); m_peers.push_back(p); - i = m_peers.end()-1; + i = boost::prior(m_peers.end()); } + c.set_peer_info(&*i); 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(); + i->connected = time_now(); + m_last_optimistic_disconnect = time_now(); } - void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid) + void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid + , int src, char flags) { INVARIANT_CHECK; - // just ignore the obviously invalid entries from the tracker + // just ignore the obviously invalid entries if(remote.address() == address() || remote.port() == 0) return; try { - std::vector::iterator i; + iterator i; if (m_torrent->settings().allow_multiple_connections_per_ip) { @@ -997,21 +984,38 @@ namespace libtorrent , match_peer_ip(remote)); } - bool just_added = false; - if (i == m_peers.end()) { - using namespace boost::posix_time; - using namespace boost::gregorian; + aux::session_impl& ses = m_torrent->session(); + // if the IP is blocked, don't add it + if (ses.m_ip_filter.access(remote.address()) & ip_filter::blocked) + { + if (ses.m_alerts.should_post(alert::info)) + { + ses.m_alerts.post_alert(peer_blocked_alert(remote.address() + , "blocked peer not added to peer list")); + } + return; + } + // we don't have any info about this peer. // add a new entry - peer p(remote, peer::connectable); + peer p(remote, peer::connectable, src); m_peers.push_back(p); // the iterator is invalid // because of the push_back() - i = m_peers.end() - 1; - just_added = true; + i = boost::prior(m_peers.end()); + if (flags & 0x02) p.seed = true; + + // try to send a DHT ping to this peer + // as well, to figure out if it supports + // DHT (uTorrent and BitComet doesn't + // advertise support) +#ifndef TORRENT_DISABLE_DHT + udp::endpoint node(remote.address(), remote.port()); + m_torrent->session().add_dht_node(node); +#endif } else { @@ -1021,6 +1025,16 @@ namespace libtorrent // not known, so save it. Client may also have changed port // for some reason. i->ip = remote; + i->source |= src; + + // if this peer has failed before, decrease the + // counter to allow it another try, since somebody + // else is appearantly able to connect to it + // if it comes from the DHT it might be stale though + if (i->failcount > 0 && src != peer_info::dht) + --i->failcount; + + if (flags & 0x02) i->seed = true; if (i->connection) { @@ -1038,22 +1052,6 @@ namespace libtorrent 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) { @@ -1081,7 +1079,7 @@ namespace libtorrent if (successfully_verified) { // have all peers update their interested-flag - for (std::vector::iterator i = m_peers.begin(); + for (iterator i = m_peers.begin(); i != m_peers.end(); ++i) { if (i->connection == 0) continue; @@ -1089,21 +1087,7 @@ namespace libtorrent 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); + i->connection->update_interest(); } } } @@ -1192,22 +1176,26 @@ namespace libtorrent bool policy::unchoke_one_peer() { - peer* p = find_unchoke_candidate(); - if (p == 0) return false; + INVARIANT_CHECK; + + iterator p = find_unchoke_candidate(); + if (p == m_peers.end()) 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(); + p->last_optimistically_unchoked = time_now(); ++m_num_unchoked; return true; } void policy::choke_one_peer() { - peer* p = find_choke_candidate(); - if (p == 0) return; + INVARIANT_CHECK; + + iterator p = find_choke_candidate(); + if (p == m_peers.end()) return; assert(p->connection); assert(!p->connection->is_disconnecting()); assert(!p->connection->is_choked()); @@ -1217,42 +1205,42 @@ namespace libtorrent 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; + INVARIANT_CHECK; + + assert(m_torrent->want_more_peers()); + + iterator p = find_connect_candidate(); + if (p == m_peers.end()) 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->connected = m_last_optimistic_disconnect = time_now(); + p->connection = m_torrent->connect_to_peer(&*p); + if (p->connection == 0) return false; 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; + { +#if defined(TORRENT_VERBOSE_LOGGING) + (*m_torrent->session().m_logger) << "*** CONNECTION FAILED '" + << e.what() << "'\n"; +#endif + ++p->failcount; + return false; + } } bool policy::disconnect_one_peer() { - peer *p = find_disconnect_candidate(); - if(!p) + iterator p = find_disconnect_candidate(); + if (p == m_peers.end()) return false; #if defined(TORRENT_VERBOSE_LOGGING) (*p->connection->m_logger) << "*** CLOSING CONNECTION 'too many connections'\n"; @@ -1270,7 +1258,7 @@ namespace libtorrent // assert(c.is_disconnecting()); bool unchoked = false; - std::vector::iterator i = std::find_if( + iterator i = std::find_if( m_peers.begin() , m_peers.end() , match_peer_connection(c)); @@ -1278,17 +1266,18 @@ namespace libtorrent // if we couldn't find the connection in our list, just ignore it. if (i == m_peers.end()) return; assert(i->connection == &c); + i->connection = 0; - i->connected = second_clock::universal_time(); - if (!i->connection->is_choked() && !m_torrent->is_aborted()) + i->connected = time_now(); + if (!c.is_choked() && !m_torrent->is_aborted()) { unchoked = true; } if (c.failed()) { - i->type = peer::not_connectable; - i->ip.port(0); + ++i->failcount; + i->connected = time_now(); } // if the share ratio is 0 (infinite), the @@ -1296,13 +1285,12 @@ namespace libtorrent // 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(); + assert(c.associated_torrent().lock().get() == m_torrent); + assert(c.share_diff() < std::numeric_limits::max()); + m_available_free_upload += c.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) { @@ -1334,9 +1322,10 @@ namespace libtorrent #ifndef NDEBUG bool policy::has_connection(const peer_connection* c) { + INVARIANT_CHECK; + assert(c); - assert((c->proxy() == tcp::endpoint() && c->remote() == c->get_socket()->remote_endpoint()) - || c->proxy() == c->get_socket()->remote_endpoint()); + assert(c->remote() == c->get_socket()->remote_endpoint()); return std::find_if( m_peers.begin() @@ -1353,12 +1342,17 @@ namespace libtorrent int total_connections = 0; int nonempty_connections = 0; - - for (std::vector::const_iterator i = m_peers.begin(); + std::set
unique_test; + for (const_iterator i = m_peers.begin(); i != m_peers.end(); ++i) { + if (!m_torrent->settings().allow_multiple_connections_per_ip) + assert(unique_test.find(i->ip.address()) == unique_test.end()); + unique_test.insert(i->ip.address()); ++total_connections; if (!i->connection) continue; + assert(i->connection->peer_info_struct() == 0 + || i->connection->peer_info_struct() == &*i); ++nonempty_connections; if (!i->connection->is_disconnecting()) ++connected_peers; @@ -1386,30 +1380,30 @@ namespace libtorrent // 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) + policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t, int src) : ip(ip_) , type(t) - , last_optimistically_unchoked( - boost::gregorian::date(1970,boost::gregorian::Jan,1)) - , connected(boost::gregorian::date(1970,boost::gregorian::Jan,1)) + , failcount(0) + , seed(false) + , last_optimistically_unchoked(min_time()) + , connected(min_time()) , prev_amount_upload(0) , prev_amount_download(0) , banned(false) + , source(src) , connection(0) { - assert(connected < second_clock::universal_time()); + assert(connected < time_now()); } size_type policy::peer::total_download() const diff --git a/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index 4d906a419..229f251c6 100644 --- a/libtorrent/src/session.cpp +++ b/libtorrent/src/session.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -73,7 +75,6 @@ POSSIBILITY OF SUCH DAMAGE. #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; @@ -87,9 +88,11 @@ namespace libtorrent { filesystem_init::filesystem_init() { +#if BOOST_VERSION < 103400 using namespace boost::filesystem; if (path::default_name_check_writable()) path::default_name_check(no_check); +#endif } } @@ -167,10 +170,11 @@ namespace libtorrent , boost::filesystem::path const& save_path , entry const& resume_data , bool compact_mode - , int block_size) + , int block_size + , storage_constructor_type sc) { return m_impl->add_torrent(ti, save_path, resume_data - , compact_mode, block_size); + , compact_mode, block_size, sc); } torrent_handle session::add_torrent( @@ -180,10 +184,11 @@ namespace libtorrent , boost::filesystem::path const& save_path , entry const& e , bool compact_mode - , int block_size) + , int block_size + , storage_constructor_type sc) { return m_impl->add_torrent(tracker_url, info_hash, name, save_path, e - , compact_mode, block_size); + , compact_mode, block_size, sc); } void session::remove_torrent(const torrent_handle& h) @@ -257,6 +262,49 @@ namespace libtorrent return m_impl->settings(); } + void session::set_peer_proxy(proxy_settings const& s) + { + m_impl->set_peer_proxy(s); + } + + void session::set_web_seed_proxy(proxy_settings const& s) + { + m_impl->set_web_seed_proxy(s); + } + + void session::set_tracker_proxy(proxy_settings const& s) + { + m_impl->set_tracker_proxy(s); + } + + proxy_settings const& session::peer_proxy() const + { + return m_impl->peer_proxy(); + } + + proxy_settings const& session::web_seed_proxy() const + { + return m_impl->web_seed_proxy(); + } + + proxy_settings const& session::tracker_proxy() const + { + return m_impl->tracker_proxy(); + } + + +#ifndef TORRENT_DISABLE_DHT + void session::set_dht_proxy(proxy_settings const& s) + { + m_impl->set_dht_proxy(s); + } + + proxy_settings const& session::dht_proxy() const + { + return m_impl->dht_proxy(); + } +#endif + void session::set_max_uploads(int limit) { m_impl->set_max_uploads(limit); @@ -312,5 +360,9 @@ namespace libtorrent m_impl->set_severity_level(s); } + connection_queue& session::get_connection_queue() + { + return m_impl->m_half_open; + } } diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index f4a5d8904..2e2238465 100644 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -73,7 +75,6 @@ POSSIBILITY OF SUCH DAMAGE. #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; @@ -214,7 +215,8 @@ namespace libtorrent { namespace detail for (std::vector::const_iterator i = t->peers.begin(); i != t->peers.end(); ++i) { - t->torrent_ptr->get_policy().peer_from_tracker(*i, id); + t->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); } } else @@ -336,7 +338,8 @@ namespace libtorrent { namespace detail for (std::vector::const_iterator i = processing->peers.begin(); i != processing->peers.end(); ++i) { - processing->torrent_ptr->get_policy().peer_from_tracker(*i, id); + processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); } } else @@ -465,8 +468,7 @@ namespace libtorrent { namespace detail { seed_random_generator() { - std::srand((unsigned int)(boost::posix_time::microsec_clock:: - universal_time().time_of_day().total_microseconds())); + std::srand(total_microseconds(time_now() - min_time())); } }; @@ -475,39 +477,57 @@ namespace libtorrent { namespace detail , fingerprint const& cl_fprint , char const* listen_interface) : m_strand(m_io_service) + , m_files(40) + , m_half_open(m_io_service) , m_dl_bandwidth_manager(m_io_service, peer_connection::download_channel) , m_ul_bandwidth_manager(m_io_service, peer_connection::upload_channel) - , m_tracker_manager(m_settings) + , m_tracker_manager(m_settings, m_tracker_proxy) , m_listen_port_range(listen_port_range) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) + , m_external_listen_port(0) , m_abort(false) , m_max_uploads(-1) , m_max_connections(-1) - , m_half_open_limit(-1) , m_incoming_connection(false) - , m_files(40) - , m_last_tick(microsec_clock::universal_time()) + , m_last_tick(time_now()) +#ifndef TORRENT_DISABLE_DHT + , m_dht_same_port(true) + , m_external_udp_port(0) +#endif + , m_natpmp(m_io_service, m_listen_interface.address() + , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) + , m_upnp(m_io_service, m_half_open, m_listen_interface.address() + , m_settings.user_agent + , bind(&session_impl::on_port_mapping, this, _1, _2, _3)) + , m_lsd(m_io_service, m_listen_interface.address() + , bind(&session_impl::on_lsd_peer, this, _1, _2)) , m_timer(m_io_service) + , m_next_connect_torrent(0) , m_checker_impl(*this) { #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) m_logger = create_log("main_session", listen_port(), false); - using boost::posix_time::second_clock; - using boost::posix_time::to_simple_string; - (*m_logger) << to_simple_string(second_clock::universal_time()) << "\n"; + (*m_logger) << time_now_string() << "\n"; - m_stats_logger = create_log("session_stats", listen_port(), false); - (*m_stats_logger) << - "1. second\n" - "2. hard upload quota\n" - "3. hard download quota\n" - "\n"; - m_second_counter = 0; m_dl_bandwidth_manager.m_ses = this; m_ul_bandwidth_manager.m_ses = this; #endif +#ifdef TORRENT_STATS + m_stats_logger.open("session_stats.log"); + m_stats_logger << + "1. second\n" + "2. upload rate\n" + "3. download rate\n" + "4. downloading torrents\n" + "5. seeding torrents\n" + "6. peers\n" + "7. connecting peers\n" + "\n"; + m_second_counter = 0; +#endif + // ---- generate a peer id ---- static seed_random_generator seeder; @@ -548,7 +568,7 @@ namespace libtorrent { namespace detail } #endif -#ifndef TORRENT_DISABLE_DHT +#ifndef TORRENT_DISABLE_DHT void session_impl::add_dht_node(udp::endpoint n) { if (m_dht) m_dht->add_node(n); @@ -585,6 +605,12 @@ namespace libtorrent { namespace detail #if defined(TORRENT_VERBOSE_LOGGING) (*i->second->m_logger) << "*** CONNECTION FILTERED\n"; #endif + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert(peer_blocked_alert(sender.address() + , "peer connection closed by IP filter")); + } + session_impl::connection_map::iterator j = i; ++i; j->second->disconnect(); @@ -619,6 +645,7 @@ namespace libtorrent { namespace detail m_listen_socket->open(m_listen_interface.protocol()); m_listen_socket->bind(m_listen_interface); m_listen_socket->listen(); + m_external_listen_port = m_listen_interface.port(); break; } catch (asio::system_error& e) @@ -672,50 +699,23 @@ namespace libtorrent { namespace detail #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) if (m_listen_socket) { - (*m_logger) << "listening on port: " << m_listen_interface.port() << "\n"; + (*m_logger) << "listening on port: " << m_listen_interface.port() + << " external port: " << m_external_listen_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_io_service)); - m_listen_socket->async_accept(*c + shared_ptr c(new socket_type(m_io_service)); + c->instantiate(); + m_listen_socket->async_accept(c->get() , bind(&session_impl::on_incoming_connection, this, c , weak_ptr(m_listen_socket), _1)); } - void session_impl::on_incoming_connection(shared_ptr const& s + void session_impl::on_incoming_connection(shared_ptr const& s , weak_ptr const& listen_socket, asio::error_code const& e) try { if (listen_socket.expired()) @@ -753,12 +753,16 @@ namespace libtorrent { namespace detail #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!! + if (m_alerts.should_post(alert::info)) + { + m_alerts.post_alert(peer_blocked_alert(endp.address() + , "incoming connection blocked by IP filter")); + } return; } boost::intrusive_ptr c( - new bt_peer_connection(*this, s)); + new bt_peer_connection(*this, s, 0)); #ifndef NDEBUG c->m_in_constructor = false; #endif @@ -770,9 +774,9 @@ namespace libtorrent { namespace detail #ifndef NDEBUG std::string err = exc.what(); #endif - } + }; - void session_impl::connection_failed(boost::shared_ptr const& s + void session_impl::connection_failed(boost::shared_ptr const& s , tcp::endpoint const& a, char const* message) #ifndef NDEBUG try @@ -783,46 +787,21 @@ namespace libtorrent { namespace detail 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 (p == m_connections.end()) return; + if (m_alerts.should_post(alert::debug)) { - if (m_alerts.should_post(alert::debug)) - { - m_alerts.post_alert( - peer_error_alert( - a - , p->second->pid() - , message)); - } + 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"; + (*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(); - } - } + p->second->set_failed(); + p->second->disconnect(); } #ifndef NDEBUG catch (...) @@ -836,43 +815,9 @@ namespace libtorrent { namespace detail 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); - } + connection_map::iterator i = m_connections.find(p->get_socket()); + if (i != m_connections.end()) + m_connections.erase(i); } void session_impl::set_peer_id(peer_id const& id) @@ -902,14 +847,75 @@ namespace libtorrent { namespace detail } 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(); + float tick_interval = total_microseconds(time_now() - m_last_tick) / 1000000.f; + m_last_tick = time_now(); m_timer.expires_from_now(seconds(1)); m_timer.async_wait(m_strand.wrap( bind(&session_impl::second_tick, this, _1))); + +#ifdef TORRENT_STATS + ++m_second_counter; + int downloading_torrents = 0; + int seeding_torrents = 0; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + if (i->second->is_seed()) + ++seeding_torrents; + else + ++downloading_torrents; + } + int num_connections = 0; + int num_half_open = 0; + for (connection_map::iterator i = m_connections.begin() + , end(m_connections.end()); i != end; ++i) + { + if (i->second->is_connecting()) + ++num_half_open; + else + ++num_connections; + } + m_stats_logger + << m_second_counter << "\t" + << m_stat.upload_rate() << "\t" + << m_stat.download_rate() << "\t" + << downloading_torrents << "\t" + << seeding_torrents << "\t" + << num_connections << "\t" + << num_half_open << "\t" + << std::endl; +#endif + + + // let torrents connect to peers if they want to + // if there are any torrents and any free slots + if (!m_torrents.empty() && m_half_open.free_slots()) + { + torrent_map::iterator next_connect_torrent = m_torrents.begin(); + if (m_next_connect_torrent < int(m_torrents.size())) + std::advance(next_connect_torrent, m_next_connect_torrent); + else + m_next_connect_torrent = 0; + torrent_map::iterator i = next_connect_torrent; + do + { + torrent& t = *i->second; + if (t.want_more_peers()) + t.try_connect_peer(); + ++m_next_connect_torrent; + if (!m_half_open.free_slots()) break; + ++i; + if (i == m_torrents.end()) + { + assert(m_next_connect_torrent == int(m_torrents.size())); + i = m_torrents.begin(); + m_next_connect_torrent = 0; + } + } while (i != next_connect_torrent); + } + // do the second_tick() on each connection // this will update their statistics (download and upload speeds) // also purge sockets that have timed out @@ -943,23 +949,34 @@ namespace libtorrent { namespace detail continue; } - c.keep_alive(); + try + { + c.keep_alive(); + } + catch (std::exception& exc) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*c.m_logger) << "**ERROR**: " << exc.what() << "\n"; +#endif + c.set_failed(); + c.disconnect(); + } } // 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();) + for (torrent_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.listen_port = m_external_listen_port; req.key = m_key; - m_tracker_manager.queue_request(m_strand, req, t.tracker_login() - , m_listen_interface.address(), i->second); + m_tracker_manager.queue_request(m_strand, m_half_open, req + , t.tracker_login(), m_listen_interface.address(), i->second); if (m_alerts.should_post(alert::info)) { @@ -975,7 +992,6 @@ namespace libtorrent { namespace detail } m_stat.second_tick(tick_interval); - // distribute the maximum upload rate among the torrents assert(m_max_uploads >= -1); @@ -1005,15 +1021,13 @@ namespace libtorrent { namespace detail catch (std::exception& exc) { #ifndef NDEBUG - std::string err = exc.what(); + std::cerr << exc.what() << std::endl; + assert(false); #endif }; // msvc 7.1 seems to require this - +/* void session_impl::connection_completed( - boost::intrusive_ptr const& p) -#ifndef NDEBUG - try -#endif + boost::intrusive_ptr const& p) try { mutex_t::scoped_lock l(m_mutex); @@ -1026,13 +1040,14 @@ namespace libtorrent { namespace detail process_connection_queue(); } -#ifndef NDEBUG catch (std::exception& e) { +#ifndef NDEBUG + std::cerr << e.what() << std::endl; assert(false); - }; #endif - + }; +*/ void session_impl::operator()() { eh_initializer(); @@ -1041,9 +1056,11 @@ namespace libtorrent { namespace detail { session_impl::mutex_t::scoped_lock l(m_mutex); open_listen_port(); + m_natpmp.set_mappings(m_listen_interface.port(), 0); + m_upnp.set_mappings(m_listen_interface.port(), 0); } - boost::posix_time::ptime timer = second_clock::universal_time(); + ptime timer = time_now(); do { @@ -1064,10 +1081,22 @@ namespace libtorrent { namespace detail while (!m_abort); deadline_timer tracker_timer(m_io_service); + // this will remove the port mappings + m_natpmp.close(); + m_upnp.close(); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " locking mutex\n"; +#endif session_impl::mutex_t::scoped_lock l(m_mutex); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " aborting all tracker requests\n"; +#endif m_tracker_manager.abort_all_requests(); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " sending stopped to all torrent's trackers\n"; +#endif for (std::map >::iterator i = m_torrents.begin(); i != m_torrents.end(); ++i) { @@ -1080,27 +1109,35 @@ namespace libtorrent { namespace detail && !i->second->trackers().empty()) { tracker_request req = i->second->generate_tracker_request(); - req.listen_port = m_listen_interface.port(); + assert(m_external_listen_port > 0); + req.listen_port = m_external_listen_port; req.key = m_key; std::string login = i->second->tracker_login(); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) boost::shared_ptr tl(new tracker_logger(*this)); m_tracker_loggers.push_back(tl); - m_tracker_manager.queue_request(m_strand, req, login, m_listen_interface.address(), tl); + m_tracker_manager.queue_request(m_strand, m_half_open, req, login + , m_listen_interface.address(), tl); #else - m_tracker_manager.queue_request(m_strand, req, login, m_listen_interface.address()); + m_tracker_manager.queue_request(m_strand, m_half_open, req, login + , m_listen_interface.address()); #endif } } - ptime start(microsec_clock::universal_time()); + ptime start(time_now()); l.unlock(); - while (microsec_clock::universal_time() - start < seconds( +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for trackers to respond (" + << m_settings.stop_tracker_timeout << " seconds timeout)\n"; +#endif + + while (time_now() - start < seconds( m_settings.stop_tracker_timeout) && !m_tracker_manager.empty()) { - tracker_timer.expires_from_now(boost::posix_time::milliseconds(100)); + tracker_timer.expires_from_now(milliseconds(100)); tracker_timer.async_wait(m_strand.wrap( bind(&io_service::stop, &m_io_service))); @@ -1108,15 +1145,17 @@ namespace libtorrent { namespace detail m_io_service.run(); } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " tracker shutdown complete, locking mutex\n"; +#endif + l.lock(); assert(m_abort); m_abort = true; - m_connection_queue.clear(); - - while (!m_half_open.empty()) - m_half_open.begin()->second->disconnect(); - +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " cleaning up connections\n"; +#endif while (!m_connections.empty()) m_connections.begin()->second->disconnect(); @@ -1128,6 +1167,9 @@ namespace libtorrent { namespace detail } #endif +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " cleaning up torrents\n"; +#endif m_torrents.clear(); assert(m_torrents.empty()); @@ -1206,8 +1248,13 @@ namespace libtorrent { namespace detail , boost::filesystem::path const& save_path , entry const& resume_data , bool compact_mode - , int block_size) + , int block_size + , storage_constructor_type sc) { + // if you get this assert, you haven't managed to + // open a listen port. call listen_on() first. + assert(m_external_listen_port > 0); + // make sure the block_size is an even power of 2 #ifndef NDEBUG for (int i = 0; i < 32; ++i) @@ -1246,7 +1293,8 @@ namespace libtorrent { namespace detail boost::shared_ptr torrent_ptr( new torrent(*this, m_checker_impl, ti, save_path , m_listen_interface, compact_mode, block_size - , settings())); + , settings(), sc)); + torrent_ptr->start(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -1291,7 +1339,8 @@ namespace libtorrent { namespace detail , boost::filesystem::path const& save_path , entry const& , bool compact_mode - , int block_size) + , int block_size + , storage_constructor_type sc) { // make sure the block_size is an even power of 2 #ifndef NDEBUG @@ -1332,7 +1381,8 @@ namespace libtorrent { namespace detail boost::shared_ptr torrent_ptr( new torrent(*this, m_checker_impl, tracker_url, info_hash, name , save_path, m_listen_interface, compact_mode, block_size - , settings())); + , settings(), sc)); + torrent_ptr->start(); #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -1368,16 +1418,17 @@ namespace libtorrent { namespace detail { tracker_request req = t.generate_tracker_request(); assert(req.event == tracker_request::stopped); - req.listen_port = m_listen_interface.port(); + assert(m_external_listen_port > 0); + req.listen_port = m_external_listen_port; req.key = m_key; #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) boost::shared_ptr tl(new tracker_logger(*this)); m_tracker_loggers.push_back(tl); - m_tracker_manager.queue_request(m_strand, req + m_tracker_manager.queue_request(m_strand, m_half_open, req , t.tracker_login(), m_listen_interface.address(), tl); #else - m_tracker_manager.queue_request(m_strand, req + m_tracker_manager.queue_request(m_strand, m_half_open, req , t.tracker_login(), m_listen_interface.address()); #endif @@ -1433,26 +1484,39 @@ namespace libtorrent { namespace detail 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(); + bool new_listen_address = m_listen_interface.address() != new_interface.address(); + + if (new_listen_address) + { + m_natpmp.rebind(new_interface.address()); + m_upnp.rebind(new_interface.address()); + m_lsd.rebind(new_interface.address()); + } + + m_natpmp.set_mappings(m_listen_interface.port(), 0); + m_upnp.set_mappings(m_listen_interface.port(), 0); + +#ifndef TORRENT_DISABLE_DHT + if ((new_listen_address || m_dht_same_port) && m_dht) + { + if (m_dht_same_port) + m_dht_settings.service_port = new_interface.port(); + // the listen interface changed, rebind the dht listen socket as well + m_dht->rebind(new_interface.address() + , m_dht_settings.service_port); + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); + } +#endif + #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) m_logger = create_log("main_session", listen_port(), false); - using boost::posix_time::second_clock; - using boost::posix_time::to_simple_string; - (*m_logger) << to_simple_string(second_clock::universal_time()) << "\n"; + (*m_logger) << time_now_string() << "\n"; #endif return m_listen_socket; @@ -1461,7 +1525,67 @@ namespace libtorrent { namespace detail unsigned short session_impl::listen_port() const { mutex_t::scoped_lock l(m_mutex); - return m_listen_interface.port(); + return m_external_listen_port; + } + + void session_impl::announce_lsd(sha1_hash const& ih) + { + mutex_t::scoped_lock l(m_mutex); + // use internal listen port for local peers + m_lsd.announce(ih, m_listen_interface.port()); + } + + void session_impl::on_lsd_peer(tcp::endpoint peer, sha1_hash const& ih) + { + mutex_t::scoped_lock l(m_mutex); + + boost::shared_ptr t = find_torrent(ih).lock(); + if (!t) return; + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() + << ": added peer from local discovery: " << peer << "\n"; +#endif + t->get_policy().peer_from_tracker(peer, peer_id(0), peer_info::lsd, 0); + } + + void session_impl::on_port_mapping(int tcp_port, int udp_port + , std::string const& errmsg) + { +#ifndef TORRENT_DISABLE_DHT + if (udp_port != 0) + { + m_external_udp_port = udp_port; + m_dht_settings.service_port = udp_port; + if (m_alerts.should_post(alert::info)) + { + std::stringstream msg; + msg << "successfully mapped UDP port " << udp_port; + m_alerts.post_alert(portmap_alert(msg.str())); + } + } +#endif + + if (tcp_port != 0) + { + m_external_listen_port = tcp_port; + if (m_alerts.should_post(alert::info)) + { + std::stringstream msg; + msg << "successfully mapped TCP port " << tcp_port; + m_alerts.post_alert(portmap_alert(msg.str())); + } + } + + if (!errmsg.empty()) + { + if (m_alerts.should_post(alert::warning)) + { + std::stringstream msg; + msg << "Error while mapping ports on NAT router: " << errmsg; + m_alerts.post_alert(portmap_error_alert(msg.str())); + } + } } session_status session_impl::status() const @@ -1496,6 +1620,7 @@ namespace libtorrent { namespace detail s.dht_nodes = 0; s.dht_node_cache = 0; s.dht_torrents = 0; + s.dht_global_nodes = 0; } #endif @@ -1512,6 +1637,23 @@ namespace libtorrent { namespace detail m_dht->stop(); m_dht = 0; } + if (m_dht_settings.service_port == 0 + || m_dht_same_port) + { + m_dht_same_port = true; + // if you hit this assert you are trying to start the + // DHT with the same port as the tcp listen port + // (which is default) _before_ you have opened the + // tcp listen port (so there is no configured port to use) + // basically, make sure you call listen_on() before + // start_dht(). See documentation for listen_on() for + // more information. + assert(m_listen_interface.port() > 0); + m_dht_settings.service_port = m_listen_interface.port(); + } + m_external_udp_port = m_dht_settings.service_port; + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); m_dht = new dht::dht_tracker(m_io_service , m_dht_settings, m_listen_interface.address() , startup_state); @@ -1528,13 +1670,26 @@ namespace libtorrent { namespace detail 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 + // only change the dht listen port in case the settings + // contains a vaiid port, and if it is different from + // the current setting + if (settings.service_port != 0) + m_dht_same_port = false; + else + m_dht_same_port = true; + if (!m_dht_same_port + && settings.service_port != m_dht_settings.service_port && m_dht) { m_dht->rebind(m_listen_interface.address() , settings.service_port); + m_natpmp.set_mappings(0, m_dht_settings.service_port); + m_upnp.set_mappings(0, m_dht_settings.service_port); + m_external_udp_port = settings.service_port; } m_dht_settings = settings; + if (m_dht_same_port) + m_dht_settings.service_port = m_listen_interface.port(); } entry session_impl::dht_state() const @@ -1579,14 +1734,23 @@ namespace libtorrent { namespace detail #ifndef TORRENT_DISABLE_DHT stop_dht(); #endif + +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << "\n\n *** shutting down session *** \n\n"; +#endif // lock the main thread and abort it mutex_t::scoped_lock l(m_mutex); m_abort = true; m_io_service.stop(); l.unlock(); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for main thread\n"; +#endif m_thread->join(); + assert(m_torrents.empty()); + // 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 @@ -1607,10 +1771,16 @@ namespace libtorrent { namespace detail m_checker_impl.m_cond.notify_one(); } +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " waiting for checker thread\n"; +#endif m_checker_thread->join(); assert(m_torrents.empty()); assert(m_connections.empty()); +#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) + (*m_logger) << time_now_string() << " shutdown complete!\n"; +#endif } void session_impl::set_max_uploads(int limit) @@ -1632,7 +1802,7 @@ namespace libtorrent { namespace detail assert(limit > 0 || limit == -1); mutex_t::scoped_lock l(m_mutex); - m_half_open_limit = limit; + m_half_open.limit(limit); } void session_impl::set_upload_rate_limit(int bytes_per_second) @@ -1658,7 +1828,7 @@ namespace libtorrent { namespace detail int session_impl::num_connections() const { mutex_t::scoped_lock l(m_mutex); - return m_connections.size() + m_half_open.size(); + return m_connections.size(); } @@ -1692,31 +1862,10 @@ namespace libtorrent { namespace detail 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::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) @@ -1816,13 +1965,15 @@ namespace libtorrent { namespace detail // the unfinished pieces entry::list_type& unfinished = rd["unfinished"].list(); - - tmp_unfinished.reserve(unfinished.size()); + int unfinished_size = int(unfinished.size()); + block_info.resize(num_blocks_per_piece * unfinished_size); + tmp_unfinished.reserve(unfinished_size); + int index = 0; for (entry::list_type::iterator i = unfinished.begin(); - i != unfinished.end(); ++i) + i != unfinished.end(); ++i, ++index) { piece_picker::downloading_piece p; - + p.info = &block_info[index * num_blocks_per_piece]; p.index = (int)(*i)["piece"].integer(); if (p.index < 0 || p.index >= info.num_pieces()) { @@ -1847,11 +1998,14 @@ namespace libtorrent { namespace detail { const int bit = j * 8 + k; if (bits & (1 << k)) - p.finished_blocks[bit] = true; + { + p.info[bit].finished = true; + ++p.finished; + } } } - if (p.finished_blocks.count() == 0) continue; + if (p.finished == 0) continue; std::vector::iterator slot_iter = std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index); @@ -1870,14 +2024,15 @@ namespace libtorrent { namespace detail = torrent_ptr->filesystem().piece_crc( slot_index , torrent_ptr->block_size() - , p.finished_blocks); + , p.info); const entry& ad = (*i)["adler32"]; // crc's didn't match, don't use the resume data if (ad.integer() != entry::integer_type(adler)) { - error = "checksum mismatch on piece " + boost::lexical_cast(p.index); + error = "checksum mismatch on piece " + + boost::lexical_cast(p.index); return; } @@ -1885,63 +2040,21 @@ namespace libtorrent { namespace detail } } - // 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)) + if (!torrent_ptr->verify_resume_data(rd, error)) return; piece_map.swap(tmp_pieces); unfinished_pieces.swap(tmp_unfinished); } - catch (invalid_encoding) + catch (invalid_encoding&) { return; } - catch (type_error) + catch (type_error&) { return; } - catch (file_error) + catch (file_error&) { return; } diff --git a/libtorrent/src/sha1.cpp b/libtorrent/src/sha1.cpp index 1d04b4a17..a7ea67113 100644 --- a/libtorrent/src/sha1.cpp +++ b/libtorrent/src/sha1.cpp @@ -10,6 +10,8 @@ By Steve Reid changelog at the end of the file. */ +#include "libtorrent/pch.hpp" + #include #include @@ -23,16 +25,16 @@ using boost::uint8_t; #include "libtorrent/config.hpp" -struct TORRENT_EXPORT SHA1_CTX +struct TORRENT_EXPORT SHA_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); +TORRENT_EXPORT void SHA1_Init(SHA_CTX* context); +TORRENT_EXPORT void SHA1_Update(SHA_CTX* context, uint8_t const* data, uint32_t len); +TORRENT_EXPORT void SHA1_Final(uint8_t* digest, SHA_CTX* context); namespace { @@ -123,7 +125,7 @@ namespace a = b = c = d = e = 0; } - void SHAPrintContext(SHA1_CTX *context, char *msg) + void SHAPrintContext(SHA_CTX *context, char *msg) { using namespace std; printf("%s (%d,%d) %x %x %x %x %x\n" @@ -134,7 +136,7 @@ namespace } template - void internal_update(SHA1_CTX* context, uint8_t const* data, uint32_t len) + void internal_update(SHA_CTX* context, uint8_t const* data, uint32_t len) { using namespace std; uint32_t i, j; // JHB @@ -174,7 +176,7 @@ namespace // SHA1Init - Initialize new context -void SHA1Init(SHA1_CTX* context) +void SHA1_Init(SHA_CTX* context) { // SHA1 initialization constants context->state[0] = 0x67452301; @@ -188,7 +190,7 @@ void SHA1Init(SHA1_CTX* context) // Run your data through this. -void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len) +void SHA1_Update(SHA_CTX* context, uint8_t const* data, uint32_t len) { #if defined __BIG_ENDIAN__ internal_update(context, data, len); @@ -207,7 +209,7 @@ void SHA1Update(SHA1_CTX* context, uint8_t const* data, uint32_t len) // Add padding and return the message digest. -void SHA1Final(SHA1_CTX* context, uint8_t* digest) +void SHA1_Final(uint8_t* digest, SHA_CTX* context) { uint8_t finalcount[8]; @@ -219,10 +221,10 @@ void SHA1Final(SHA1_CTX* context, uint8_t* digest) >> ((3-(i & 3)) * 8) ) & 255); } - SHA1Update(context, (uint8_t const*)"\200", 1); + SHA1_Update(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() + SHA1_Update(context, (uint8_t const*)"\0", 1); + SHA1_Update(context, finalcount, 8); // Should cause a SHA1Transform() for (uint32_t i = 0; i < 20; ++i) { @@ -299,6 +301,7 @@ By Arvid Norberg 4- using anonymous namespace to avoid external linkage on internal functions 5- using standard C++ includes +6- made API compatible with openssl still 100% PD */ diff --git a/libtorrent/src/socks5_stream.cpp b/libtorrent/src/socks5_stream.cpp new file mode 100644 index 000000000..b1679c4ac --- /dev/null +++ b/libtorrent/src/socks5_stream.cpp @@ -0,0 +1,315 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/socks5_stream.hpp" + +namespace libtorrent +{ + + void socks5_stream::name_lookup(asio::error_code const& e, tcp::resolver::iterator i + , boost::shared_ptr h) + { + if (e || i == tcp::resolver::iterator()) + { + (*h)(e); + close(); + return; + } + + m_sock.async_connect(i->endpoint(), boost::bind( + &socks5_stream::connected, this, _1, h)); + } + + void socks5_stream::connected(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + // send SOCKS5 authentication methods + m_buffer.resize(m_user.empty()?3:4); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + if (m_user.empty()) + { + write_uint8(1, p); // 1 authentication method (no auth) + write_uint8(0, p); // no authentication + } + else + { + write_uint8(2, p); // 2 authentication methods + write_uint8(0, p); // no authentication + write_uint8(2, p); // username/password + } + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake1, this, _1, h)); + } + + void socks5_stream::handshake1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake2, this, _1, h)); + } + + void socks5_stream::handshake2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int method = read_uint8(p); + + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (method == 0) + { + socks_connect(h); + } + else if (method == 2) + { + if (m_user.empty()) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + // start sub-negotiation + m_buffer.resize(m_user.size() + m_password.size() + 3); + char* p = &m_buffer[0]; + write_uint8(1, p); + write_uint8(m_user.size(), p); + write_string(m_user, p); + write_uint8(m_password.size(), p); + write_string(m_password, p); + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake3, this, _1, h)); + } + else + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + } + + void socks5_stream::handshake3(asio::error_code const& e + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(2); + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::handshake4, this, _1, h)); + } + + void socks5_stream::handshake4(asio::error_code const& e + , boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + char* p = &m_buffer[0]; + int version = read_uint8(p); + int status = read_uint8(p); + + if (version != 1) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + if (status != 0) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } + + void socks5_stream::socks_connect(boost::shared_ptr h) + { + using namespace libtorrent::detail; + + // send SOCKS5 connect command + m_buffer.resize(6 + (m_remote_endpoint.address().is_v4()?4:16)); + char* p = &m_buffer[0]; + write_uint8(5, p); // SOCKS VERSION 5 + write_uint8(1, p); // CONNECT command + write_uint8(0, p); // reserved + write_uint8(m_remote_endpoint.address().is_v4()?1:4, p); // address type + write_address(m_remote_endpoint.address(), p); + write_uint16(m_remote_endpoint.port(), p); + assert(p - &m_buffer[0] == int(m_buffer.size())); + + asio::async_write(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect1, this, _1, h)); + } + + void socks5_stream::connect1(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + m_buffer.resize(6 + 4); // assume an IPv4 address + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect2, this, _1, h)); + } + + void socks5_stream::connect2(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + using namespace libtorrent::detail; + + // send SOCKS5 connect command + char* p = &m_buffer[0]; + int version = read_uint8(p); + if (version < 5) + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + int response = read_uint8(p); + if (response != 0) + { + asio::error_code e = asio::error::fault; + switch (response) + { + case 1: e = asio::error::fault; break; + case 2: e = asio::error::no_permission; break; + case 3: e = asio::error::network_unreachable; break; + case 4: e = asio::error::host_unreachable; break; + case 5: e = asio::error::connection_refused; break; + case 6: e = asio::error::timed_out; break; + case 7: e = asio::error::operation_not_supported; break; + case 8: e = asio::error::address_family_not_supported; break; + } + (*h)(e); + close(); + return; + } + p += 1; // reserved + int atyp = read_uint8(p); + // we ignore the proxy IP it was bound to + if (atyp == 1) + { + std::vector().swap(m_buffer); + (*h)(e); + return; + } + int skip_bytes = 0; + if (atyp == 4) + { + skip_bytes = 12; + } + else if (atyp == 3) + { + skip_bytes = read_uint8(p) - 3; + } + else + { + (*h)(asio::error::operation_not_supported); + close(); + return; + } + m_buffer.resize(skip_bytes); + + asio::async_read(m_sock, asio::buffer(m_buffer) + , boost::bind(&socks5_stream::connect3, this, _1, h)); + } + + void socks5_stream::connect3(asio::error_code const& e, boost::shared_ptr h) + { + if (e) + { + (*h)(e); + close(); + return; + } + + std::vector().swap(m_buffer); + (*h)(e); + } +} + diff --git a/libtorrent/src/stat.cpp b/libtorrent/src/stat.cpp index c36bd3725..d695edc42 100644 --- a/libtorrent/src/stat.cpp +++ b/libtorrent/src/stat.cpp @@ -35,11 +35,13 @@ POSSIBILITY OF SUCH DAMAGE. // per second and one (low pass-filter) for rates < 1 // packet per second. +#include "libtorrent/pch.hpp" + #include +#include #include "libtorrent/stat.hpp" #include "libtorrent/invariant_check.hpp" -#include #if defined _MSC_VER && _MSC_VER <= 1200 #define for if (false) {} else for diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index 69c192275..0623dfeb6 100644 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -46,7 +48,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include @@ -74,6 +75,19 @@ POSSIBILITY OF SUCH DAMAGE. #include #endif +#if defined(__APPLE__) +// for getattrlist() +#include +#include +// for statfs() +#include +#include +#endif + +#if defined(__linux__) +#include +#endif + #if defined(_WIN32) && defined(UNICODE) #include @@ -104,7 +118,9 @@ namespace libtorrent } } } +#endif +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 namespace { using libtorrent::safe_convert; @@ -206,11 +222,11 @@ bool operator<(boost::filesystem::path const& lhs #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; +#if !defined(NDEBUG) && defined(TORRENT_STORAGE_DEBUG) namespace { using namespace libtorrent; @@ -221,8 +237,8 @@ namespace log << s; log.flush(); } - } +#endif namespace libtorrent { @@ -240,7 +256,7 @@ namespace libtorrent try { path f = p / i->path; -#if defined(_WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 size = file_size_win(f); time = last_write_time_win(f); #else @@ -254,10 +270,17 @@ namespace libtorrent return sizes; } + // matches the sizes and timestamps of the files passed in + // in non-compact mode, actual file sizes and timestamps + // are allowed to be bigger and more recent than the fast + // resume data. This is because full allocation will not move + // pieces, so any older version of the resume data will + // still be a correct subset of the actual data on disk. bool match_filesizes( torrent_info const& t , path p , std::vector > const& sizes + , bool compact_mode , std::string* error) { if ((int)sizes.size() != t.num_files()) @@ -277,7 +300,7 @@ namespace libtorrent try { path f = p / i->path; -#if defined(_WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 size = file_size_win(f); time = last_write_time_win(f); #else @@ -286,19 +309,23 @@ namespace libtorrent #endif } catch (std::exception&) {} - if (size != s->first) + if (size != s->first + || (!compact_mode && size < s->first)) { if (error) *error = "filesize mismatch for file '" + i->path.native_file_string() - + "', expected to be " + boost::lexical_cast(s->first) + + "', size: " + boost::lexical_cast(size) + + ", expected to be " + boost::lexical_cast(s->first) + " bytes"; return false; } - if (time != s->second) + if (time != s->second + || (!compact_mode && time < s->second)) { if (error) *error = "timestamp mismatch for file '" + i->path.native_file_string() - + "', expected to have modification date " + + "', modification date: " + boost::lexical_cast(time) + + ", expected to have modification date " + boost::lexical_cast(s->second); return false; } @@ -341,53 +368,172 @@ namespace libtorrent int slot; }; - class storage::impl : public thread_safe_storage, boost::noncopyable + class storage : public storage_interface, thread_safe_storage, boost::noncopyable { public: - impl(torrent_info const& info, path const& path, file_pool& fp) + storage(torrent_info const& info, path const& path, file_pool& fp) : thread_safe_storage(info.num_pieces()) - , info(info) - , files(fp) + , m_info(info) + , m_files(fp) { - save_path = complete(path); - assert(save_path.is_complete()); + assert(info.begin_files() != info.end_files()); + m_save_path = complete(path); + assert(m_save_path.is_complete()); } - impl(impl const& x) - : thread_safe_storage(x.info.num_pieces()) - , info(x.info) - , save_path(x.save_path) - , files(x.files) - {} + void release_files(); + void initialize(bool allocate_files); + bool move_storage(path save_path); + size_type read(char* buf, int slot, int offset, int size); + void write(const char* buf, int slot, int offset, int size); + void move_slot(int src_slot, int dst_slot); + void swap_slots(int slot1, int slot2); + void swap_slots3(int slot1, int slot2, int slot3); + bool verify_resume_data(entry& rd, std::string& error); + void write_resume_data(entry& rd) const; - ~impl() + size_type read_impl(char* buf, int slot, int offset, int size, bool fill_zero); + + ~storage() { - files.release(this); + m_files.release(this); } - torrent_info const& info; - path save_path; + torrent_info const& m_info; + path m_save_path; // the file pool is typically stored in // the session, to make all storage // instances use the same pool - file_pool& files; + file_pool& m_files; + + // temporary storage for moving pieces + std::vector m_scratch_buffer; }; - storage::storage(torrent_info const& info, path const& path - , file_pool& fp) - : m_pimpl(new impl(info, path, fp)) + void storage::initialize(bool allocate_files) { - assert(info.begin_files() != info.end_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 (dir != last_path) + { + last_path = dir; + +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 + if (!exists_win(last_path)) + create_directories_win(last_path); +#else + if (!exists(last_path)) + create_directories(last_path); +#endif + } + + // if the file is empty, just create it. But also make sure + // the directory exits. + if (file_iter->size == 0) + { + file(m_save_path / file_iter->path, file::out); + continue; + } + + if (allocate_files) + { + m_files.open_file(this, m_save_path / file_iter->path, file::in | file::out) + ->set_size(file_iter->size); + } + } } void storage::release_files() { - m_pimpl->files.release(m_pimpl.get()); + m_files.release(this); + std::vector().swap(m_scratch_buffer); } - void storage::swap(storage& other) + void storage::write_resume_data(entry& rd) const { - m_pimpl.swap(other.m_pimpl); + std::vector > file_sizes + = get_filesizes(m_info, m_save_path); + + rd["file sizes"] = entry::list_type(); + entry::list_type& fl = rd["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)); + } + } + + bool storage::verify_resume_data(entry& rd, std::string& error) + { + 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 (file_sizes.empty()) + { + error = "the number of files in resume data is 0"; + return false; + } + + entry::list_type& slots = rd["slots"].list(); + bool seed = int(slots.size()) == m_info.num_pieces() + && std::find_if(slots.begin(), slots.end() + , boost::bind(std::less() + , boost::bind((size_type const& (entry::*)() const) + &entry::integer, _1), 0)) == slots.end(); + + bool full_allocation_mode = false; + try + { + full_allocation_mode = rd["allocation"].string() == "full"; + } + catch (std::exception&) {} + + if (seed) + { + if (m_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(m_info.num_files()) + ")"; + return false; + } + + 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 = m_info.begin_files() + , end(m_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 false; + } + } + return true; + } + + return match_filesizes(m_info, m_save_path, file_sizes + , !full_allocation_mode, &error); } // returns true on success @@ -398,7 +544,7 @@ namespace libtorrent save_path = complete(save_path); -#if defined(_WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 std::wstring wsave_path(safe_convert(save_path.native_file_string())); if (!exists_win(save_path)) { @@ -409,25 +555,25 @@ namespace libtorrent return false; } #else - if(!exists(save_path)) + if (!exists(save_path)) create_directory(save_path); - else if(!is_directory(save_path)) + else if (!is_directory(save_path)) return false; #endif - m_pimpl->files.release(m_pimpl.get()); + m_files.release(this); - old_path = m_pimpl->save_path / m_pimpl->info.name(); - new_path = save_path / m_pimpl->info.name(); + old_path = m_save_path / m_info.name(); + new_path = save_path / m_info.name(); try { -#if defined(_WIN32) && defined(UNICODE) +#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400 rename_win(old_path, new_path); #else rename(old_path, new_path); #endif - m_pimpl->save_path = save_path; + m_save_path = save_path; return true; } catch (std::exception&) {} @@ -435,15 +581,14 @@ namespace libtorrent } #ifndef NDEBUG - +/* void storage::shuffle() { - int num_pieces = m_pimpl->info.num_pieces(); + int num_pieces = m_info.num_pieces(); std::vector pieces(num_pieces); for (std::vector::iterator i = pieces.begin(); - i != pieces.end(); - ++i) + i != pieces.end(); ++i) { *i = static_cast(i - pieces.begin()); } @@ -456,43 +601,90 @@ namespace libtorrent { 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)); + const int slot_size =static_cast(m_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 + void storage::move_slot(int src_slot, int dst_slot) + { + int piece_size = m_info.piece_size(dst_slot); + m_scratch_buffer.resize(piece_size); + read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true); + write(&m_scratch_buffer[0], dst_slot, 0, piece_size); + } + + void storage::swap_slots(int slot1, int slot2) + { + // the size of the target slot is the size of the piece + int piece_size = m_info.piece_length(); + int piece1_size = m_info.piece_size(slot2); + int piece2_size = m_info.piece_size(slot1); + m_scratch_buffer.resize(piece_size * 2); + read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); + read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); + write(&m_scratch_buffer[0], slot2, 0, piece1_size); + write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size); + } + + void storage::swap_slots3(int slot1, int slot2, int slot3) + { + // the size of the target slot is the size of the piece + int piece_size = m_info.piece_length(); + int piece1_size = m_info.piece_size(slot2); + int piece2_size = m_info.piece_size(slot3); + int piece3_size = m_info.piece_size(slot1); + m_scratch_buffer.resize(piece_size * 2); + read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true); + read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true); + write(&m_scratch_buffer[0], slot2, 0, piece1_size); + read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true); + write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size); + write(&m_scratch_buffer[0], slot1, 0, piece3_size); + } + size_type storage::read( char* buf , int slot , int offset , int size) + { + return read_impl(buf, slot, offset, size, false); + } + + size_type storage::read_impl( + char* buf + , int slot + , int offset + , int size + , bool fill_zero) { assert(buf != 0); - assert(slot >= 0 && slot < m_pimpl->info.num_pieces()); + assert(slot >= 0 && slot < m_info.num_pieces()); assert(offset >= 0); - assert(offset < m_pimpl->info.piece_size(slot)); + assert(offset < m_info.piece_size(slot)); assert(size > 0); - slot_lock lock(*m_pimpl, slot); + slot_lock lock(*this, slot); #ifndef NDEBUG std::vector slices - = m_pimpl->info.map_block(slot, offset, size); + = m_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()); + size_type start = slot * (size_type)m_info.piece_length() + offset; + assert(start + size <= m_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();;) + for (file_iter = m_info.begin_files();;) { if (file_offset < file_iter->size) break; @@ -501,10 +693,9 @@ namespace libtorrent ++file_iter; } - boost::shared_ptr in(m_pimpl->files.open_file( - m_pimpl.get() - , m_pimpl->save_path / file_iter->path - , file::in)); + int buf_pos = 0; + boost::shared_ptr in(m_files.open_file( + this, m_save_path / file_iter->path, file::in)); assert(file_offset < file_iter->size); @@ -514,7 +705,10 @@ namespace libtorrent if (new_pos != file_offset) { // the file was not big enough - throw file_error("slot has no storage"); + if (!fill_zero) + throw file_error("slot has no storage"); + std::memset(buf + buf_pos, 0, size - buf_pos); + return size; } #ifndef NDEBUG @@ -523,7 +717,7 @@ namespace libtorrent #endif int left_to_read = size; - int slot_size = static_cast(m_pimpl->info.piece_size(slot)); + int slot_size = static_cast(m_info.piece_size(slot)); if (offset + left_to_read > slot_size) left_to_read = slot_size - offset; @@ -531,7 +725,6 @@ namespace libtorrent assert(left_to_read >= 0); size_type result = left_to_read; - int buf_pos = 0; #ifndef NDEBUG int counter = 0; @@ -549,7 +742,7 @@ namespace libtorrent 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 + assert(m_info.file_at(slices[counter].file_index).path == file_iter->path); #endif @@ -558,7 +751,11 @@ namespace libtorrent if (read_bytes != actual_read) { // the file was not big enough - throw file_error("slot has no storage"); + if (actual_read > 0) buf_pos += actual_read; + if (!fill_zero) + throw file_error("slot has no storage"); + std::memset(buf + buf_pos, 0, size - buf_pos); + return size; } left_to_read -= read_bytes; @@ -575,16 +772,14 @@ namespace libtorrent // 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; + path path = m_save_path / file_iter->path; file_offset = 0; - in = m_pimpl->files.open_file( - m_pimpl.get() - , path, file::in); + in = m_files.open_file( + this, path, file::in); in->seek(0); } } - return result; } @@ -597,38 +792,37 @@ namespace libtorrent { assert(buf != 0); assert(slot >= 0); - assert(slot < m_pimpl->info.num_pieces()); + assert(slot < m_info.num_pieces()); assert(offset >= 0); assert(size > 0); - slot_lock lock(*m_pimpl, slot); + slot_lock lock(*this, slot); #ifndef NDEBUG std::vector slices - = m_pimpl->info.map_block(slot, offset, size); + = m_info.map_block(slot, offset, size); assert(!slices.empty()); #endif - size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset; + size_type start = slot * (size_type)m_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();;) + for (file_iter = m_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()); + assert(file_iter != m_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); + path p(m_save_path / file_iter->path); + boost::shared_ptr out = m_files.open_file( + this, p, file::out | file::in); assert(file_offset < file_iter->size); assert(slices[0].offset == file_offset); @@ -643,7 +837,7 @@ namespace libtorrent } int left_to_write = size; - int slot_size = static_cast(m_pimpl->info.piece_size(slot)); + int slot_size = static_cast(m_info.piece_size(slot)); if (offset + left_to_write > slot_size) left_to_write = slot_size - offset; @@ -667,7 +861,7 @@ namespace libtorrent { assert(int(slices.size()) > counter); assert(slices[counter].size == write_bytes); - assert(m_pimpl->info.file_at(slices[counter].file_index).path + assert(m_info.file_at(slices[counter].file_index).path == file_iter->path); assert(buf_pos >= 0); @@ -695,21 +889,118 @@ namespace libtorrent #endif ++file_iter; - assert(file_iter != m_pimpl->info.end_files()); - path p = m_pimpl->save_path / file_iter->path; + assert(file_iter != m_info.end_files()); + path p = m_save_path / file_iter->path; file_offset = 0; - out = m_pimpl->files.open_file( - m_pimpl.get() - , p, file::out | file::in); + out = m_files.open_file( + this, p, file::out | file::in); out->seek(0); } } } + storage_interface* default_storage_constructor(torrent_info const& ti + , boost::filesystem::path const& path, file_pool& fp) + { + return new storage(ti, path, fp); + } + bool supports_sparse_files(path const& p) + { + assert(p.is_complete()); +#if defined(_WIN32) + // assume windows API is available + DWORD max_component_len = 0; + DWORD volume_flags = 0; + std::string root_device = p.root_name() + "\\"; +#if defined(UNICODE) + std::wstring wph(safe_convert(root_device)); + bool ret = ::GetVolumeInformation(wph.c_str(), 0 + , 0, 0, &max_component_len, &volume_flags, 0, 0); +#else + bool ret = ::GetVolumeInformation(root_device.c_str(), 0 + , 0, 0, &max_component_len, &volume_flags, 0, 0); +#endif + if (!ret) return false; + if (volume_flags & FILE_SUPPORTS_SPARSE_FILES) + return true; +#endif +#if defined(__APPLE__) || defined(__linux__) + // find the last existing directory of the save path + path query_path = p; + while (!query_path.empty() && !exists(query_path)) + query_path = query_path.branch_path(); +#endif + +#if defined(__APPLE__) + + struct statfs fsinfo; + int ret = statfs(query_path.native_directory_string().c_str(), &fsinfo); + if (ret != 0) return false; + + attrlist request; + request.bitmapcount = ATTR_BIT_MAP_COUNT; + request.reserved = 0; + request.commonattr = 0; + request.volattr = ATTR_VOL_CAPABILITIES; + request.dirattr = 0; + request.fileattr = 0; + request.forkattr = 0; + + struct vol_capabilities_attr_buf + { + unsigned long length; + vol_capabilities_attr_t info; + } vol_cap; + + ret = getattrlist(fsinfo.f_mntonname, &request, &vol_cap + , sizeof(vol_cap), 0); + if (ret != 0) return false; + + if (vol_cap.info.capabilities[VOL_CAPABILITIES_FORMAT] + & (VOL_CAP_FMT_SPARSE_FILES | VOL_CAP_FMT_ZERO_RUNS)) + { + return true; + } + + return true; +#endif + +#if defined(__linux__) + struct statfs buf; + int err = statfs(query_path.native_directory_string().c_str(), &buf); + if (err == 0) + { +#ifndef NDEBUG + std::cerr << "buf.f_type " << std::hex << buf.f_type << std::endl; +#endif + switch (buf.f_type) + { + case 0x5346544e: // NTFS + case 0xEF51: // EXT2 OLD + case 0xEF53: // EXT2 and EXT3 + case 0x00011954: // UFS + case 0x52654973: // ReiserFS + case 0x58465342: // XFS + return true; + } + } +#ifndef NDEBUG + else + { + std::cerr << "statfs returned " << err << std::endl; + std::cerr << "errno: " << errno << std::endl; + std::cerr << "path: " << query_path.native_directory_string() << std::endl; + } +#endif +#endif + + // TODO: POSIX implementation + return false; + } // -- piece_manager ----------------------------------------------------- @@ -721,7 +1012,8 @@ namespace libtorrent impl( torrent_info const& info , path const& path - , file_pool& fp); + , file_pool& fp + , storage_constructor_type sc); bool check_fastresume( aux::piece_checker_data& d @@ -735,12 +1027,12 @@ namespace libtorrent void release_files(); - void allocate_slots(int num_slots); + bool allocate_slots(int num_slots, bool abort_on_disk = false); void mark_failed(int index); unsigned long piece_crc( int slot_index , int block_size - , const std::bitset<256>& bitmask); + , piece_picker::block_info const* bi); int slot_for_piece(int piece_index) const; @@ -761,7 +1053,7 @@ namespace libtorrent bool move_storage(path save_path) { - if (m_storage.move_storage(save_path)) + if (m_storage->move_storage(save_path)) { m_save_path = complete(save_path); return true; @@ -789,7 +1081,7 @@ namespace libtorrent void debug_log() const; #endif #endif - storage m_storage; + boost::scoped_ptr m_storage; // if this is true, pieces are always allocated at the // lowest possible slot index. If it is false, pieces @@ -861,33 +1153,29 @@ namespace libtorrent // 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 - , file_pool& fp) - : m_storage(info, save_path, fp) + , file_pool& fp + , storage_constructor_type sc) + : m_storage(sc(info, save_path, fp)) , 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()); + m_fill_mode = !supports_sparse_files(save_path); } piece_manager::piece_manager( torrent_info const& info , path const& save_path - , file_pool& fp) - : m_pimpl(new impl(info, save_path, fp)) + , file_pool& fp + , storage_constructor_type sc) + : m_pimpl(new impl(info, save_path, fp, sc)) { } @@ -895,6 +1183,16 @@ namespace libtorrent { } + void piece_manager::write_resume_data(entry& rd) const + { + m_pimpl->m_storage->write_resume_data(rd); + } + + bool piece_manager::verify_resume_data(entry& rd, std::string& error) + { + return m_pimpl->m_storage->verify_resume_data(rd, error); + } + void piece_manager::release_files() { m_pimpl->release_files(); @@ -902,7 +1200,11 @@ namespace libtorrent void piece_manager::impl::release_files() { - m_storage.release_files(); + // synchronization ------------------------------------------------------ + boost::recursive_mutex::scoped_lock lock(m_mutex); + // ---------------------------------------------------------------------- + + m_storage->release_files(); } void piece_manager::impl::export_piece_map( @@ -930,6 +1232,9 @@ namespace libtorrent } } + bool piece_manager::compact_allocation() const + { return m_pimpl->m_compact_mode; } + void piece_manager::export_piece_map( std::vector& p) const { @@ -981,15 +1286,16 @@ namespace libtorrent unsigned long piece_manager::piece_crc( int index , int block_size - , const std::bitset<256>& bitmask) + , piece_picker::block_info const* bi) { - return m_pimpl->piece_crc(index, block_size, bitmask); + return m_pimpl->piece_crc(index, block_size, bi); } unsigned long piece_manager::impl::piece_crc( int slot_index , int block_size - , const std::bitset<256>& bitmask) + , piece_picker::block_info const* bi) + try { assert(slot_index >= 0); assert(slot_index < m_info.num_pieces()); @@ -1003,17 +1309,17 @@ namespace libtorrent for (int i = 0; i < num_blocks-1; ++i) { - if (!bitmask[i]) continue; - m_storage.read( + if (!bi[i].finished) continue; + m_storage->read( &buf[0] , slot_index , i * block_size , block_size); crc.update(&buf[0], block_size); } - if (bitmask[num_blocks - 1]) + if (bi[num_blocks - 1].finished) { - m_storage.read( + m_storage->read( &buf[0] , slot_index , block_size * (num_blocks - 1) @@ -1022,28 +1328,33 @@ namespace libtorrent } return crc.final(); } + catch (std::exception&) + { + return 0; + } size_type piece_manager::impl::read( char* buf - , int piece_index - , int offset - , int size) + , 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()); + 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); + return m_storage->read(buf, slot, offset, size); } size_type piece_manager::read( char* buf - , int piece_index - , int offset - , int size) + , int piece_index + , int offset + , int size) { return m_pimpl->read(buf, piece_index, offset, size); } @@ -1060,7 +1371,7 @@ namespace libtorrent 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); + m_storage->write(buf, slot, offset, size); } void piece_manager::write( @@ -1249,7 +1560,7 @@ namespace libtorrent // use while debugging to find // states that cannot be scanned // by check_pieces. -// m_storage.shuffle(); +// 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); @@ -1336,7 +1647,7 @@ namespace libtorrent m_state = state_finished; return std::make_pair(true, 1.f); } - + if (m_unallocated_slots.empty()) { m_state = state_finished; @@ -1347,7 +1658,21 @@ namespace libtorrent // pieces are spread out and placed at their // final position. assert(!m_unallocated_slots.empty()); - allocate_slots(1); + + if (!m_fill_mode) + { + // if we're not filling the allocation + // just make sure we move the current pieces + // into place, and just skip all other + // allocation + // allocate_slots returns true if it had to + // move any data + allocate_slots(m_unallocated_slots.size(), true); + } + else + { + allocate_slots(1); + } return std::make_pair(false, 1.f - (float)m_unallocated_slots.size() / (float)m_slot_to_piece.size()); @@ -1355,33 +1680,8 @@ namespace libtorrent 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(); + m_storage->initialize(!m_fill_mode && !m_compact_mode); - // 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())); @@ -1397,11 +1697,13 @@ namespace libtorrent try { - m_storage.read( - &m_piece_data[0] - , m_current_slot - , 0 - , int(m_info.piece_size(m_current_slot))); + int piece_size = int(m_info.piece_size(m_current_slot)); + int num_read = m_storage->read(&m_piece_data[0] + , m_current_slot, 0, piece_size); + + // if the file is incomplete, skip the rest of it + if (num_read != piece_size) + throw file_error(""); if (m_hash_to_piece.empty()) { @@ -1411,13 +1713,8 @@ namespace libtorrent } } - int piece_index = identify_data( - m_piece_data - , m_current_slot - , pieces - , num_pieces - , m_hash_to_piece - , mutex); + int piece_index = identify_data(m_piece_data, m_current_slot + , pieces, num_pieces, m_hash_to_piece, mutex); assert(num_pieces == std::count(pieces.begin(), pieces.end(), true)); assert(piece_index == unassigned || piece_index >= 0); @@ -1474,17 +1771,11 @@ namespace libtorrent 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); + if (other_piece >= 0) + m_storage->swap_slots(other_slot, m_current_slot); + else + m_storage->move_slot(m_current_slot, other_slot); + assert(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); } @@ -1500,24 +1791,19 @@ namespace libtorrent 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) + if (piece_index >= 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_piece_to_slot[piece_index] = other_slot; + m_storage->swap_slots(other_slot, m_current_slot); + } + else + { + m_storage->move_slot(other_slot, m_current_slot); } - 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); } @@ -1554,15 +1840,7 @@ namespace libtorrent 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); + m_storage->swap_slots(m_current_slot, slot1); assert(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); @@ -1583,7 +1861,6 @@ namespace libtorrent 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) { @@ -1594,22 +1871,17 @@ namespace libtorrent 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) + if (piece1 >= 0) { - m_storage.read(&buf1[0], slot1, 0, slot1_size); - m_storage.write(&buf1[0], slot2, 0, slot1_size); + m_piece_to_slot[piece1] = slot2; + m_storage->swap_slots3(m_current_slot, slot1, slot2); } - m_storage.write(&buf2[0], slot1, 0, slot3_size); + else + { + m_storage->move_slot(m_current_slot, slot1); + m_storage->move_slot(slot2, m_current_slot); + } + assert(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); } @@ -1786,10 +2058,7 @@ namespace libtorrent 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); + m_storage->move_slot(piece_index, slot_index); assert(m_slot_to_piece[piece_index] == piece_index); assert(m_piece_to_slot[piece_index] == piece_index); @@ -1842,7 +2111,7 @@ namespace libtorrent } - void piece_manager::impl::allocate_slots(int num_slots) + bool piece_manager::impl::allocate_slots(int num_slots, bool abort_on_disk) { assert(num_slots > 0); @@ -1861,41 +2130,54 @@ namespace libtorrent 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); + const int stack_buffer_size = 16*1024; + char zeroes[stack_buffer_size]; + memset(zeroes, 0, stack_buffer_size); + + bool written = false; for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i) { +// INVARIANT_CHECK; + int pos = m_unallocated_slots.front(); - // int piece_pos = pos; - bool write_back = false; + assert(m_slot_to_piece[pos] == unallocated); + assert(m_piece_to_slot[pos] != pos); 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_storage->move_slot(new_free_slot, pos); m_slot_to_piece[pos] = pos; m_piece_to_slot[pos] = pos; - write_back = true; + written = true; + } + else if (m_fill_mode) + { + int piece_size = int(m_info.piece_size(pos)); + int offset = 0; + for (; piece_size > 0; piece_size -= stack_buffer_size + , offset += stack_buffer_size) + { + m_storage->write(zeroes, pos, offset + , std::min(piece_size, stack_buffer_size)); + } + written = 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))); + if (abort_on_disk && written) return true; } assert(m_free_slots.size() > 0); + return written; } - void piece_manager::allocate_slots(int num_slots) + bool piece_manager::allocate_slots(int num_slots, bool abort_on_disk) { - m_pimpl->allocate_slots(num_slots); + return m_pimpl->allocate_slots(num_slots, abort_on_disk); } path const& piece_manager::save_path() const diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index 8efbff573..7310c7b78 100644 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -29,6 +29,9 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "libtorrent/pch.hpp" + #include #include #include @@ -68,9 +71,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/extensions.hpp" #include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/instantiate_connection.hpp" using namespace libtorrent; -using namespace boost::posix_time; using boost::tuples::tuple; using boost::tuples::get; using boost::tuples::make_tuple; @@ -79,47 +82,6 @@ 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 { @@ -143,12 +105,6 @@ namespace 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; } @@ -226,7 +182,8 @@ namespace libtorrent , tcp::endpoint const& net_interface , bool compact_mode , int block_size - , session_settings const& s) + , session_settings const& s + , storage_constructor_type sc) : m_torrent_file(tf) , m_abort(false) , m_paused(false) @@ -234,16 +191,17 @@ namespace libtorrent , m_event(tracker_request::started) , m_block_size(0) , m_storage(0) - , m_next_request(second_clock::universal_time()) + , m_next_request(time_now()) , m_duration(1800) , m_complete(-1) , m_incomplete(-1) , m_host_resolver(ses.m_io_service) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) , m_resolve_countries(false) -#ifndef TORRENT_DISABLE_DHT - , m_dht_announce_timer(ses.m_io_service) #endif + , m_announce_timer(ses.m_io_service) + , m_last_dht_announce(time_now() - minutes(15)) , m_policy() , m_ses(ses) , m_checker(checker) @@ -254,6 +212,7 @@ namespace libtorrent , m_failed_trackers(0) , m_time_scaler(0) , m_num_pieces(0) + , m_sequenced_download_threshold(0) , m_got_tracker_response(false) , m_ratio(0.f) , m_total_failed_bytes(0) @@ -264,6 +223,7 @@ namespace libtorrent , m_default_block_size(block_size) , m_connections_initialized(true) , m_settings(s) + , m_storage_constructor(sc) { #ifndef NDEBUG m_initial_done = 0; @@ -293,17 +253,9 @@ namespace libtorrent m_connections_quota.max = std::numeric_limits::max(); m_policy.reset(new policy(this)); init(); - -#ifndef TORRENT_DISABLE_DHT - if (should_announce_dht()) - { - m_dht_announce_timer.expires_from_now(seconds(10)); - m_dht_announce_timer.async_wait(m_ses.m_strand.wrap( - bind(&torrent::on_dht_announce, this, _1))); - } -#endif } + torrent::torrent( session_impl& ses , aux::checker_impl& checker @@ -314,7 +266,8 @@ namespace libtorrent , tcp::endpoint const& net_interface , bool compact_mode , int block_size - , session_settings const& s) + , session_settings const& s + , storage_constructor_type sc) : m_torrent_file(info_hash) , m_abort(false) , m_paused(false) @@ -322,16 +275,17 @@ namespace libtorrent , m_event(tracker_request::started) , m_block_size(0) , m_storage(0) - , m_next_request(second_clock::universal_time()) + , m_next_request(time_now()) , m_duration(1800) , m_complete(-1) , m_incomplete(-1) , m_host_resolver(ses.m_io_service) +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES , m_resolving_country(false) , m_resolve_countries(false) -#ifndef TORRENT_DISABLE_DHT - , m_dht_announce_timer(ses.m_io_service) #endif + , m_announce_timer(ses.m_io_service) + , m_last_dht_announce(time_now() - minutes(15)) , m_policy() , m_ses(ses) , m_checker(checker) @@ -341,6 +295,7 @@ namespace libtorrent , m_failed_trackers(0) , m_time_scaler(0) , m_num_pieces(0) + , m_sequenced_download_threshold(0) , m_got_tracker_response(false) , m_ratio(0.f) , m_total_failed_bytes(0) @@ -351,6 +306,7 @@ namespace libtorrent , m_default_block_size(block_size) , m_connections_initialized(false) , m_settings(s) + , m_storage_constructor(sc) { #ifndef NDEBUG m_initial_done = 0; @@ -384,14 +340,14 @@ namespace libtorrent } m_policy.reset(new policy(this)); -#ifndef TORRENT_DISABLE_DHT - if (should_announce_dht()) - { - m_dht_announce_timer.expires_from_now(seconds(10)); - m_dht_announce_timer.async_wait(m_ses.m_strand.wrap( - bind(&torrent::on_dht_announce, this, _1))); - } -#endif + } + + void torrent::start() + { + boost::weak_ptr self(shared_from_this()); + m_announce_timer.expires_from_now(seconds(1)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); } #ifndef TORRENT_DISABLE_DHT @@ -450,7 +406,8 @@ namespace libtorrent 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_ses.m_files)); + m_storage.reset(new piece_manager(m_torrent_file, m_save_path + , m_ses.m_files, m_storage_constructor)); 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) @@ -468,6 +425,53 @@ namespace libtorrent m_net_interface = tcp::endpoint(address::from_string(net_interface), 0); } + void torrent::on_announce_disp(boost::weak_ptr p + , asio::error_code const& e) + { + if (e) return; + boost::shared_ptr t = p.lock(); + if (!t) return; + t->on_announce(); + } + + void torrent::on_announce() +#ifndef NDEBUG + try +#endif + { + boost::weak_ptr self(shared_from_this()); + + // announce on local network every 5 minutes + m_announce_timer.expires_from_now(minutes(5)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); + + // announce with the local discovery service + m_ses.announce_lsd(m_torrent_file.info_hash()); + +#ifndef TORRENT_DISABLE_DHT + if (!m_ses.m_dht) return; + ptime now = time_now(); + if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) + { + m_last_dht_announce = now; + // TODO: There should be a way to abort an announce operation on the dht. + // when the torrent is destructed + assert(m_ses.m_external_listen_port > 0); + m_ses.m_dht->announce(m_torrent_file.info_hash() + , m_ses.m_external_listen_port + , m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1))); + } +#endif + } +#ifndef NDEBUG + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + assert(false); + }; +#endif + #ifndef TORRENT_DISABLE_DHT void torrent::on_dht_announce_response_disp(boost::weak_ptr t @@ -478,24 +482,6 @@ namespace libtorrent tor->on_dht_announce_response(peers); } - void torrent::on_dht_announce(asio::error_code const& e) - { - if (e) return; - if (should_announce_dht()) - { - m_dht_announce_timer.expires_from_now(boost::posix_time::minutes(30)); - m_dht_announce_timer.async_wait(m_ses.m_strand.wrap( - 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() - , m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1))); - } - void torrent::on_dht_announce_response(std::vector const& peers) { if (peers.empty()) return; @@ -506,7 +492,8 @@ namespace libtorrent get_handle(), peers.size(), "Got peers from DHT")); } std::for_each(peers.begin(), peers.end(), bind( - &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0))); + &policy::peer_from_tracker, boost::ref(m_policy), _1, peer_id(0) + , peer_info::dht, 0)); } #endif @@ -524,8 +511,7 @@ namespace libtorrent m_just_paused = false; return true; } - return !m_paused && - m_next_request < second_clock::universal_time(); + return !m_paused && m_next_request < time_now(); } void torrent::tracker_warning(std::string const& msg) @@ -561,7 +547,7 @@ namespace libtorrent m_currently_trying_tracker = 0; m_duration = interval; - m_next_request = second_clock::universal_time() + boost::posix_time::seconds(m_duration); + m_next_request = time_now() + seconds(m_duration); if (complete >= 0) m_complete = complete; if (incomplete >= 0) m_incomplete = incomplete; @@ -601,10 +587,16 @@ namespace libtorrent #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) debug_log("blocked ip from tracker: " + i->ip); #endif + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "peer from tracker blocked by IP filter")); + } + continue; } - m_policy->peer_from_tracker(a, i->pid); + m_policy->peer_from_tracker(a, i->pid, peer_info::tracker, 0); } catch (std::exception&) { @@ -616,6 +608,7 @@ namespace libtorrent bind(&torrent::on_peer_name_lookup, shared_from_this(), _1, _2, i->pid))); } } + m_policy->pulse(); if (m_ses.m_alerts.should_post(alert::info)) { @@ -643,10 +636,16 @@ namespace libtorrent #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) debug_log("blocked ip from tracker: " + host->endpoint().address().to_string()); #endif + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(host->endpoint().address() + , "peer from tracker blocked by IP filter")); + } + return; } - m_policy->peer_from_tracker(*host, pid); + m_policy->peer_from_tracker(*host, pid, peer_info::tracker, 0); } catch (std::exception&) {}; @@ -695,10 +694,9 @@ namespace libtorrent { INVARIANT_CHECK; - if (!valid_metadata()) return tuple(0,0); - - if (m_torrent_file.num_pieces() == 0) + if (!valid_metadata() || m_torrent_file.num_pieces() == 0) return tuple(0,0); + const int last_piece = m_torrent_file.num_pieces() - 1; if (is_seed()) @@ -720,7 +718,7 @@ namespace libtorrent int corr = m_torrent_file.piece_size(last_piece) - m_torrent_file.piece_length(); total_done += corr; - if (!m_picker->is_filtered(last_piece)) + if (m_picker->piece_priority(last_piece) != 0) wanted_done += corr; } @@ -739,8 +737,7 @@ namespace libtorrent int corr = 0; int index = i->index; assert(!m_have_pieces[index]); - assert(int(i->finished_blocks.count()) - < m_picker->blocks_in_piece(index)); + assert(i->finished < m_picker->blocks_in_piece(index)); #ifndef NDEBUG for (std::vector::const_iterator j = boost::next(i); @@ -752,23 +749,23 @@ namespace libtorrent for (int j = 0; j < blocks_per_piece; ++j) { - assert(i->finished_blocks[j] == 0 || i->finished_blocks[j] == 1); - assert(m_picker->is_finished(piece_block(index, j)) == i->finished_blocks[j]); - corr += i->finished_blocks[j] * m_block_size; + assert(i->info[j].finished == 0 || i->info[j].finished == 1); + assert(m_picker->is_finished(piece_block(index, j)) == i->info[j].finished); + corr += i->info[j].finished * m_block_size; assert(index != last_piece || j < m_picker->blocks_in_last_piece() - || i->finished_blocks[j] == 0); + || i->info[j].finished == 0); } // 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]) + && i->info[m_picker->blocks_in_last_piece()-1].finished) { corr -= m_block_size; corr += m_torrent_file.piece_size(last_piece) % m_block_size; } total_done += corr; - if (!m_picker->is_filtered(index)) + if (m_picker->piece_priority(index) != 0) wanted_done += corr; } @@ -816,7 +813,7 @@ namespace libtorrent i != downloading_piece.end(); ++i) { total_done += i->second; - if (!m_picker->is_filtered(i->first.piece_index)) + if (m_picker->piece_priority(i->first.piece_index) != 0) wanted_done += i->second; } @@ -837,7 +834,7 @@ namespace libtorrent std::cerr << " " << i->index << " "; for (int j = 0; j < blocks_per_piece; ++j) { - std::cerr << i->finished_blocks[j]; + std::cerr << i->info[j].finished; } std::cerr << std::endl; } @@ -923,7 +920,22 @@ namespace libtorrent , get_handle() , "banning peer because of too many corrupt pieces")); } - m_policy->ban_peer(*p->second); + + // mark the peer as banned + policy::peer* peerinfo = p->second->peer_info_struct(); + if (peerinfo) + { + peerinfo->banned = true; + } + else + { + // it might be a web seed + if (web_peer_connection const* wpc + = dynamic_cast(p->second)) + { + remove_url_seed(wpc->url()); + } + } #if defined(TORRENT_VERBOSE_LOGGING) (*p->second->m_logger) << "*** BANNING PEER 'too many corrupt pieces'\n"; @@ -1003,7 +1015,11 @@ namespace libtorrent try { (*i)->on_piece_pass(index); } catch (std::exception&) {} } #endif - if (is_seed()) m_picker.reset(); + if (is_seed()) + { + m_picker.reset(); + m_torrent_file.seed_free(); + } } std::string torrent::tracker_login() const @@ -1012,6 +1028,133 @@ namespace libtorrent return m_username + ":" + m_password; } + + + void torrent::set_piece_priority(int index, int priority) + { + INVARIANT_CHECK; + + assert(valid_metadata()); + if (is_seed()) return; + + // this call is only valid on torrents with metadata + assert(m_picker.get()); + assert(index >= 0); + assert(index < m_torrent_file.num_pieces()); + + m_picker->set_piece_priority(index, priority); + update_peer_interest(); + } + + int torrent::piece_priority(int index) const + { + INVARIANT_CHECK; + + assert(valid_metadata()); + if (is_seed()) return 1; + + // 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->piece_priority(index); + } + + void torrent::prioritize_pieces(std::vector const& pieces) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) return; + + assert(m_picker.get()); + + int index = 0; + for (std::vector::const_iterator i = pieces.begin() + , end(pieces.end()); i != end; ++i, ++index) + { + assert(*i >= 0); + assert(*i <= 7); + m_picker->set_piece_priority(index, *i); + } + update_peer_interest(); + } + + void torrent::piece_priorities(std::vector& pieces) const + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + assert(valid_metadata()); + if (is_seed()) + { + pieces.clear(); + pieces.resize(m_torrent_file.num_pieces(), 1); + return; + } + + assert(m_picker.get()); + m_picker->piece_priorities(pieces); + } + + namespace + { + void set_if_greater(int& piece_prio, int file_prio) + { + if (file_prio > piece_prio) piece_prio = file_prio; + } + } + + void torrent::prioritize_files(std::vector const& files) + { + INVARIANT_CHECK; + + // this call is only valid on torrents with metadata + if (!valid_metadata() || is_seed()) return; + + // the bitmask need to have exactly one bit for every file + // in the torrent + assert(int(files.size()) == m_torrent_file.num_files()); + + size_type position = 0; + + if (m_torrent_file.num_pieces() == 0) return; + + int piece_length = m_torrent_file.piece_length(); + // initialize the piece priorities to 0, then only allow + // setting higher priorities + std::vector pieces(m_torrent_file.num_pieces(), 0); + for (int i = 0; i < int(files.size()); ++i) + { + size_type start = position; + size_type size = m_torrent_file.file_at(i).size; + if (size == 0) continue; + position += size; + // mark all pieces of the file with this file's priority + // but only if the priority is higher than the pieces + // already set (to avoid problems with overlapping pieces) + int start_piece = int(start / piece_length); + int last_piece = int((position - 1) / piece_length); + assert(last_piece <= int(pieces.size())); + // if one piece spans several files, we might + // come here several times with the same start_piece, end_piece + std::for_each(pieces.begin() + start_piece + , pieces.begin() + last_piece + 1 + , bind(&set_if_greater, _1, files[i])); + } + prioritize_pieces(pieces); + update_peer_interest(); + } + + // updates the interested flag in peers + void torrent::update_peer_interest() + { + for (peer_iterator i = begin(); i != end(); ++i) + i->second->update_interest(); + } + void torrent::filter_piece(int index, bool filter) { INVARIANT_CHECK; @@ -1024,10 +1167,8 @@ namespace libtorrent 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); + m_picker->set_piece_priority(index, filter ? 1 : 0); + update_peer_interest(); } void torrent::filter_pieces(std::vector const& bitmask) @@ -1040,26 +1181,17 @@ namespace libtorrent 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 ((m_picker->piece_priority(index) == 0) == *i) continue; if (*i) - m_picker->mark_as_filtered(index); + m_picker->set_piece_priority(index, 0); else - state.push_back(index); - } - - for (std::vector::reverse_iterator i = state.rbegin(); - i != state.rend(); ++i) - { - m_picker->mark_as_unfiltered(*i); + m_picker->set_piece_priority(index, 1); } + update_peer_interest(); } bool torrent::is_piece_filtered(int index) const @@ -1072,7 +1204,7 @@ namespace libtorrent assert(index >= 0); assert(index < m_torrent_file.num_pieces()); - return m_picker->is_filtered(index); + return m_picker->piece_priority(index) == 0; } void torrent::filtered_pieces(std::vector& bitmask) const @@ -1146,15 +1278,12 @@ namespace libtorrent assert(!m_trackers.empty()); - m_next_request - = second_clock::universal_time() - + boost::posix_time::seconds(tracker_retry_delay_max); + m_next_request = time_now() + 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.web_downloaded = m_web_stat.total_payload_download(); req.uploaded = m_stat.total_payload_upload(); req.left = bytes_left(); if (req.left == -1) req.left = 16*1024; @@ -1183,29 +1312,42 @@ namespace libtorrent assert(p != 0); peer_iterator i = m_connections.find(p->remote()); - if (i == m_connections.end()) return; + if (i == m_connections.end()) + { + assert(false); + 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 (p->is_seed()) { - if (*i) piece_list.push_back(static_cast(i - pieces.begin())); + if (m_picker.get()) + { + assert(!is_seed()); + m_picker->dec_refcount_all(); + } } - - for (std::vector::reverse_iterator i = piece_list.rbegin(); - i != piece_list.rend(); ++i) + else { - peer_lost(*i); + // if we're a seed, we don't keep track of piece availability + if (!is_seed()) + { + const std::vector& pieces = p->get_bitfield(); + + for (std::vector::const_iterator i = pieces.begin(); + i != pieces.end(); ++i) + { + if (*i) peer_lost(static_cast(i - pieces.begin())); + } + } } } m_policy->connection_closed(*p); + p->set_peer_info(0); m_connections.erase(i); #ifndef NDEBUG m_policy->check_invariant(); @@ -1224,33 +1366,37 @@ namespace libtorrent 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"; + (*m_ses.m_logger) << time_now_string() << " resolving: " << url << "\n"; #endif m_resolving_web_seeds.insert(url); - if (m_ses.settings().proxy_ip.empty()) + proxy_settings const& ps = m_ses.web_seed_proxy(); + if (ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw) + { + // use proxy + tcp::resolver::query q(ps.hostname + , boost::lexical_cast(ps.port)); + m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( + bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, url))); + } + else { std::string protocol; + std::string auth; std::string hostname; int port; std::string path; - boost::tie(protocol, hostname, port, path) + boost::tie(protocol, auth, hostname, port, path) = parse_url_components(url); + // TODO: should auth be used here? + tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url , tcp::endpoint()))); } - 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, m_ses.m_strand.wrap( - bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, url))); - } } @@ -1262,8 +1408,7 @@ namespace libtorrent 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 proxy hostname for: " << url << "\n"; + (*m_ses.m_logger) << time_now_string() << " completed resolve proxy hostname for: " << url << "\n"; #endif if (e || host == tcp::resolver::iterator()) @@ -1286,19 +1431,22 @@ namespace libtorrent tcp::endpoint a(host->endpoint()); - if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) - { - // TODO: post alert: "proxy at " + a.address().to_string() + " (" + hostname + ") blocked by ip filter"); - return; - } - - std::string protocol; + using boost::tuples::ignore; std::string hostname; int port; - std::string path; - boost::tie(protocol, hostname, port, path) + boost::tie(ignore, ignore, hostname, port, ignore) = parse_url_components(url); + if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) + { + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "proxy (" + hostname + ") blocked by IP filter")); + } + return; + } + tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, m_ses.m_strand.wrap( bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url, a))); @@ -1316,8 +1464,7 @@ namespace libtorrent 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"; + (*m_ses.m_logger) << time_now_string() << " completed resolve: " << url << "\n"; #endif std::set::iterator i = m_resolving_web_seeds.find(url); @@ -1348,7 +1495,11 @@ namespace libtorrent if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) { - // TODO: post alert: "web seed at " + a.address().to_string() + " blocked by ip filter"); + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(peer_blocked_alert(a.address() + , "web seed (" + url + ") blocked by IP filter")); + } return; } @@ -1360,32 +1511,44 @@ namespace libtorrent else return; } - boost::shared_ptr s(new stream_socket(m_ses.m_io_service)); + boost::shared_ptr s + = instantiate_connection(m_ses.m_io_service, m_ses.web_seed_proxy()); + if (m_ses.web_seed_proxy().type == proxy_settings::http + || m_ses.web_seed_proxy().type == proxy_settings::http_pw) + { + // the web seed connection will talk immediately to + // the proxy, without requiring CONNECT support + s->get().set_no_connect(true); + } boost::intrusive_ptr c(new web_peer_connection( - m_ses, shared_from_this(), s, a, proxy, url)); + m_ses, shared_from_this(), s, a, url, 0)); #ifndef NDEBUG c->m_in_constructor = false; #endif +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + boost::shared_ptr pp((*i)->new_connection(c.get())); + if (pp) c->add_extension(pp); + } +#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))); + m_ses.m_connections.insert(std::make_pair(s, c)); -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - - m_ses.process_connection_queue(); + m_ses.m_half_open.enqueue( + bind(&peer_connection::connect, c, _1) + , bind(&peer_connection::timed_out, c) + , seconds(settings().peer_connect_timeout)); } catch (std::exception& e) { @@ -1402,9 +1565,13 @@ namespace libtorrent } catch (std::exception& exc) { +#ifndef NDEBUG + std::cerr << exc.what() << std::endl; +#endif assert(false); }; +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void torrent::resolve_peer_country(boost::intrusive_ptr const& p) const { if (m_resolving_country @@ -1422,12 +1589,11 @@ namespace libtorrent namespace { - typedef std::pair country_entry; - - bool compare_first(country_entry const& lhs, country_entry const& rhs) + struct country_entry { - return lhs.first < rhs.first; - } + int code; + char const* name; + }; } void torrent::on_country_lookup(asio::error_code const& error, tcp::resolver::iterator i @@ -1442,246 +1608,54 @@ namespace libtorrent // must be ordered in increasing order country_entry country_map[] = { - country_entry( 4, "AF") - , country_entry( 8, "AL") - , country_entry( 10, "AQ") - , country_entry( 12, "DZ") - , country_entry( 16, "AS") - , country_entry( 20, "AD") - , country_entry( 24, "AO") - , country_entry( 28, "AG") - , country_entry( 31, "AZ") - , country_entry( 32, "AR") - , country_entry( 36, "AU") - , country_entry( 40, "AT") - , country_entry( 44, "BS") - , country_entry( 48, "BH") - , country_entry( 50, "BD") - , country_entry( 51, "AM") - , country_entry( 52, "BB") - , country_entry( 56, "BE") - , country_entry( 60, "BM") - , country_entry( 64, "BT") - , country_entry( 68, "BO") - , country_entry( 70, "BA") - , country_entry( 72, "BW") - , country_entry( 74, "BV") - , country_entry( 76, "BR") - , country_entry( 84, "BZ") - , country_entry( 86, "IO") - , country_entry( 90, "SB") - , country_entry( 92, "VG") - , country_entry( 96, "BN") - , country_entry(100, "BG") - , country_entry(104, "MM") - , country_entry(108, "BI") - , country_entry(112, "BY") - , country_entry(116, "KH") - , country_entry(120, "CM") - , country_entry(124, "CA") - , country_entry(132, "CV") - , country_entry(136, "KY") - , country_entry(140, "CF") - , country_entry(144, "LK") - , country_entry(148, "TD") - , country_entry(152, "CL") - , country_entry(156, "CN") - , country_entry(158, "TW") - , country_entry(162, "CX") - , country_entry(166, "CC") - , country_entry(170, "CO") - , country_entry(174, "KM") - , country_entry(175, "YT") - , country_entry(178, "CG") - , country_entry(180, "CD") - , country_entry(184, "CK") - , country_entry(188, "CR") - , country_entry(191, "HR") - , country_entry(192, "CU") - , country_entry(203, "CZ") - , country_entry(204, "BJ") - , country_entry(208, "DK") - , country_entry(212, "DM") - , country_entry(214, "DO") - , country_entry(218, "EC") - , country_entry(222, "SV") - , country_entry(226, "GQ") - , country_entry(231, "ET") - , country_entry(232, "ER") - , country_entry(233, "EE") - , country_entry(234, "FO") - , country_entry(238, "FK") - , country_entry(239, "GS") - , country_entry(242, "FJ") - , country_entry(246, "FI") - , country_entry(248, "AX") - , country_entry(250, "FR") - , country_entry(254, "GF") - , country_entry(258, "PF") - , country_entry(260, "TF") - , country_entry(262, "DJ") - , country_entry(266, "GA") - , country_entry(268, "GE") - , country_entry(270, "GM") - , country_entry(275, "PS") - , country_entry(276, "DE") - , country_entry(288, "GH") - , country_entry(292, "GI") - , country_entry(296, "KI") - , country_entry(300, "GR") - , country_entry(304, "GL") - , country_entry(308, "GD") - , country_entry(312, "GP") - , country_entry(316, "GU") - , country_entry(320, "GT") - , country_entry(324, "GN") - , country_entry(328, "GY") - , country_entry(332, "HT") - , country_entry(334, "HM") - , country_entry(336, "VA") - , country_entry(340, "HN") - , country_entry(344, "HK") - , country_entry(348, "HU") - , country_entry(352, "IS") - , country_entry(356, "IN") - , country_entry(360, "ID") - , country_entry(364, "IR") - , country_entry(368, "IQ") - , country_entry(372, "IE") - , country_entry(376, "IL") - , country_entry(380, "IT") - , country_entry(384, "CI") - , country_entry(388, "JM") - , country_entry(392, "JP") - , country_entry(398, "KZ") - , country_entry(400, "JO") - , country_entry(404, "KE") - , country_entry(408, "KP") - , country_entry(410, "KR") - , country_entry(414, "KW") - , country_entry(417, "KG") - , country_entry(418, "LA") - , country_entry(422, "LB") - , country_entry(426, "LS") - , country_entry(428, "LV") - , country_entry(430, "LR") - , country_entry(434, "LY") - , country_entry(438, "LI") - , country_entry(440, "LT") - , country_entry(442, "LU") - , country_entry(446, "MO") - , country_entry(450, "MG") - , country_entry(454, "MW") - , country_entry(458, "MY") - , country_entry(462, "MV") - , country_entry(466, "ML") - , country_entry(470, "MT") - , country_entry(474, "MQ") - , country_entry(478, "MR") - , country_entry(480, "MU") - , country_entry(484, "MX") - , country_entry(492, "MC") - , country_entry(496, "MN") - , country_entry(498, "MD") - , country_entry(500, "MS") - , country_entry(504, "MA") - , country_entry(508, "MZ") - , country_entry(512, "OM") - , country_entry(516, "NA") - , country_entry(520, "NR") - , country_entry(524, "NP") - , country_entry(528, "NL") - , country_entry(530, "AN") - , country_entry(533, "AW") - , country_entry(540, "NC") - , country_entry(548, "VU") - , country_entry(554, "NZ") - , country_entry(558, "NI") - , country_entry(562, "NE") - , country_entry(566, "NG") - , country_entry(570, "NU") - , country_entry(574, "NF") - , country_entry(578, "NO") - , country_entry(580, "MP") - , country_entry(581, "UM") - , country_entry(583, "FM") - , country_entry(584, "MH") - , country_entry(585, "PW") - , country_entry(586, "PK") - , country_entry(591, "PA") - , country_entry(598, "PG") - , country_entry(600, "PY") - , country_entry(604, "PE") - , country_entry(608, "PH") - , country_entry(612, "PN") - , country_entry(616, "PL") - , country_entry(620, "PT") - , country_entry(624, "GW") - , country_entry(626, "TL") - , country_entry(630, "PR") - , country_entry(634, "QA") - , country_entry(634, "QA") - , country_entry(638, "RE") - , country_entry(642, "RO") - , country_entry(643, "RU") - , country_entry(646, "RW") - , country_entry(654, "SH") - , country_entry(659, "KN") - , country_entry(660, "AI") - , country_entry(662, "LC") - , country_entry(666, "PM") - , country_entry(670, "VC") - , country_entry(674, "SM") - , country_entry(678, "ST") - , country_entry(682, "SA") - , country_entry(686, "SN") - , country_entry(690, "SC") - , country_entry(694, "SL") - , country_entry(702, "SG") - , country_entry(703, "SK") - , country_entry(704, "VN") - , country_entry(705, "SI") - , country_entry(706, "SO") - , country_entry(710, "ZA") - , country_entry(716, "ZW") - , country_entry(724, "ES") - , country_entry(732, "EH") - , country_entry(736, "SD") - , country_entry(740, "SR") - , country_entry(744, "SJ") - , country_entry(748, "SZ") - , country_entry(752, "SE") - , country_entry(756, "CH") - , country_entry(760, "SY") - , country_entry(762, "TJ") - , country_entry(764, "TH") - , country_entry(768, "TG") - , country_entry(772, "TK") - , country_entry(776, "TO") - , country_entry(780, "TT") - , country_entry(784, "AE") - , country_entry(788, "TN") - , country_entry(792, "TR") - , country_entry(795, "TM") - , country_entry(796, "TC") - , country_entry(798, "TV") - , country_entry(800, "UG") - , country_entry(804, "UA") - , country_entry(807, "MK") - , country_entry(818, "EG") - , country_entry(826, "GB") - , country_entry(834, "TZ") - , country_entry(840, "US") - , country_entry(850, "VI") - , country_entry(854, "BF") - , country_entry(858, "UY") - , country_entry(860, "UZ") - , country_entry(862, "VE") - , country_entry(876, "WF") - , country_entry(882, "WS") - , country_entry(887, "YE") - , country_entry(891, "CS") - , country_entry(894, "ZM") + { 4, "AF"}, { 8, "AL"}, { 10, "AQ"}, { 12, "DZ"}, { 16, "AS"} + , { 20, "AD"}, { 24, "AO"}, { 28, "AG"}, { 31, "AZ"}, { 32, "AR"} + , { 36, "AU"}, { 40, "AT"}, { 44, "BS"}, { 48, "BH"}, { 50, "BD"} + , { 51, "AM"}, { 52, "BB"}, { 56, "BE"}, { 60, "BM"}, { 64, "BT"} + , { 68, "BO"}, { 70, "BA"}, { 72, "BW"}, { 74, "BV"}, { 76, "BR"} + , { 84, "BZ"}, { 86, "IO"}, { 90, "SB"}, { 92, "VG"}, { 96, "BN"} + , {100, "BG"}, {104, "MM"}, {108, "BI"}, {112, "BY"}, {116, "KH"} + , {120, "CM"}, {124, "CA"}, {132, "CV"}, {136, "KY"}, {140, "CF"} + , {144, "LK"}, {148, "TD"}, {152, "CL"}, {156, "CN"}, {158, "TW"} + , {162, "CX"}, {166, "CC"}, {170, "CO"}, {174, "KM"}, {175, "YT"} + , {178, "CG"}, {180, "CD"}, {184, "CK"}, {188, "CR"}, {191, "HR"} + , {192, "CU"}, {203, "CZ"}, {204, "BJ"}, {208, "DK"}, {212, "DM"} + , {214, "DO"}, {218, "EC"}, {222, "SV"}, {226, "GQ"}, {231, "ET"} + , {232, "ER"}, {233, "EE"}, {234, "FO"}, {238, "FK"}, {239, "GS"} + , {242, "FJ"}, {246, "FI"}, {248, "AX"}, {250, "FR"}, {254, "GF"} + , {258, "PF"}, {260, "TF"}, {262, "DJ"}, {266, "GA"}, {268, "GE"} + , {270, "GM"}, {275, "PS"}, {276, "DE"}, {288, "GH"}, {292, "GI"} + , {296, "KI"}, {300, "GR"}, {304, "GL"}, {308, "GD"}, {312, "GP"} + , {316, "GU"}, {320, "GT"}, {324, "GN"}, {328, "GY"}, {332, "HT"} + , {334, "HM"}, {336, "VA"}, {340, "HN"}, {344, "HK"}, {348, "HU"} + , {352, "IS"}, {356, "IN"}, {360, "ID"}, {364, "IR"}, {368, "IQ"} + , {372, "IE"}, {376, "IL"}, {380, "IT"}, {384, "CI"}, {388, "JM"} + , {392, "JP"}, {398, "KZ"}, {400, "JO"}, {404, "KE"}, {408, "KP"} + , {410, "KR"}, {414, "KW"}, {417, "KG"}, {418, "LA"}, {422, "LB"} + , {426, "LS"}, {428, "LV"}, {430, "LR"}, {434, "LY"}, {438, "LI"} + , {440, "LT"}, {442, "LU"}, {446, "MO"}, {450, "MG"}, {454, "MW"} + , {458, "MY"}, {462, "MV"}, {466, "ML"}, {470, "MT"}, {474, "MQ"} + , {478, "MR"}, {480, "MU"}, {484, "MX"}, {492, "MC"}, {496, "MN"} + , {498, "MD"}, {500, "MS"}, {504, "MA"}, {508, "MZ"}, {512, "OM"} + , {516, "NA"}, {520, "NR"}, {524, "NP"}, {528, "NL"}, {530, "AN"} + , {533, "AW"}, {540, "NC"}, {548, "VU"}, {554, "NZ"}, {558, "NI"} + , {562, "NE"}, {566, "NG"}, {570, "NU"}, {574, "NF"}, {578, "NO"} + , {580, "MP"}, {581, "UM"}, {583, "FM"}, {584, "MH"}, {585, "PW"} + , {586, "PK"}, {591, "PA"}, {598, "PG"}, {600, "PY"}, {604, "PE"} + , {608, "PH"}, {612, "PN"}, {616, "PL"}, {620, "PT"}, {624, "GW"} + , {626, "TL"}, {630, "PR"}, {634, "QA"}, {634, "QA"}, {638, "RE"} + , {642, "RO"}, {643, "RU"}, {646, "RW"}, {654, "SH"}, {659, "KN"} + , {660, "AI"}, {662, "LC"}, {666, "PM"}, {670, "VC"}, {674, "SM"} + , {678, "ST"}, {682, "SA"}, {686, "SN"}, {690, "SC"}, {694, "SL"} + , {702, "SG"}, {703, "SK"}, {704, "VN"}, {705, "SI"}, {706, "SO"} + , {710, "ZA"}, {716, "ZW"}, {724, "ES"}, {732, "EH"}, {736, "SD"} + , {740, "SR"}, {744, "SJ"}, {748, "SZ"}, {752, "SE"}, {756, "CH"} + , {760, "SY"}, {762, "TJ"}, {764, "TH"}, {768, "TG"}, {772, "TK"} + , {776, "TO"}, {780, "TT"}, {784, "AE"}, {788, "TN"}, {792, "TR"} + , {795, "TM"}, {796, "TC"}, {798, "TV"}, {800, "UG"}, {804, "UA"} + , {807, "MK"}, {818, "EG"}, {826, "GB"}, {834, "TZ"}, {840, "US"} + , {850, "VI"}, {854, "BF"}, {858, "UY"}, {860, "UZ"}, {862, "VE"} + , {876, "WF"}, {882, "WS"}, {887, "YE"}, {891, "CS"}, {894, "ZM"} }; if (error || i == tcp::resolver::iterator()) @@ -1701,11 +1675,12 @@ namespace libtorrent // look up the country code in the map const int size = sizeof(country_map)/sizeof(country_map[0]); + country_entry tmp = {country, ""}; country_entry* i = - std::lower_bound(country_map, country_map + size - , country_entry(country, ""), &compare_first); + std::lower_bound(country_map, country_map + size, tmp + , bind(&country_entry::code, _1) < bind(&country_entry::code, _2)); if (i == country_map + size - || i->first != country) + || i->code != country) { // unknown country! p->set_country("!!"); @@ -1715,23 +1690,35 @@ namespace libtorrent return; } - p->set_country(i->second); + p->set_country(i->name); } } +#endif - peer_connection& torrent::connect_to_peer(const tcp::endpoint& a) + peer_connection* torrent::connect_to_peer(policy::peer* peerinfo) { INVARIANT_CHECK; - if (m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) - throw protocol_error(a.address().to_string() + " blocked by ip filter"); + assert(peerinfo); + assert(peerinfo->connection == 0); +#ifndef NDEBUG + // this asserts that we don't have duplicates in the policy's peer list + peer_iterator i_ = m_connections.find(peerinfo->ip); + assert(i_ == m_connections.end() + || i_->second->is_disconnecting() + || dynamic_cast(i_->second) == 0 + || m_ses.settings().allow_multiple_connections_per_ip); +#endif - if (m_connections.find(a) != m_connections.end()) - throw protocol_error("already connected to peer"); + assert(want_more_peers()); - boost::shared_ptr s(new stream_socket(m_ses.m_io_service)); + tcp::endpoint const& a(peerinfo->ip); + assert((m_ses.m_ip_filter.access(a.address()) & ip_filter::blocked) == 0); + + boost::shared_ptr s + = instantiate_connection(m_ses.m_io_service, m_ses.peer_proxy()); boost::intrusive_ptr c(new bt_peer_connection( - m_ses, shared_from_this(), s, a)); + m_ses, shared_from_this(), s, a, peerinfo)); #ifndef NDEBUG c->m_in_constructor = false; @@ -1748,22 +1735,15 @@ namespace libtorrent 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))); + m_ses.m_connections.insert(std::make_pair(s, c)); -#ifndef NDEBUG - m_policy->check_invariant(); -#endif - - m_ses.process_connection_queue(); + m_ses.m_half_open.enqueue( + bind(&peer_connection::connect, c, _1) + , bind(&peer_connection::timed_out, c) + , seconds(settings().peer_connect_timeout)); } catch (std::exception& e) { @@ -1775,7 +1755,7 @@ namespace libtorrent throw; } if (c->is_disconnecting()) throw protocol_error("failed to connect"); - return *c; + return c.get(); } void torrent::set_metadata(entry const& metadata) @@ -1863,14 +1843,19 @@ namespace libtorrent m_connections.erase(ci); throw; } - assert((p->proxy() == tcp::endpoint() && p->remote() == p->get_socket()->remote_endpoint()) - || p->proxy() == p->get_socket()->remote_endpoint()); + assert(p->remote() == p->get_socket()->remote_endpoint()); #ifndef NDEBUG m_policy->check_invariant(); #endif } + bool torrent::want_more_peers() const + { + return int(m_connections.size()) < m_connections_quota.given + && m_ses.m_half_open.free_slots(); + } + void torrent::disconnect_all() { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); @@ -2025,7 +2010,6 @@ namespace libtorrent { INVARIANT_CHECK; - using namespace boost::posix_time; ++m_currently_trying_tracker; if ((unsigned)m_currently_trying_tracker >= m_trackers.size()) @@ -2038,17 +2022,18 @@ namespace libtorrent ++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); + m_next_request = time_now() + seconds(delay); #ifndef TORRENT_DISABLE_DHT - // only start the dht announce unless we already are already running - // the announce timer (a positive expiration time indicates - // that it's running) - if (m_dht_announce_timer.expires_from_now().is_negative() && should_announce_dht()) + // only start the announce if we want to announce with the dht + if (should_announce_dht()) { - m_dht_announce_timer.expires_from_now(boost::posix_time::seconds(1)); - m_dht_announce_timer.async_wait(m_ses.m_strand.wrap( - bind(&torrent::on_dht_announce, this, _1))); + // force the DHT to reannounce + m_last_dht_announce = time_now() - minutes(15); + boost::weak_ptr self(shared_from_this()); + m_announce_timer.expires_from_now(seconds(1)); + m_announce_timer.async_wait(m_ses.m_strand.wrap( + bind(&torrent::on_announce_disp, self, _1))); } #endif @@ -2056,7 +2041,7 @@ namespace libtorrent else { // don't delay before trying the next tracker - m_next_request = second_clock::universal_time(); + m_next_request = time_now(); } } @@ -2144,11 +2129,24 @@ namespace libtorrent if (!is_seed()) { m_picker->files_checked(m_have_pieces, unfinished_pieces); + if (m_sequenced_download_threshold > 0) + picker().set_sequenced_download_threshold(m_sequenced_download_threshold); } - else + +#ifndef TORRENT_DISABLE_EXTENSIONS + for (extension_list_t::iterator i = m_extensions.begin() + , end(m_extensions.end()); i != end; ++i) + { + try { (*i)->on_files_checked(); } catch (std::exception&) {} + } +#endif + + if (is_seed()) { m_picker.reset(); + m_torrent_file.seed_free(); } + if (!m_connections_initialized) { m_connections_initialized = true; @@ -2277,10 +2275,14 @@ namespace libtorrent void torrent::set_sequenced_download_threshold(int threshold) { - // TODO: if there is not valid metadata, save this setting and - // set it once the piece picker is created. - if (valid_metadata() && !is_seed()) + if (has_picker()) + { picker().set_sequenced_download_threshold(threshold); + } + else + { + m_sequenced_download_threshold = threshold; + } } @@ -2322,6 +2324,13 @@ namespace libtorrent m_bandwidth_limit[peer_connection::upload_channel].throttle(limit); } + int torrent::upload_limit() const + { + int limit = m_bandwidth_limit[peer_connection::upload_channel].throttle(); + if (limit == std::numeric_limits::max()) limit = -1; + return limit; + } + void torrent::set_download_limit(int limit) { assert(limit >= -1); @@ -2330,6 +2339,13 @@ namespace libtorrent m_bandwidth_limit[peer_connection::download_channel].throttle(limit); } + int torrent::download_limit() const + { + int limit = m_bandwidth_limit[peer_connection::download_channel].throttle(); + if (limit == std::numeric_limits::max()) limit = -1; + return limit; + } + void torrent::pause() { INVARIANT_CHECK; @@ -2401,7 +2417,6 @@ namespace libtorrent { // let the stats fade out to 0 m_stat.second_tick(tick_interval); - m_web_stat.second_tick(tick_interval); m_connections_quota.min = 0; m_connections_quota.max = 0; m_uploads_quota.min = 0; @@ -2411,8 +2426,8 @@ namespace libtorrent // ---- WEB SEEDS ---- - // if we're a seed, we don't need to connect to any web-seed - if (!is_seed() && !m_web_seeds.empty()) + // if we have everything we want we don't need to connect to any web-seed + if (!is_finished() && !m_web_seeds.empty()) { // keep trying web-seeds if there are any // first find out which web seeds we are connected to @@ -2442,21 +2457,34 @@ namespace libtorrent } for (peer_iterator i = m_connections.begin(); - i != m_connections.end(); ++i) + i != m_connections.end();) { peer_connection* p = i->second; + ++i; m_stat += p->statistics(); - if (dynamic_cast(p)) - { - m_web_stat += p->statistics(); - } // updates the peer connection's ul/dl bandwidth // resource requests - p->second_tick(tick_interval); + try + { + p->second_tick(tick_interval); + } + catch (std::exception& e) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*p->m_logger) << "**ERROR**: " << e.what() << "\n"; +#endif + p->set_failed(); + p->disconnect(); + } } accumulator += m_stat; m_stat.second_tick(tick_interval); - m_web_stat.second_tick(tick_interval); + } + + void torrent::try_connect_peer() + { + assert(want_more_peers()); + m_policy->connect_one_peer(); } void torrent::distribute_resources(float tick_interval) @@ -2579,10 +2607,11 @@ namespace libtorrent 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.next_announce = boost::posix_time::seconds( + total_seconds(next_announce() - time_now())); + 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) @@ -2623,7 +2652,7 @@ namespace libtorrent 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)) + if (m_picker->piece_priority(last_piece_index) == 0) { st.total_wanted -= m_torrent_file.piece_size(last_piece_index); --filtered_pieces; diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index dcc46fd47..da571ab63 100644 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -179,6 +181,13 @@ namespace libtorrent , bind(&torrent::set_upload_limit, _1, limit)); } + int torrent_handle::upload_limit() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::upload_limit, _1)); + } + void torrent_handle::set_download_limit(int limit) const { INVARIANT_CHECK; @@ -189,6 +198,13 @@ namespace libtorrent , bind(&torrent::set_download_limit, _1, limit)); } + int torrent_handle::download_limit() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::download_limit, _1)); + } + bool torrent_handle::move_storage( boost::filesystem::path const& save_path) const { @@ -328,6 +344,57 @@ namespace libtorrent , bind(&torrent::set_sequenced_download_threshold, _1, threshold)); } + std::string torrent_handle::name() const + { + INVARIANT_CHECK; + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::name, _1)); + } + + + void torrent_handle::piece_priority(int index, int priority) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::set_piece_priority, _1, index, priority)); + } + + int torrent_handle::piece_priority(int index) const + { + INVARIANT_CHECK; + + return call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::piece_priority, _1, index)); + } + + void torrent_handle::prioritize_pieces(std::vector const& pieces) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::prioritize_pieces, _1, boost::cref(pieces))); + } + + std::vector torrent_handle::piece_priorities() const + { + INVARIANT_CHECK; + std::vector ret; + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::piece_priorities, _1, boost::ref(ret))); + return ret; + } + + void torrent_handle::prioritize_files(std::vector const& files) const + { + INVARIANT_CHECK; + + call_member(m_ses, m_chk, m_info_hash + , bind(&torrent::prioritize_files, _1, boost::cref(files))); + } + +// ============ start deprecation =============== + void torrent_handle::filter_piece(int index, bool filter) const { INVARIANT_CHECK; @@ -339,7 +406,7 @@ namespace libtorrent { INVARIANT_CHECK; call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::filter_pieces, _1, pieces)); + , bind(&torrent::filter_pieces, _1, boost::cref(pieces))); } bool torrent_handle::is_piece_filtered(int index) const @@ -349,13 +416,6 @@ namespace libtorrent , bind(&torrent::is_piece_filtered, _1, index)); } - std::string torrent_handle::name() const - { - INVARIANT_CHECK; - return call_member(m_ses, m_chk, m_info_hash - , bind(&torrent::name, _1)); - } - std::vector torrent_handle::filtered_pieces() const { INVARIANT_CHECK; @@ -372,6 +432,9 @@ namespace libtorrent , bind(&torrent::filter_files, _1, files)); } +// ============ end deprecation =============== + + std::vector const& torrent_handle::trackers() const { INVARIANT_CHECK; @@ -448,6 +511,8 @@ namespace libtorrent ret["file-format"] = "libtorrent resume file"; ret["file-version"] = 1; + ret["allocation"] = t->filesystem().compact_allocation()?"compact":"full"; + const sha1_hash& info_hash = t->torrent_file().info_hash(); ret["info-hash"] = std::string((char*)info_hash.begin(), (char*)info_hash.end()); @@ -477,7 +542,7 @@ namespace libtorrent for (std::vector::const_iterator i = q.begin(); i != q.end(); ++i) { - if (i->finished_blocks.count() == 0) continue; + if (i->finished == 0) continue; entry piece_struct(entry::dictionary_t); @@ -492,7 +557,7 @@ namespace libtorrent { unsigned char v = 0; for (int k = 0; k < 8; ++k) - v |= i->finished_blocks[j*8+k]?(1 << k):0; + v |= i->info[j*8+k].finished?(1 << k):0; bitmask.insert(bitmask.end(), v); } piece_struct["bitmask"] = bitmask; @@ -502,7 +567,7 @@ namespace libtorrent = t->filesystem().piece_crc( t->filesystem().slot_for_piece(i->index) , t->block_size() - , i->finished_blocks); + , i->info); piece_struct["adler32"] = adler; @@ -537,19 +602,7 @@ namespace libtorrent 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)); - } + t->filesystem().write_resume_data(ret); return ret; } @@ -563,7 +616,7 @@ namespace libtorrent , bind(&torrent::save_path, _1)); } - void torrent_handle::connect_peer(tcp::endpoint const& adr) const + void torrent_handle::connect_peer(tcp::endpoint const& adr, int source) const { INVARIANT_CHECK; @@ -587,7 +640,7 @@ namespace libtorrent peer_id id; std::fill(id.begin(), id.end(), 0); - t->get_policy().peer_from_tracker(adr, id); + t->get_policy().peer_from_tracker(adr, id, source, 0); } void torrent_handle::force_reannounce( @@ -601,9 +654,8 @@ namespace libtorrent 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); + t->force_tracker_request(time_now() + + seconds(duration.total_seconds())); } void torrent_handle::force_reannounce() const @@ -632,6 +684,7 @@ namespace libtorrent , bind(&torrent::set_ratio, _1, ratio)); } +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES void torrent_handle::resolve_countries(bool r) { INVARIANT_CHECK; @@ -645,6 +698,7 @@ namespace libtorrent return call_member(m_ses, m_chk, m_info_hash , bind(&torrent::resolving_countries, _1)); } +#endif void torrent_handle::get_peer_info(std::vector& v) const { @@ -671,8 +725,10 @@ namespace libtorrent peer_info& p = v.back(); peer->get_peer_info(p); +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES if (t->resolving_countries()) t->resolve_peer_country(intrusive_ptr(peer)); +#endif } } @@ -700,15 +756,16 @@ namespace libtorrent = 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.piece_state = (partial_piece_info::state_t)i->state; + pi.blocks_in_piece = p.blocks_in_piece(i->index); + for (int j = 0; j < pi.blocks_in_piece; ++j) { pi.peer[j] = i->info[j].peer; pi.num_downloads[j] = i->info[j].num_downloads; + pi.finished_blocks[j] = i->info[j].finished; + pi.requested_blocks[j] = i->info[j].requested; } pi.piece_index = i->index; - pi.blocks_in_piece = p.blocks_in_piece(i->index); queue.push_back(pi); } } diff --git a/libtorrent/src/torrent_info.cpp b/libtorrent/src/torrent_info.cpp index 238864283..e546a1243 100644 --- a/libtorrent/src/torrent_info.cpp +++ b/libtorrent/src/torrent_info.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -43,7 +45,6 @@ POSSIBILITY OF SUCH DAMAGE. #endif #include -#include #include #include #include @@ -57,6 +58,9 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" +namespace pt = boost::posix_time; +namespace gr = boost::gregorian; + using namespace libtorrent; using namespace boost::filesystem; @@ -213,15 +217,16 @@ namespace 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_num_pieces(0) + , m_creation_date(pt::ptime(pt::not_a_date_time)) , m_multifile(false) , m_private(false) , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif { try { @@ -240,24 +245,32 @@ namespace libtorrent torrent_info::torrent_info(sha1_hash const& info_hash) : m_piece_length(0) , m_total_size(0) + , m_num_pieces(0) , m_info_hash(info_hash) , m_name() - , m_creation_date(second_clock::universal_time()) + , m_creation_date(pt::second_clock::universal_time()) , m_multifile(false) , m_private(false) , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif { } torrent_info::torrent_info() : m_piece_length(0) , m_total_size(0) + , m_num_pieces(0) , m_info_hash(0) , m_name() - , m_creation_date(second_clock::universal_time()) + , m_creation_date(pt::second_clock::universal_time()) , m_multifile(false) , m_private(false) , m_extra_info(entry::dictionary_t) +#ifndef NDEBUG + , m_half_metadata(false) +#endif { } @@ -277,15 +290,15 @@ namespace libtorrent } } #endif + assert(!m_half_metadata); m_piece_length = size; - - int num_pieces = static_cast( + m_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.resize(m_num_pieces); + for (int i = old_num_pieces; i < m_num_pieces; ++i) { m_piece_hash[i].clear(); } @@ -343,14 +356,14 @@ namespace libtorrent // 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); + m_num_pieces = static_cast((m_total_size + m_piece_length - 1) / m_piece_length); + m_piece_hash.resize(m_num_pieces); const std::string& hash_string = info["pieces"].string(); - if ((int)hash_string.length() != num_pieces * 20) + if ((int)hash_string.length() != m_num_pieces * 20) throw invalid_torrent_file(); - for (int i = 0; i < num_pieces; ++i) + for (int i = 0; i < m_num_pieces; ++i) std::copy( hash_string.begin() + i*20 , hash_string.begin() + (i+1)*20 @@ -453,8 +466,8 @@ namespace libtorrent // extract creation date try { - m_creation_date = ptime(date(1970, Jan, 1)) - + seconds(long(torrent_file["creation date"].integer())); + m_creation_date = pt::ptime(gr::date(1970, gr::Jan, 1)) + + pt::seconds(long(torrent_file["creation date"].integer())); } catch (type_error) {} @@ -492,14 +505,14 @@ namespace libtorrent parse_info_section(torrent_file["info"]); } - boost::optional + boost::optional torrent_info::creation_date() const { - if (m_creation_date != ptime(date(not_a_date_time))) + if (m_creation_date != pt::ptime(gr::date(pt::not_a_date_time))) { - return boost::optional(m_creation_date); + return boost::optional(m_creation_date); } - return boost::optional(); + return boost::optional(); } void torrent_info::add_tracker(std::string const& url, int tier) @@ -549,12 +562,12 @@ namespace libtorrent if (m_piece_length == 0) m_piece_length = 256 * 1024; - int num_pieces = static_cast( + m_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); - if (num_pieces > old_num_pieces) + m_piece_hash.resize(m_num_pieces); + if (m_num_pieces > old_num_pieces) std::for_each(m_piece_hash.begin() + old_num_pieces , m_piece_hash.end(), boost::bind(&sha1_hash::clear, _1)); } @@ -637,9 +650,6 @@ namespace libtorrent { assert(m_piece_length > 0); - using namespace boost::gregorian; - using namespace boost::posix_time; - namespace fs = boost::filesystem; if ((m_urls.empty() && m_nodes.empty()) || m_files.empty()) @@ -694,7 +704,7 @@ namespace libtorrent dict["comment"] = m_comment; dict["creation date"] = - (m_creation_date - ptime(date(1970, Jan, 1))).total_seconds(); + (m_creation_date - pt::ptime(gr::date(1970, gr::Jan, 1))).total_seconds(); if (!m_created_by.empty()) dict["created by"] = m_created_by; @@ -738,6 +748,18 @@ namespace libtorrent assert(false); } + void torrent_info::seed_free() + { + std::vector().swap(m_url_seeds); + nodes_t().swap(m_nodes); + std::vector().swap(m_piece_hash); +#ifndef NDEBUG + m_half_metadata = true; +#endif + } + +// ------- start deprecation ------- + void torrent_info::print(std::ostream& os) const { os << "trackers:\n"; @@ -748,8 +770,8 @@ namespace libtorrent } 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"; +// if (m_creation_date != pt::ptime(gr::date(pt::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"; @@ -758,6 +780,8 @@ namespace libtorrent os << " " << std::setw(11) << i->size << " " << i->path.string() << "\n"; } +// ------- end deprecation ------- + size_type torrent_info::piece_size(int index) const { assert(index >= 0 && index < num_pieces()); diff --git a/libtorrent/src/tracker_manager.cpp b/libtorrent/src/tracker_manager.cpp index f27620072..7ee8c57ba 100644 --- a/libtorrent/src/tracker_manager.cpp +++ b/libtorrent/src/tracker_manager.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -79,11 +81,6 @@ namespace 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) { @@ -316,8 +313,8 @@ namespace libtorrent timeout_handler::timeout_handler(asio::strand& str) : m_strand(str) - , m_start_time(second_clock::universal_time()) - , m_read_time(second_clock::universal_time()) + , m_start_time(time_now()) + , m_read_time(time_now()) , m_timeout(str.io_service()) , m_completion_timeout(0) , m_read_timeout(0) @@ -328,8 +325,8 @@ namespace libtorrent { 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_start_time = time_now(); + m_read_time = time_now(); m_timeout.expires_at(std::min( m_read_time + seconds(m_read_timeout) @@ -340,7 +337,7 @@ namespace libtorrent void timeout_handler::restart_read_timeout() { - m_read_time = second_clock::universal_time(); + m_read_time = time_now(); } void timeout_handler::cancel() @@ -354,14 +351,14 @@ namespace libtorrent if (error) return; if (m_completion_timeout == 0) return; - ptime now(second_clock::universal_time()); + ptime now(time_now()); time_duration receive_timeout = now - m_read_time; time_duration completion_timeout = now - m_start_time; if (m_read_timeout - < receive_timeout.total_seconds() + < total_seconds(receive_timeout) || m_completion_timeout - < completion_timeout.total_seconds()) + < total_seconds(completion_timeout)) { on_timeout(); return; @@ -427,11 +424,13 @@ namespace libtorrent m_connections.erase(i); } - - tuple + + // returns protocol, auth, hostname, port, path + tuple parse_url_components(std::string url) { std::string hostname; // hostname only + std::string auth; // user:pass std::string protocol; // should be http int port = 80; @@ -442,7 +441,7 @@ namespace libtorrent ++start; std::string::iterator end = std::find(url.begin(), url.end(), ':'); - protocol = std::string(start, end); + protocol.assign(start, end); if (end == url.end()) throw std::runtime_error("invalid url"); ++end; @@ -454,7 +453,20 @@ namespace libtorrent ++end; start = end; + std::string::iterator at = std::find(start, url.end(), '@'); + std::string::iterator colon = std::find(start, url.end(), ':'); end = std::find(start, url.end(), '/'); + + if (at != url.end() + && colon != url.end() + && colon < at + && at < end) + { + auth.assign(start, at); + start = at; + ++start; + } + std::string::iterator port_pos = std::find(start, url.end(), ':'); @@ -478,12 +490,13 @@ namespace libtorrent } start = end; - return make_tuple(protocol, hostname, port + return make_tuple(protocol, auth, hostname, port , std::string(start, url.end())); } void tracker_manager::queue_request( asio::strand& str + , connection_queue& cc , tracker_request req , std::string const& auth , address bind_infc @@ -494,6 +507,10 @@ namespace libtorrent if (req.event == tracker_request::stopped) req.num_want = 0; + assert(!m_abort || req.event == tracker_request::stopped); + if (m_abort && req.event != tracker_request::stopped) + return; + try { std::string protocol; @@ -501,7 +518,9 @@ namespace libtorrent int port; std::string request_string; - boost::tie(protocol, hostname, port, request_string) + using boost::tuples::ignore; + // TODO: should auth be used here? + boost::tie(protocol, ignore, hostname, port, request_string) = parse_url_components(req.url); boost::intrusive_ptr con; @@ -510,6 +529,7 @@ namespace libtorrent { con = new http_tracker_connection( str + , cc , *this , req , hostname @@ -518,6 +538,7 @@ namespace libtorrent , bind_infc , c , m_settings + , m_proxy , auth); } else if (protocol == "udp") @@ -555,6 +576,7 @@ namespace libtorrent // 'event=stopped'-requests) mutex_t::scoped_lock l(m_mutex); + m_abort = true; tracker_connections_t keep_connections; for (tracker_connections_t::const_iterator i = diff --git a/libtorrent/src/udp_tracker_connection.cpp b/libtorrent/src/udp_tracker_connection.cpp index 16aec7691..d992ca050 100644 --- a/libtorrent/src/udp_tracker_connection.cpp +++ b/libtorrent/src/udp_tracker_connection.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -65,7 +67,6 @@ namespace }; } -using namespace boost::posix_time; using boost::bind; using boost::lexical_cast; diff --git a/libtorrent/src/upnp.cpp b/libtorrent/src/upnp.cpp new file mode 100644 index 000000000..c60725e9d --- /dev/null +++ b/libtorrent/src/upnp.cpp @@ -0,0 +1,1038 @@ +/* + +Copyright (c) 2007, 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/pch.hpp" + +#include "libtorrent/socket.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/http_tracker_connection.hpp" +#include "libtorrent/xml_parse.hpp" +#include "libtorrent/connection_queue.hpp" + +#include +#include +#include +#include +#include +#include + +#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined(TORRENT_UPNP_LOGGING) +#define TORRENT_UPNP_LOGGING +#endif + +using boost::bind; +using namespace libtorrent; + +address_v4 upnp::upnp_multicast_address; +udp::endpoint upnp::upnp_multicast_endpoint; + +namespace libtorrent +{ + bool is_local(address const& a) + { + if (a.is_v6()) return false; + address_v4 a4 = a.to_v4(); + return ((a4.to_ulong() & 0xff000000) == 0x0a000000 + || (a4.to_ulong() & 0xfff00000) == 0xac100000 + || (a4.to_ulong() & 0xffff0000) == 0xc0a80000); + } + + address_v4 guess_local_address(asio::io_service& ios) + { + // make a best guess of the interface we're using and its IP + udp::resolver r(ios); + udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0")); + for (;i != udp::resolver_iterator(); ++i) + { + // ignore the loopback + if (i->endpoint().address() == address_v4((127 << 24) + 1)) continue; + // ignore addresses that are not on a local network + if (!is_local(i->endpoint().address())) continue; + // ignore non-IPv4 addresses + if (i->endpoint().address().is_v4()) break; + } + if (i == udp::resolver_iterator()) return address_v4::any(); + return i->endpoint().address().to_v4(); + } +} + +upnp::upnp(io_service& ios, connection_queue& cc + , address const& listen_interface, std::string const& user_agent + , portmap_callback_t const& cb) + : m_udp_local_port(0) + , m_tcp_local_port(0) + , m_user_agent(user_agent) + , m_callback(cb) + , m_retry_count(0) + , m_socket(ios) + , m_broadcast_timer(ios) + , m_refresh_timer(ios) + , m_strand(ios) + , m_disabled(false) + , m_closing(false) + , m_cc(cc) +{ + // UPnP multicast address and port + upnp_multicast_address = address_v4::from_string("239.255.255.250"); + upnp_multicast_endpoint = udp::endpoint(upnp_multicast_address, 1900); + +#ifdef TORRENT_UPNP_LOGGING + m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc); +#endif + rebind(listen_interface); +} + +upnp::~upnp() +{ +} + +void upnp::rebind(address const& listen_interface) try +{ + address_v4 bind_to = address_v4::any(); + if (listen_interface.is_v4() && listen_interface != address_v4::any()) + { + m_local_ip = listen_interface.to_v4(); + bind_to = listen_interface.to_v4(); + if (!is_local(m_local_ip)) + { + // the local address seems to be an external + // internet address. Assume it is not behind a NAT + throw std::runtime_error("local IP is not on a local network"); + } + } + else + { + m_local_ip = guess_local_address(m_socket.io_service()); + bind_to = address_v4::any(); + } + + if (!is_local(m_local_ip)) + { + throw std::runtime_error("local host is probably not on a NATed " + "network. disabling UPnP"); + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " local ip: " << m_local_ip.to_string() + << " bind to: " << bind_to.to_string() << std::endl; +#endif + + // the local interface hasn't changed + if (m_socket.is_open() + && m_socket.local_endpoint().address() == m_local_ip) + return; + + m_socket.close(); + + using namespace asio::ip::multicast; + + m_socket.open(udp::v4()); + m_socket.set_option(datagram_socket::reuse_address(true)); + m_socket.bind(udp::endpoint(bind_to, 0)); + + m_socket.set_option(join_group(upnp_multicast_address)); + m_socket.set_option(outbound_interface(bind_to)); + m_socket.set_option(hops(255)); + m_disabled = false; + + m_retry_count = 0; + discover_device(); +} +catch (std::exception& e) +{ + disable(); + std::stringstream msg; + msg << "UPnP portmapping disabled: " << e.what(); + m_callback(0, 0, msg.str()); +}; + +void upnp::discover_device() try +{ + const char msearch[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n\r\n"; + + m_socket.async_receive_from(asio::buffer(m_receive_buffer + , sizeof(m_receive_buffer)), m_remote, m_strand.wrap(bind( + &upnp::on_reply, this, _1, _2))); + + asio::error_code ec; +#ifdef TORRENT_DEBUG_UPNP + // simulate packet loss + if (m_retry_count & 1) +#endif + m_socket.send_to(asio::buffer(msearch, sizeof(msearch) - 1) + , upnp_multicast_endpoint, 0, ec); + + if (ec) + { + disable(); + return; + } + + ++m_retry_count; + m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count)); + m_broadcast_timer.async_wait(m_strand.wrap(bind(&upnp::resend_request + , this, _1))); + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> Broadcasting search for rootdevice" << std::endl; +#endif +} +catch (std::exception&) +{ + disable(); +} + +void upnp::set_mappings(int tcp, int udp) +{ +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** set mappings " << tcp << " " << udp; + if (m_disabled) m_log << " DISABLED"; + m_log << std::endl; +#endif + + if (m_disabled) return; + if (udp != 0) m_udp_local_port = udp; + if (tcp != 0) m_tcp_local_port = tcp; + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + if (d.mapping[0].local_port != m_tcp_local_port) + { + if (d.mapping[0].external_port == 0) + d.mapping[0].external_port = m_tcp_local_port; + d.mapping[0].local_port = m_tcp_local_port; + d.mapping[0].need_update = true; + } + if (d.mapping[1].local_port != m_udp_local_port) + { + if (d.mapping[1].external_port == 0) + d.mapping[1].external_port = m_udp_local_port; + d.mapping[1].local_port = m_udp_local_port; + d.mapping[1].need_update = true; + } + if (d.service_namespace + && (d.mapping[0].need_update || d.mapping[1].need_update)) + map_port(d, 0); + } +} + +void upnp::resend_request(asio::error_code const& e) +#ifndef NDEBUG +try +#endif +{ + if (e) return; + if (m_retry_count < 9 + && (m_devices.empty() || m_retry_count < 4)) + { + discover_device(); + return; + } + + if (m_devices.empty()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Got no response in 9 retries. Giving up, " + "disabling UPnP." << std::endl; +#endif + disable(); + return; + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + try + { + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 + , boost::ref(d))))); + d.upnp_connection->get(d.url); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Connection failed to: " << d.url + << " " << e.what() << std::endl; +#endif + d.disabled = true; + } + } + } +} +#ifndef NDEBUG +catch (std::exception&) +{ + assert(false); +} +#endif + +void upnp::on_reply(asio::error_code const& e + , std::size_t bytes_transferred) +#ifndef NDEBUG +try +#endif +{ + using namespace libtorrent::detail; + if (e) return; + + // parse out the url for the device + +/* + the response looks like this: + + HTTP/1.1 200 OK + ST:upnp:rootdevice + USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice + Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc + Server: Custom/1.0 UPnP/1.0 Proc/Ver + EXT: + Cache-Control:max-age=180 + DATE: Fri, 02 Jan 1970 08:10:38 GMT +*/ + http_parser p; + try + { + p.incoming(buffer::const_interval(m_receive_buffer + , m_receive_buffer + bytes_transferred)); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with incorrect HTTP packet: " + << e.what() << ". Ignoring device" << std::endl; +#endif + return; + } + + if (p.status_code() != 200) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with HTTP status: " << p.status_code() + << ". Ignoring device" << std::endl; +#endif + return; + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with incomplete HTTP " + "packet. Ignoring device" << std::endl; +#endif + return; + } + + std::string url = p.header("location"); + if (url.empty()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response is missing a location header. " + "Ignoring device" << std::endl; +#endif + return; + } + + rootdevice d; + d.url = url; + + std::set::iterator i = m_devices.find(d); + + if (i == m_devices.end()) + { + + std::string protocol; + std::string auth; + // we don't have this device in our list. Add it + boost::tie(protocol, auth, d.hostname, d.port, d.path) + = parse_url_components(d.url); + + // ignore the auth here. It will be re-parsed + // by the http connection later + + if (protocol != "http") + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice uses unsupported protocol: '" << protocol + << "'. Ignoring device" << std::endl; +#endif + return; + } + + if (d.port == 0) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice responded with a url with port 0. " + "Ignoring device" << std::endl; +#endif + return; + } +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Found rootdevice: " << d.url << std::endl; +#endif + + if (m_tcp_local_port != 0) + { + d.mapping[0].need_update = true; + d.mapping[0].local_port = m_tcp_local_port; +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** Mapping 0 will be updated" << std::endl; +#endif + } + if (m_udp_local_port != 0) + { + d.mapping[1].need_update = true; + d.mapping[1].local_port = m_udp_local_port; +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** Mapping 1 will be updated" << std::endl; +#endif + } + boost::tie(i, boost::tuples::ignore) = m_devices.insert(d); + } + + + // since we're using udp, send the query 4 times + // just to make sure we find all devices + if (m_retry_count >= 4 && !m_devices.empty()) + { + m_broadcast_timer.cancel(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + if (i->control_url.empty() && !i->upnp_connection && !i->disabled) + { + // we don't have a WANIP or WANPPP url for this device, + // ask for it + rootdevice& d = const_cast(*i); + try + { + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 + , boost::ref(d))))); + d.upnp_connection->get(d.url); + } + catch (std::exception& e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " *** Connection failed to: " << d.url + << " " << e.what() << std::endl; +#endif + d.disabled = true; + } + } + } + } +} +#ifndef NDEBUG +catch (std::exception&) +{ + assert(false); +}; +#endif + +void upnp::post(rootdevice& d, std::stringstream const& soap + , std::string const& soap_action) +{ + std::stringstream header; + + header << "POST " << d.control_url << " HTTP/1.1\r\n" + "Host: " << d.hostname << ":" << d.port << "\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "Content-Length: " << soap.str().size() << "\r\n" + "Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap.str(); + + d.upnp_connection->sendbuffer = header.str(); + d.upnp_connection->start(d.hostname, boost::lexical_cast(d.port) + , seconds(10)); +} + +void upnp::map_port(rootdevice& d, int i) +{ + if (d.upnp_connection) return; + + if (!d.mapping[i].need_update) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() << " *** mapping (" << i + << ") does not need update, skipping" << std::endl; +#endif + if (i < num_mappings - 1) + map_port(d, i + 1); + return; + } + d.mapping[i].need_update = false; + assert(!d.upnp_connection); + assert(d.service_namespace); + + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, this, _1, _2 + , boost::ref(d), i)))); + + std::string soap_action = "AddPortMapping"; + + std::stringstream soap; + + soap << "\n" + "" + ""; + + soap << "" + "" << d.mapping[i].external_port << "" + "" << (d.mapping[i].protocol ? "UDP" : "TCP") << "" + "" << d.mapping[i].local_port << "" + "" << m_local_ip.to_string() << "" + "1" + "" << m_user_agent << "" + "" << d.lease_duration << ""; + soap << ""; + + post(d, soap, soap_action); +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> AddPortMapping: " << soap.str() << std::endl; +#endif + +} + +// requires the mutex to be locked +void upnp::unmap_port(rootdevice& d, int i) +{ + if (d.mapping[i].external_port == 0) + { + if (i < num_mappings - 1) + { + unmap_port(d, i + 1); + } + else + { + m_devices.erase(d); + } + return; + } + d.upnp_connection.reset(new http_connection(m_socket.io_service() + , m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, this, _1, _2 + , boost::ref(d), i)))); + + std::string soap_action = "DeletePortMapping"; + + std::stringstream soap; + + soap << "\n" + "" + ""; + + soap << "" + "" << d.mapping[i].external_port << "" + "" << (d.mapping[i].protocol ? "UDP" : "TCP") << ""; + soap << ""; + + post(d, soap, soap_action); +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " ==> DeletePortMapping: " << soap.str() << std::endl; +#endif +} + +namespace +{ + struct parse_state + { + parse_state(): found_service(false), exit(false) {} + void reset(char const* st) + { + found_service = false; + exit = false; + service_type = st; + } + bool found_service; + bool exit; + std::string top_tag; + std::string control_url; + char const* service_type; + }; + + void find_control_url(int type, char const* string, parse_state& state) + { + if (state.exit) return; + + if (type == xml_start_tag) + { + if ((!state.top_tag.empty() && state.top_tag == "service") + || !strcmp(string, "service")) + { + state.top_tag = string; + } + } + else if (type == xml_end_tag) + { + if (!strcmp(string, "service")) + { + state.top_tag.clear(); + if (state.found_service) state.exit = true; + } + else if (!state.top_tag.empty() && state.top_tag != "service") + state.top_tag = "service"; + } + else if (type == xml_string) + { + if (state.top_tag == "serviceType") + { + if (!strcmp(string, state.service_type)) + state.found_service = true; + } + else if (state.top_tag == "controlURL") + { + state.control_url = string; + if (state.found_service) state.exit = true; + } + } + } + +} + +void upnp::on_upnp_xml(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while fetching control url: " << e.message() << std::endl; +#endif + return; + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + return; + } + + parse_state s; + s.reset("urn:schemas-upnp-org:service:WANIPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_control_url, _1, _2, boost::ref(s)))); + if (s.found_service) + { + d.service_namespace = s.service_type; + } + else + { + // we didn't find the WAN IP connection, look for + // a PPP connection + s.reset("urn:schemas-upnp-org:service:WANPPPConnection:1"); + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_control_url, _1, _2, boost::ref(s)))); + if (s.found_service) + { + d.service_namespace = s.service_type; + } + else + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response, did not find a port mapping interface" << std::endl; +#endif + return; + } + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== Rootdevice response, found control URL: " << s.control_url + << " namespace: " << d.service_namespace << std::endl; +#endif + + d.control_url = s.control_url; + + map_port(d, 0); +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::disable() +{ + m_disabled = true; + m_devices.clear(); + m_broadcast_timer.cancel(); + m_refresh_timer.cancel(); + m_socket.close(); +} + +namespace +{ + struct error_code_parse_state + { + error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {} + bool in_error_code; + bool exit; + int error_code; + }; + + void find_error_code(int type, char const* string, error_code_parse_state& state) + { + if (state.exit) return; + if (type == xml_start_tag && !strcmp("errorCode", string)) + { + state.in_error_code = true; + } + else if (type == xml_string && state.in_error_code) + { + state.error_code = std::atoi(string); + state.exit = true; + } + } +} + +namespace +{ + struct error_code_t + { + int code; + char const* msg; + }; + + error_code_t error_codes[] = + { + {402, "Invalid Arguments"} + , {501, "Action Failed"} + , {714, "The specified value does not exist in the array"} + , {715, "The source IP address cannot be wild-carded"} + , {716, "The external port cannot be wild-carded"} + , {718, "The port mapping entry specified conflicts with " + "a mapping assigned previously to another client"} + , {724, "Internal and External port values must be the same"} + , {725, "The NAT implementation only supports permanent " + "lease times on port mappings"} + , {726, "RemoteHost must be a wildcard and cannot be a " + "specific IP address or DNS name"} + , {727, "ExternalPort must be a wildcard and cannot be a specific port "} + }; + +} + +void upnp::on_upnp_map_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while adding portmap: " << e.message() << std::endl; +#endif + m_devices.erase(d); + return; + } + + if (m_closing) return; + +// error code response may look like this: +// +// +// +// s:Client +// UPnPError +// +// +// 402 +// Invalid Args +// +// +// +// +// + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + m_devices.erase(d); + return; + } + + error_code_parse_state s; + xml_parse((char*)p.get_body().begin, (char*)p.get_body().end + , m_strand.wrap(bind(&find_error_code, _1, _2, boost::ref(s)))); + +#ifdef TORRENT_UPNP_LOGGING + if (s.error_code != -1) + { + m_log << time_now_string() + << " <== got error message: " << s.error_code << std::endl; + } +#endif + + if (s.error_code == 725) + { + // only permanent leases supported + d.lease_duration = 0; + d.mapping[mapping].need_update = true; + map_port(d, mapping); + return; + } + else if (s.error_code == 718) + { + // conflict in mapping, try next external port + ++d.mapping[mapping].external_port; + d.mapping[mapping].need_update = true; + map_port(d, mapping); + return; + } + else if (s.error_code != -1) + { + int num_errors = sizeof(error_codes) / sizeof(error_codes[0]); + error_code_t* end = error_codes + num_errors; + error_code_t tmp = {s.error_code, 0}; + error_code_t* e = std::lower_bound(error_codes, end, tmp + , bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2)); + std::string error_string = "UPnP mapping error "; + error_string += boost::lexical_cast(s.error_code); + if (e != end && e->code == s.error_code) + { + error_string += ": "; + error_string += e->msg; + } + m_callback(0, 0, error_string); + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== map response: " << std::string(p.get_body().begin, p.get_body().end) + << std::endl; +#endif + + if (s.error_code == -1) + { + int tcp = 0; + int udp = 0; + + if (mapping == 0) + tcp = d.mapping[mapping].external_port; + else + udp = d.mapping[mapping].external_port; + + m_callback(tcp, udp, ""); + if (d.lease_duration > 0) + { + d.mapping[mapping].expires = time_now() + + seconds(int(d.lease_duration * 0.75f)); + ptime next_expire = m_refresh_timer.expires_at(); + if (next_expire < time_now() + || next_expire > d.mapping[mapping].expires) + { + m_refresh_timer.expires_at(d.mapping[mapping].expires); + m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, this, _1))); + } + } + else + { + d.mapping[mapping].expires = max_time(); + } + } + + for (int i = 0; i < num_mappings; ++i) + { + if (d.mapping[i].need_update) + { + map_port(d, i); + return; + } + } +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::on_upnp_unmap_response(asio::error_code const& e + , libtorrent::http_parser const& p, rootdevice& d, int mapping) try +{ + if (d.upnp_connection) + { + d.upnp_connection->close(); + d.upnp_connection.reset(); + } + + if (e) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== error while deleting portmap: " << e.message() << std::endl; +#endif + } + + if (!p.header_finished()) + { +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== incomplete http message" << std::endl; +#endif + return; + } + +#ifdef TORRENT_UPNP_LOGGING + m_log << time_now_string() + << " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end) + << std::endl; +#endif + + // ignore errors and continue with the next mapping for this device + if (mapping < num_mappings - 1) + { + unmap_port(d, mapping + 1); + return; + } + + // the main thread is likely to be waiting for + // all the unmap operations to complete + m_devices.erase(d); +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::on_expire(asio::error_code const& e) try +{ + if (e) return; + + ptime now = time_now(); + ptime next_expire = max_time(); + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end; ++i) + { + rootdevice& d = const_cast(*i); + for (int m = 0; m < num_mappings; ++m) + { + if (d.mapping[m].expires != max_time()) + continue; + + if (d.mapping[m].expires < now) + { + d.mapping[m].expires = max_time(); + map_port(d, m); + } + else if (d.mapping[m].expires < next_expire) + { + next_expire = d.mapping[m].expires; + } + } + } + if (next_expire != max_time()) + { + m_refresh_timer.expires_at(next_expire); + m_refresh_timer.async_wait(m_strand.wrap(bind(&upnp::on_expire, this, _1))); + } +} +catch (std::exception&) +{ + disable(); +}; + +void upnp::close() +{ + m_refresh_timer.cancel(); + m_broadcast_timer.cancel(); + m_closing = true; + m_socket.close(); + + if (m_disabled) + { + m_devices.clear(); + return; + } + + for (std::set::iterator i = m_devices.begin() + , end(m_devices.end()); i != end;) + { + rootdevice& d = const_cast(*i); + if (d.control_url.empty()) + { + m_devices.erase(i++); + continue; + } + ++i; + unmap_port(d, 0); + } +} + diff --git a/libtorrent/src/ut_pex.cpp b/libtorrent/src/ut_pex.cpp index e1da13f76..586cf7ccb 100644 --- a/libtorrent/src/ut_pex.cpp +++ b/libtorrent/src/ut_pex.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #ifdef _MSC_VER #pragma warning(push, 1) #endif @@ -58,6 +60,18 @@ namespace libtorrent { namespace max_peer_entries = 100 }; + bool send_peer(peer_connection const& p) + { + // don't send out peers that we haven't connected to + // (that have connected to us) + if (!p.is_local()) return false; + // don't send out peers that we haven't successfully connected to + if (p.is_connecting()) return false; + // ut pex does not support IPv6 + if (!p.remote().address().is_v4()) return false; + return true; + } + struct ut_pex_plugin: torrent_plugin { ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(0) {} @@ -79,61 +93,56 @@ namespace libtorrent { namespace if (++m_1_minute < 60) return; m_1_minute = 0; - std::list cs; - for (torrent::peer_iterator i = m_torrent.begin() - , end(m_torrent.end()); i != end; ++i) - { - // don't send out peers that we haven't connected to - // (that have connected to us) - if (!i->second->is_local()) continue; - // don't send out peers that we haven't successfully connected to - if (i->second->is_connecting()) 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; + entry pex; + std::string& pla = pex["added"].string(); + std::string& pld = pex["dropped"].string(); + std::string& plf = pex["added.f"].string(); std::back_insert_iterator pla_out(pla); std::back_insert_iterator pld_out(pld); std::back_insert_iterator plf_out(plf); - // TODO: use random selection in case added_peers.size() > num_peers - 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); + std::set dropped; + m_old_peers.swap(dropped); - if (--num_peers == 0) break; + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + if (!send_peer(*i->second)) continue; + + m_old_peers.insert(i->first); + + std::set::iterator di = dropped.find(i->first); + if (di == dropped.end()) + { + // don't write too big of a package + if (num_added >= max_peer_entries) continue; + + // i->first was added since the last time + detail::write_endpoint(i->first, pla_out); + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + int flags = i->second->is_seed() ? 2 : 0; + detail::write_uint8(flags, plf_out); + ++num_added; + } + else + { + // this was in the previous message + // so, it wasn't dropped + dropped.erase(di); + } } - num_peers = max_peer_entries; - // TODO: use random selection in case dropped_peers.size() > num_peers - for (std::list::const_iterator i = dropped_peers.begin() - , end(dropped_peers.end());i != end; ++i) + for (std::set::const_iterator i = dropped.begin() + , end(dropped.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); } @@ -141,7 +150,7 @@ namespace libtorrent { namespace private: torrent& m_torrent; - std::list m_old_peers; + std::set m_old_peers; int m_1_minute; std::vector m_ut_pex_msg; }; @@ -155,6 +164,7 @@ namespace libtorrent { namespace , m_tp(tp) , m_1_minute(0) , m_message_index(0) + , m_first_time(true) {} virtual void add_handshake(entry& h) @@ -180,6 +190,7 @@ namespace libtorrent { namespace } virtual bool on_extended(int length, int msg, buffer::const_interval body) + try { if (msg != extension_index) return false; if (m_message_index == 0) return false; @@ -189,29 +200,31 @@ namespace libtorrent { namespace 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"); + entry pex_msg = bdecode(body.begin, body.end); + std::string const& peers = pex_msg["added"].string(); + std::string const& peer_flags = pex_msg["added.f"].string(); - if (!PeerList) return true; - std::string const& peers = PeerList->string(); int num_peers = peers.length() / 6; char const* in = peers.c_str(); + char const* fin = peer_flags.c_str(); - peer_id pid; - pid.clear(); + if (int(peer_flags.size()) != num_peers) + return true; + + peer_id pid(0); 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); + char flags = detail::read_uint8(fin); + p.peer_from_tracker(adr, pid, peer_info::pex, flags); } return true; } + catch (std::exception&) + { + return true; + } // the peers second tick // every minute we send a pex message @@ -220,15 +233,66 @@ namespace libtorrent { namespace if (!m_message_index) return; // no handshake yet if (++m_1_minute <= 60) return; - send_ut_peer_list(); + if (m_first_time) + { + send_ut_peer_list(); + m_first_time = false; + } + else + { + send_ut_peer_diff(); + } m_1_minute = 0; } private: + void send_ut_peer_diff() + { + std::vector const& 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(); + } + void send_ut_peer_list() { - std::vector& pex_msg = m_tp.get_ut_pex_msg(); + entry pex; + // leave the dropped string empty + pex["dropped"].string(); + std::string& pla = pex["added"].string(); + std::string& plf = pex["added.f"].string(); + std::back_insert_iterator pla_out(pla); + std::back_insert_iterator plf_out(plf); + + int num_added = 0; + for (torrent::peer_iterator i = m_torrent.begin() + , end(m_torrent.end()); i != end; ++i) + { + if (!send_peer(*i->second)) continue; + + // don't write too big of a package + if (num_added >= max_peer_entries) continue; + + // i->first was added since the last time + detail::write_endpoint(i->first, pla_out); + // no supported flags to set yet + // 0x01 - peer supports encryption + // 0x02 - peer is a seed + int flags = i->second->is_seed() ? 2 : 0; + detail::write_uint8(flags, plf_out); + ++num_added; + } + std::vector pex_msg; + bencode(std::back_inserter(pex_msg), pex); buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size()); @@ -247,10 +311,18 @@ namespace libtorrent { namespace ut_pex_plugin& m_tp; int m_1_minute; int m_message_index; + + // this is initialized to true, and set to + // false after the first pex message has been sent. + // it is used to know if a diff message or a full + // message should be sent. + bool m_first_time; }; boost::shared_ptr ut_pex_plugin::new_connection(peer_connection* pc) { + bt_peer_connection* c = dynamic_cast(pc); + if (!c) return boost::shared_ptr(); return boost::shared_ptr(new ut_pex_peer_plugin(m_torrent , *pc, *this)); } diff --git a/libtorrent/src/web_peer_connection.cpp b/libtorrent/src/web_peer_connection.cpp index 65652db2e..bbe7c6e66 100644 --- a/libtorrent/src/web_peer_connection.cpp +++ b/libtorrent/src/web_peer_connection.cpp @@ -30,6 +30,8 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include "libtorrent/pch.hpp" + #include #include #include @@ -48,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE. #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; @@ -58,11 +59,11 @@ namespace libtorrent web_peer_connection::web_peer_connection( session_impl& ses , boost::weak_ptr t - , boost::shared_ptr s + , boost::shared_ptr s , tcp::endpoint const& remote - , tcp::endpoint const& proxy - , std::string const& url) - : peer_connection(ses, t, s, remote, proxy) + , std::string const& url + , policy::peer* peerinfo) + : peer_connection(ses, t, s, remote, peerinfo) , m_url(url) , m_first_request(true) { @@ -93,9 +94,12 @@ namespace libtorrent #endif std::string protocol; - boost::tie(protocol, m_host, m_port, m_path) + boost::tie(protocol, m_auth, m_host, m_port, m_path) = parse_url_components(url); - + + if (!m_auth.empty()) + m_auth = base64encode(m_auth); + m_server_string = "URL seed @ "; m_server_string += m_host; } @@ -106,17 +110,31 @@ namespace libtorrent boost::optional web_peer_connection::downloading_piece_progress() const { - if (!m_parser.header_finished() || m_requests.empty()) + if (m_requests.empty()) return boost::optional(); boost::shared_ptr t = associated_torrent().lock(); assert(t); - buffer::const_interval http_body = m_parser.get_body(); piece_block_progress ret; ret.piece_index = m_requests.front().piece; - ret.bytes_downloaded = http_body.left() % t->block_size(); + if (!m_piece.empty()) + { + ret.bytes_downloaded = int(m_piece.size()); + } + else + { + if (!m_parser.header_finished()) + { + ret.bytes_downloaded = 0; + } + else + { + int receive_buffer_size = receive_buffer().left() - m_parser.body_start(); + ret.bytes_downloaded = receive_buffer_size % t->block_size(); + } + } ret.block_index = (m_requests.front().start + ret.bytes_downloaded) / t->block_size(); ret.full_block_bytes = t->block_size(); const int last_piece = t->torrent_file().num_pieces() - 1; @@ -136,8 +154,8 @@ namespace libtorrent t->torrent_file().num_pieces(), true)); // it is always possible to request pieces incoming_unchoke(); - - reset_recv_buffer(t->torrent_file().piece_length() + 1024 * 2); + + reset_recv_buffer(t->block_size() + 1024); } void web_peer_connection::write_request(peer_request const& r) @@ -156,6 +174,7 @@ namespace libtorrent torrent_info const& info = t->torrent_file(); std::string request; + request.reserve(400); int size = r.length; const int block_size = t->block_size(); @@ -168,9 +187,9 @@ namespace libtorrent size -= request_size; } - bool using_proxy = false; - if (!m_ses.settings().proxy_ip.empty()) - using_proxy = true; + proxy_settings const& ps = m_ses.web_seed_proxy(); + bool using_proxy = ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw; if (single_file_request) { @@ -186,11 +205,15 @@ namespace libtorrent request += "\r\nUser-Agent: "; request += m_ses.settings().user_agent; } - if (using_proxy && !m_ses.settings().proxy_login.empty()) + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } + if (ps.type == proxy_settings::http_pw) { request += "\r\nProxy-Authorization: Basic "; - request += base64encode(m_ses.settings().proxy_login + ":" - + m_ses.settings().proxy_password); + request += base64encode(ps.username + ":" + ps.password); } if (using_proxy) { @@ -239,11 +262,15 @@ namespace libtorrent request += "\r\nUser-Agent: "; request += m_ses.settings().user_agent; } - if (using_proxy && !m_ses.settings().proxy_login.empty()) + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } + if (ps.type == proxy_settings::http_pw) { request += "\r\nProxy-Authorization: Basic "; - request += base64encode(m_ses.settings().proxy_login + ":" - + m_ses.settings().proxy_password); + request += base64encode(ps.username + ":" + ps.password); } if (using_proxy) { @@ -257,6 +284,7 @@ namespace libtorrent request += "\r\nConnection: keep-alive"; request += "\r\n\r\n"; m_first_request = false; + assert(f.file_index >= 0); m_file_requests.push_back(f.file_index); } } @@ -301,36 +329,45 @@ namespace libtorrent int payload; int protocol; bool header_finished = m_parser.header_finished(); - boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); - m_statistics.received_bytes(payload, protocol); - - assert(recv_buffer.left() <= packet_size()); - assert (recv_buffer.left() < packet_size() - || m_parser.finished()); - - // this means the entire status line hasn't been received yet - if (m_parser.status_code() == -1) break; - - // if the status code is not one of the accepted ones, abort - if (m_parser.status_code() != 206 // partial content - && m_parser.status_code() != 200 // OK - && !(m_parser.status_code() >= 300 // redirect - && m_parser.status_code() < 400)) + if (!header_finished) { - // we should not try this server again. - t->remove_url_seed(m_url); - std::string error_msg = boost::lexical_cast(m_parser.status_code()) - + " " + m_parser.message(); - if (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(url_seed_alert(t->get_handle(), url() - , error_msg)); - } - throw std::runtime_error(error_msg); - } + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer); + m_statistics.received_bytes(payload, protocol); - if (!m_parser.header_finished()) break; + assert(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + assert(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) break; + + // if the status code is not one of the accepted ones, abort + if (m_parser.status_code() != 206 // partial content + && m_parser.status_code() != 200 // OK + && !(m_parser.status_code() >= 300 // redirect + && m_parser.status_code() < 400)) + { + // we should not try this server again. + t->remove_url_seed(m_url); + std::string error_msg = boost::lexical_cast(m_parser.status_code()) + + " " + m_parser.message(); + if (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(url_seed_alert(t->get_handle(), url() + , error_msg)); + } + throw std::runtime_error(error_msg); + } + if (!m_parser.header_finished()) break; + + m_body_start = m_parser.body_start(); + m_received_body = 0; + } + else + { + m_statistics.received_bytes(bytes_transferred, 0); + } // we just completed reading the header if (!header_finished) @@ -385,9 +422,13 @@ namespace libtorrent m_server_string += ")"; } + m_body_start = m_parser.body_start(); + m_received_body = 0; } - buffer::const_interval http_body = m_parser.get_body(); + recv_buffer.begin += m_body_start; + // we only received the header, no data + if (recv_buffer.left() == 0) break; size_type range_start; size_type range_end; @@ -428,19 +469,17 @@ namespace libtorrent , range_end - range_start); peer_request front_request = m_requests.front(); + if (in_range.piece != front_request.piece || in_range.start > front_request.start + int(m_piece.size())) { throw std::runtime_error("invalid range in HTTP response"); } - front_request = m_requests.front(); - // skip the http header and the blocks we've already read. The // http_body.begin is now in sync with the request at the front // of the request queue assert(in_range.start - int(m_piece.size()) <= front_request.start); - http_body.begin += front_request.start - in_range.start + int(m_piece.size()); // the http response body consists of 3 parts // 1. the middle of a block or the ending of a block @@ -463,41 +502,54 @@ namespace libtorrent // (if it completed) call incoming_piece() with // m_piece as buffer. - m_piece.reserve(info.piece_length()); - int copy_size = std::min(front_request.length - int(m_piece.size()) - , http_body.left()); - std::copy(http_body.begin, http_body.begin + copy_size, std::back_inserter(m_piece)); - assert(int(m_piece.size()) <= front_request.length); - http_body.begin += copy_size; int piece_size = int(m_piece.size()); - if (piece_size < front_request.length) - return; + int copy_size = std::min(std::min(front_request.length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + m_piece.resize(piece_size + copy_size); + assert(copy_size > 0); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + assert(int(m_piece.size()) <= front_request.length); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + assert(m_received_body <= range_end - range_start); + assert(int(m_piece.size()) <= front_request.length); + if (int(m_piece.size()) == front_request.length) + { + // each call to incoming_piece() may result in us becoming + // a seed. If we become a seed, all seeds we're connected to + // will be disconnected, including this web seed. We need to + // check for the disconnect condition after the call. - // each call to incoming_piece() may result in us becoming - // a seed. If we become a seed, all seeds we're connected to - // will be disconnected, including this web seed. We need to - // check for the disconnect condition after the call. - - m_requests.pop_front(); - incoming_piece(front_request, &m_piece[0]); - if (associated_torrent().expired()) return; - m_piece.clear(); + m_requests.pop_front(); + incoming_piece(front_request, &m_piece[0]); + if (associated_torrent().expired()) return; + cut_receive_buffer(m_body_start, t->block_size() + 1024); + m_body_start = 0; + recv_buffer = receive_buffer(); + assert(m_received_body <= range_end - range_start); + m_piece.clear(); + assert(m_piece.empty()); + } } // report all received blocks to the bittorrent engine while (!m_requests.empty() && range_contains(in_range, m_requests.front()) - && http_body.left() >= m_requests.front().length) + && recv_buffer.left() >= m_requests.front().length) { peer_request r = m_requests.front(); m_requests.pop_front(); - assert(http_body.begin == recv_buffer.begin + m_parser.body_start() - + r.start - in_range.start); - assert(http_body.left() >= r.length); + assert(recv_buffer.left() >= r.length); - incoming_piece(r, http_body.begin); + incoming_piece(r, recv_buffer.begin); if (associated_torrent().expired()) return; - http_body.begin += r.length; + m_received_body += r.length; + assert(receive_buffer().begin + m_body_start == recv_buffer.begin); + assert(m_received_body <= range_end - range_start); + cut_receive_buffer(r.length + m_body_start, t->block_size() + 1024); + m_body_start = 0; + recv_buffer = receive_buffer(); } if (!m_requests.empty()) @@ -506,24 +558,34 @@ namespace libtorrent > m_requests.front().start + int(m_piece.size()); if (in_range.start + in_range.length < m_requests.front().start + m_requests.front().length - && m_parser.finished()) + && (m_received_body + recv_buffer.left() >= range_end - range_start)) { - m_piece.reserve(info.piece_length()); - int copy_size = std::min(m_requests.front().length - int(m_piece.size()) - , http_body.left()); - std::copy(http_body.begin, http_body.begin + copy_size, std::back_inserter(m_piece)); - http_body.begin += copy_size; + int piece_size = int(m_piece.size()); + int copy_size = std::min(std::min(m_requests.front().length - piece_size + , recv_buffer.left()), int(range_end - range_start - m_received_body)); + assert(copy_size >= 0); + if (copy_size > 0) + { + m_piece.resize(piece_size + copy_size); + std::memcpy(&m_piece[0] + piece_size, recv_buffer.begin, copy_size); + recv_buffer.begin += copy_size; + m_received_body += copy_size; + m_body_start += copy_size; + } + assert(m_received_body == range_end - range_start); } } - if (m_parser.finished()) + assert(m_received_body <= range_end - range_start); + if (m_received_body == range_end - range_start) { + cut_receive_buffer(recv_buffer.begin - receive_buffer().begin + , t->block_size() + 1024); + recv_buffer = receive_buffer(); m_file_requests.pop_front(); - assert(http_body.left() == 0); m_parser.reset(); - assert(recv_buffer.end == http_body.end || *http_body.end == 'H'); - cut_receive_buffer(http_body.end - recv_buffer.begin - , t->torrent_file().piece_length() + 1024 * 2); + m_body_start = 0; + m_received_body = 0; continue; } break; @@ -540,9 +602,11 @@ namespace libtorrent p.payload_up_speed = statistics().upload_payload_rate(); p.pid = pid(); p.ip = remote(); - + +#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES p.country[0] = m_country[0]; p.country[1] = m_country[1]; +#endif p.total_download = statistics().total_payload_download(); p.total_upload = statistics().total_payload_upload(); @@ -593,6 +657,8 @@ namespace libtorrent p.client = m_server_string; p.connection_type = peer_info::web_seed; + p.source = 0; + p.failcount = 0; } bool web_peer_connection::in_handshake() const diff --git a/miniupnpc/Changelog.txt b/miniupnpc/Changelog.txt deleted file mode 100644 index f8efffacf..000000000 --- a/miniupnpc/Changelog.txt +++ /dev/null @@ -1,85 +0,0 @@ -$Id: Changelog.txt,v 1.27 2007/03/01 22:29:48 nanard Exp $ -miniUPnP client Changelog. - -2007/03/01: - Small typo fix... - -2007/01/30: - Now parsing the HTTP header from SOAP responses in order to - get content-length value. - -2007/01/29: - Fixed the Soap Query to speedup the HTTP request. - added some Win32 DLL stuff... - -2007/01/27: - Fixed some WIN32 compatibility issues - -2006/12/14: - Added UPNPIGD_IsConnected() function in miniupnp.c/.h - Added UPNP_GetValidIGD() in miniupnp.c/.h - cleaned upnpc.c main(). now using UPNP_GetValidIGD() - -2006/12/07: - Version 1.0-RC1 released - -2006/12/03: - Minor changes to compile under SunOS/Solaris - -2006/11/30: - made a minixml parser validator program - updated minixml to handle attributes correctly - -2006/11/22: - Added a -r option to the upnpc sample thanks to Alexander Hubmann. - -2006/11/19: - Cleanup code to make it more ANSI C compliant - -2006/11/10: - detect and display local lan address. - -2006/11/04: - Packets and Bytes Sent/Received are now unsigned int. - -2006/11/01: - Bug fix thanks to Giuseppe D'Angelo - -2006/10/31: - C++ compatibility for .h files. - Added a way to get ip Address on the LAN used to reach the IGD. - -2006/10/25: - Added M-SEARCH to the services in the discovery process. - -2006/10/22: - updated the Makefile to use makedepend, added a "make install" - update Makefile - -2006/10/20: - fixing the description url parsing thanks to patch sent by - Wayne Dawe. - Fixed/translated some comments. - Implemented a better discover process, first looking - for IGD then for root devices (as some devices only reply to - M-SEARCH for root devices). - -2006/09/02: - added freeUPNPDevlist() function. - -2006/08/04: - More command line arguments checking - -2006/08/01: - Added the .bat file to compile under Win32 with minGW32 - -2006/07/31: - Fixed the rootdesc parser (igd_desc_parse.c) - -2006/07/20: - parseMSEARCHReply() is now returning the ST: line as well - starting changes to detect several UPnP devices on the network - -2006/07/19: - using GetCommonLinkProperties to get down/upload bitrate - diff --git a/miniupnpc/Makefile b/miniupnpc/Makefile deleted file mode 100644 index c46569370..000000000 --- a/miniupnpc/Makefile +++ /dev/null @@ -1,76 +0,0 @@ -# $Id: Makefile,v 1.21 2007/03/02 10:45:52 nanard Exp $ -# MiniUPnP Project -# http://miniupnp.free.fr/ -# (c) 2005-2006 Thomas Bernard -CC = gcc -#AR = gar -CFLAGS = -O -Wall -g -DDEBUG -#CFLAGS = -O -Wall -DNDEBUG -INSTALL = install -#following libs are needed on Solaris -#LDLIBS=-lsocket -lnsl -lresolv - -SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \ - upnpc.c upnpcommands.c upnpreplyparse.c testminixml.c \ - minixmlvalid.c - -OBJS = $(patsubst %.c,%.o,$(SRCS)) - -# HEADERS to install -HEADERS = miniupnpc.h miniwget.h upnpcommands.h igd_desc_parse.h \ - upnpreplyparse.h -LIBRARY = libminiupnpc.a -EXECUTABLES = upnpc testminixml minixmlvalid - -INSTALLPREFIX = /usr -INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc -INSTALLDIRLIB = $(INSTALLPREFIX)/lib - -.PHONY: install clean depend all - -all: validateminixml $(LIBRARY) $(EXECUTABLES) - -validateminixml: minixmlvalid - @echo "minixml validation test" - ./minixmlvalid - touch $@ - -clean: - $(RM) $(LIBRARY) $(EXECUTABLES) $(OBJS) - -install: $(LIBRARY) - $(INSTALL) -d $(INSTALLDIRINC) - $(INSTALL) --mode=644 $(HEADERS) $(INSTALLDIRINC) - $(INSTALL) --mode=644 $(LIBRARY) $(INSTALLDIRLIB) - -cleaninstall: - $(RM) -r $(INSTALLDIRINC) - $(RM) $(INSTALLDIRLIB)/$(LIBRARY) - -depend: - makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null - -libminiupnpc.a: miniwget.o minixml.o igd_desc_parse.o minisoap.o miniupnpc.o upnpreplyparse.o upnpcommands.o - $(AR) crs $@ $? - -upnpc: upnpc.o libminiupnpc.a - -testminixml: minixml.o igd_desc_parse.o testminixml.o - -minixmlvalid: minixml.o minixmlvalid.o - -# DO NOT DELETE THIS LINE -- make depend depends on it. - -igd_desc_parse.o: igd_desc_parse.h -miniupnpc.o: miniupnpc.h declspec.h igd_desc_parse.h miniwget.h minisoap.h -miniupnpc.o: minixml.h upnpcommands.h upnpreplyparse.h -minixml.o: minixml.h -minisoap.o: minisoap.h -miniwget.o: miniupnpc.h declspec.h igd_desc_parse.h -upnpc.o: miniwget.h declspec.h miniupnpc.h igd_desc_parse.h upnpcommands.h -upnpc.o: upnpreplyparse.h -upnpcommands.o: upnpcommands.h upnpreplyparse.h miniupnpc.h declspec.h -upnpcommands.o: igd_desc_parse.h -upnpreplyparse.o: upnpreplyparse.h minixml.h -testminixml.o: minixml.h igd_desc_parse.h -minixmlvalid.o: minixml.h diff --git a/miniupnpc/Makefile.mingw b/miniupnpc/Makefile.mingw deleted file mode 100644 index c87924213..000000000 --- a/miniupnpc/Makefile.mingw +++ /dev/null @@ -1,35 +0,0 @@ -# $Id: Makefile.mingw,v 1.4 2006/11/08 22:07:47 nanard Exp $ -CC = gcc -#CFLAGS = -Wall -g -DDEBUG -CFLAGS = -Wall -Os -DNDEBUG -LDLIBS = -lws2_32 -# -lwsock32 - -all: upnpc testminixml libminiupnpc.a - -clean: - del upnpc testminixml *.o - -libminiupnpc.a: miniwget.o minixml.o igd_desc_parse.o minisoap.o miniupnpc.o upnpreplyparse.o upnpcommands.o - $(AR) cr $@ $? - -upnpc: upnpc.o libminiupnpc.a - -minixml.o: minixml.c minixml.h - -upnpc.o: upnpc.c miniwget.h minisoap.h miniupnpc.h igd_desc_parse.h upnpreplyparse.h upnpcommands.h - -miniwget.o: miniwget.c miniwget.h - -minisoap.o: minisoap.c minisoap.h - -miniupnpc.o: miniupnpc.c miniupnpc.h minisoap.h miniwget.h minixml.h - -igd_desc_parse.o: igd_desc_parse.c igd_desc_parse.h - -testminixml: minixml.o igd_desc_parse.o testminixml.c - -upnpreplyparse.o: upnpreplyparse.c upnpreplyparse.h minixml.h - -upnpcommands.o: upnpcommands.c upnpcommands.h upnpreplyparse.h miniupnpc.h - diff --git a/miniupnpc/README b/miniupnpc/README deleted file mode 100644 index 7956e056b..000000000 --- a/miniupnpc/README +++ /dev/null @@ -1,42 +0,0 @@ -Project: miniupnp -Project web page: http://miniupnp.free.fr/ -Author: Thomas Bernard -Copyright (c) 2005 Thomas Bernard -This software is subject to the conditions detailed in the -LICENCE file provided within this distribution. - -For the comfort of Win32 users, bsdqueue.h is included in the distribution. -Its licence is included in the header of the file. -bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system. - -* miniupnp Client * - -To compile, simply run 'gmake' (could be 'make'). -Under win32, to compile with MinGW, type "mingw32make.bat". -The compilation is known to work under linux, FreeBSD, -OpenBSD, MacOS X and cygwin. -To install the library and headers on the system use : -> su -> make install -> exit - -upnpc.c is a sample client using the libminiupnpc. -To use the libminiupnpc in your application, link it with -libminiupnpc.a and use the following functions found in miniupnpc.h, -upnpcommands.h and miniwget.h : -- upnpDiscover() -- miniwget() -- parserootdesc() -- GetUPNPUrls() -- UPNP_* (calling UPNP methods) - -Note : use #include etc... for the includes -and -lminiupnpc for the link - -Feel free to contact me if you have any problem : -e-mail : miniupnp@free.fr - -If you are using libminiupnpc in your application, please -send me an email ! - - diff --git a/miniupnpc/bsdqueue.h b/miniupnpc/bsdqueue.h deleted file mode 100644 index 1fe0599f5..000000000 --- a/miniupnpc/bsdqueue.h +++ /dev/null @@ -1,531 +0,0 @@ -/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */ -/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * - * A singly-linked list is headed by a single forward pointer. The elements - * are singly linked for minimum space and pointer manipulation overhead at - * the expense of O(n) removal for arbitrary elements. New elements can be - * added to the list after an existing element or at the head of the list. - * Elements being removed from the head of the list should use the explicit - * macro for this purpose for optimum efficiency. A singly-linked list may - * only be traversed in the forward direction. Singly-linked lists are ideal - * for applications with large datasets and few or no removals or for - * implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -#ifdef QUEUE_MACRO_DEBUG -#define _Q_INVALIDATE(a) (a) = ((void *)-1) -#else -#define _Q_INVALIDATE(a) -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#ifdef SLIST_ENTRY -#undef SLIST_ENTRY -#endif - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = SLIST_FIRST(head); \ - (var) != SLIST_END(head); \ - (var) = SLIST_NEXT(var, field)) - -#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ - for ((varp) = &SLIST_FIRST((head)); \ - ((var) = *(varp)) != SLIST_END(head); \ - (varp) = &SLIST_NEXT((var), field)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) { \ - SLIST_FIRST(head) = SLIST_END(head); \ -} - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (0) - -#define SLIST_REMOVE_NEXT(head, elm, field) do { \ - (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->slh_first; \ - \ - while (curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - _Q_INVALIDATE((elm)->field.sle_next); \ - } \ -} while (0) - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for((var) = LIST_FIRST(head); \ - (var)!= LIST_END(head); \ - (var) = LIST_NEXT(var, field)) - -/* - * List functions. - */ -#define LIST_INIT(head) do { \ - LIST_FIRST(head) = LIST_END(head); \ -} while (0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.le_next = (head)->lh_first) != NULL) \ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (0) - -#define LIST_REMOVE(elm, field) do { \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.le_prev); \ - _Q_INVALIDATE((elm)->field.le_next); \ -} while (0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for((var) = SIMPLEQ_FIRST(head); \ - (var) != SIMPLEQ_END(head); \ - (var) = SIMPLEQ_NEXT(var, field)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (0) - -/* - * Tail queue definitions. - */ -#define TAILQ_HEAD(name, type) \ -struct name { \ - struct type *tqh_first; /* first element */ \ - struct type **tqh_last; /* addr of last next element */ \ -} - -#define TAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } - -#define TAILQ_ENTRY(type) \ -struct { \ - struct type *tqe_next; /* next element */ \ - struct type **tqe_prev; /* address of previous next element */ \ -} - -/* - * tail queue access methods - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) NULL -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -/* XXX */ -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) \ - (TAILQ_FIRST(head) == TAILQ_END(head)) - -#define TAILQ_FOREACH(var, head, field) \ - for((var) = TAILQ_FIRST(head); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_NEXT(var, field)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for((var) = TAILQ_LAST(head, headname); \ - (var) != TAILQ_END(head); \ - (var) = TAILQ_PREV(var, headname, field)) - -/* - * Tail queue functions. - */ -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - _Q_INVALIDATE((elm)->field.tqe_prev); \ - _Q_INVALIDATE((elm)->field.tqe_next); \ -} while (0) - -/* - * Circular queue definitions. - */ -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue access methods - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for((var) = CIRCLEQ_FIRST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_NEXT(var, field)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for((var) = CIRCLEQ_LAST(head); \ - (var) != CIRCLEQ_END(head); \ - (var) = CIRCLEQ_PREV(var, field)) - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ - CIRCLEQ_END(head)) \ - (head).cqh_last = (elm2); \ - else \ - (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ - if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ - CIRCLEQ_END(head)) \ - (head).cqh_first = (elm2); \ - else \ - (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ - _Q_INVALIDATE((elm)->field.cqe_prev); \ - _Q_INVALIDATE((elm)->field.cqe_next); \ -} while (0) - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/miniupnpc/declspec.h b/miniupnpc/declspec.h deleted file mode 100644 index 113339c5d..000000000 --- a/miniupnpc/declspec.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __DECLSPEC_H__ -#define __DECLSPEC_H__ - -#ifdef WIN32 - #ifdef MINIUPNP_EXPORTS - #define LIBSPEC __declspec(dllexport) - #else - #define LIBSPEC __declspec(dllimport) - #endif -#else - #define LIBSPEC -#endif - -#endif - diff --git a/miniupnpc/igd_desc_parse.c b/miniupnpc/igd_desc_parse.c deleted file mode 100644 index 5b06facee..000000000 --- a/miniupnpc/igd_desc_parse.c +++ /dev/null @@ -1,115 +0,0 @@ -/* $Id: igd_desc_parse.c,v 1.7 2006/11/19 22:32:33 nanard Exp $ */ -/* Project : miniupnp - * http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#include "igd_desc_parse.h" -#include -#include - -/* Start element handler : - * update nesting level counter and copy element name */ -void IGDstartelt(void * d, const char * name, int l) -{ - struct IGDdatas * datas = (struct IGDdatas *)d; - memcpy( datas->cureltname, name, l); - datas->cureltname[l] = '\0'; - datas->level++; -} - -/* End element handler : - * update nesting level counter and update parser state if - * service element is parsed */ -void IGDendelt(void * d, const char * name, int l) -{ - struct IGDdatas * datas = (struct IGDdatas *)d; - datas->level--; - /*printf("endelt %2d %.*s\n", datas->level, l, name);*/ - if( (l==7) && !memcmp(name, "service", l) ) - { - /*datas->state++; */ - /* - if( datas->state < 1 - && !strcmp(datas->servicetype, - // "urn:schemas-upnp-org:service:WANIPConnection:1") ) - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) - datas->state ++; - */ - if(0==strcmp(datas->servicetype_CIF, - "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) - datas->state = 2; - if(0==strcmp(datas->servicetype, - "urn:schemas-upnp-org:service:WANIPConnection:1") ) - datas->state = 3; -/* if(0==strcmp(datas->servicetype, - "urn:schemas-upnp-org:service:WANPPPConnection:1") ) - datas->state = 4; */ - } -} - -/* Data handler : - * copy data depending on the current element name and state */ -void IGDdata(void * d, const char * data, int l) -{ - struct IGDdatas * datas = (struct IGDdatas *)d; - char * dstmember = 0; - /*printf("%2d %s : %.*s\n", - datas->level, datas->cureltname, l, data); */ - if( !strcmp(datas->cureltname, "URLBase") ) - dstmember = datas->urlbase; - else if(datas->state<=1) - { - if( !strcmp(datas->cureltname, "serviceType") ) - dstmember = datas->servicetype_CIF; - else if( !strcmp(datas->cureltname, "controlURL") ) - dstmember = datas->controlurl_CIF; - else if( !strcmp(datas->cureltname, "eventSubURL") ) - dstmember = datas->eventsuburl_CIF; - else if( !strcmp(datas->cureltname, "SCPDURL") ) - dstmember = datas->scpdurl_CIF; - else if( !strcmp(datas->cureltname, "deviceType") ) - dstmember = datas->devicetype_CIF; - } - else if(datas->state==2) - { - if( !strcmp(datas->cureltname, "serviceType") ) - dstmember = datas->servicetype; - else if( !strcmp(datas->cureltname, "controlURL") ) - dstmember = datas->controlurl; - else if( !strcmp(datas->cureltname, "eventSubURL") ) - dstmember = datas->eventsuburl; - else if( !strcmp(datas->cureltname, "SCPDURL") ) - dstmember = datas->scpdurl; - else if( !strcmp(datas->cureltname, "deviceType") ) - dstmember = datas->devicetype; - } - if(dstmember) - { - if(l>=MINIUPNPC_URL_MAXSIZE) - l = MINIUPNPC_URL_MAXSIZE-1; - memcpy(dstmember, data, l); - dstmember[l] = '\0'; - } -} - -void printIGD(struct IGDdatas * d) -{ - printf("urlbase = %s\n", d->urlbase); - printf("WAN Device (Common interface config) :\n"); - printf(" deviceType = %s\n", d->devicetype_CIF); - printf(" serviceType = %s\n", d->servicetype_CIF); - printf(" controlURL = %s\n", d->controlurl_CIF); - printf(" eventSubURL = %s\n", d->eventsuburl_CIF); - printf(" SCPDURL = %s\n", d->scpdurl_CIF); - printf("WAN Connection Device :\n"); - printf(" deviceType = %s\n", d->devicetype); - printf(" servicetype = %s\n", d->servicetype); - printf(" controlURL = %s\n", d->controlurl); - printf(" eventSubURL = %s\n", d->eventsuburl); - printf(" SCPDURL = %s\n", d->scpdurl); -} - - diff --git a/miniupnpc/igd_desc_parse.h b/miniupnpc/igd_desc_parse.h deleted file mode 100644 index 474dc7b49..000000000 --- a/miniupnpc/igd_desc_parse.h +++ /dev/null @@ -1,38 +0,0 @@ -/* $Id: igd_desc_parse.h,v 1.4 2006/07/06 00:05:11 nanard Exp $ */ -/* Project : miniupnp - * http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#ifndef __IGD_DESC_PARSE_H__ -#define __IGD_DESC_PARSE_H__ - -/* Structure to store the result of the parsing of UPnP - * descriptions of Internet Gateway Devices */ -#define MINIUPNPC_URL_MAXSIZE (64) -struct IGDdatas { - char cureltname[MINIUPNPC_URL_MAXSIZE]; - char urlbase[MINIUPNPC_URL_MAXSIZE]; - int level; - int state; - char controlurl_CIF[MINIUPNPC_URL_MAXSIZE]; - char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE]; - char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE]; - char servicetype_CIF[MINIUPNPC_URL_MAXSIZE]; - char devicetype_CIF[MINIUPNPC_URL_MAXSIZE]; - char controlurl[MINIUPNPC_URL_MAXSIZE]; - char eventsuburl[MINIUPNPC_URL_MAXSIZE]; - char scpdurl[MINIUPNPC_URL_MAXSIZE]; - char servicetype[MINIUPNPC_URL_MAXSIZE]; - char devicetype[MINIUPNPC_URL_MAXSIZE]; -}; - -void IGDstartelt(void *, const char *, int); -void IGDendelt(void *, const char *, int); -void IGDdata(void *, const char *, int); -void printIGD(struct IGDdatas *); - -#endif - diff --git a/miniupnpc/mingw32make.bat b/miniupnpc/mingw32make.bat deleted file mode 100644 index 94ec0b932..000000000 --- a/miniupnpc/mingw32make.bat +++ /dev/null @@ -1,5 +0,0 @@ -mingw32-make -f Makefile.mingw -if errorlevel 1 goto end -strip upnpc.exe -upx --best upnpc.exe -:end diff --git a/miniupnpc/minisoap.c b/miniupnpc/minisoap.c deleted file mode 100644 index 7267af649..000000000 --- a/miniupnpc/minisoap.c +++ /dev/null @@ -1,73 +0,0 @@ -/* $Id: minisoap.c,v 1.10 2007/01/29 20:05:07 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * - * Minimal SOAP implementation for UPnP protocol. - */ -#include -#include -#ifdef WIN32 -#include -#include -#define snprintf _snprintf -#else -#include -#include -#include -#endif -#include "minisoap.h" - -/* only for malloc */ -#include - -/* httpWrite sends the headers and the body to the socket - * and returns the number of bytes sent */ -static int -httpWrite(int fd, const char * body, int bodysize, - const char * headers, int headerssize) -{ - int n = 0; - /*n = write(fd, headers, headerssize);*/ - /*if(bodysize>0) - n += write(fd, body, bodysize);*/ - /* Note : my old linksys router only took into account - * soap request that are sent into only one packet */ - char * p; - p = malloc(headerssize+bodysize); - memcpy(p, headers, headerssize); - memcpy(p+headerssize, body, bodysize); - /*n = write(fd, p, headerssize+bodysize);*/ - n = send(fd, p, headerssize+bodysize, 0); - shutdown(fd, SHUT_WR); /*SD_SEND*/ - free(p); - return n; -} - -/* self explanatory */ -int soapPostSubmit(int fd, - const char * url, - const char * host, - unsigned short port, - const char * action, - const char * body) -{ - int bodysize; - char headerbuf[1024]; - int headerssize; - bodysize = (int)strlen(body); - headerssize = snprintf(headerbuf, sizeof(headerbuf), - "POST %s HTTP/1.1\r\n" - "HOST: %s:%d\r\n" - "Content-length: %d\r\n" - "Content-Type: text/xml\r\n" - "SOAPAction: \"%s\"\r\n" - "Connection: Close\r\n" - "\r\n", - url, host, port, bodysize, action); - return httpWrite(fd, body, bodysize, headerbuf, headerssize); -} - - diff --git a/miniupnpc/minisoap.h b/miniupnpc/minisoap.h deleted file mode 100644 index 9fa297fd3..000000000 --- a/miniupnpc/minisoap.h +++ /dev/null @@ -1,15 +0,0 @@ -/* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. */ -#ifndef __MINISOAP_H__ -#define __MINISOAP_H__ - -/*int httpWrite(int, const char *, int, const char *);*/ -int soapPostSubmit(int, const char *, const char *, unsigned short, - const char *, const char *); - -#endif - diff --git a/miniupnpc/miniupnpc.c b/miniupnpc/miniupnpc.c deleted file mode 100644 index fc4623733..000000000 --- a/miniupnpc/miniupnpc.c +++ /dev/null @@ -1,659 +0,0 @@ -/* $Id: miniupnpc.c,v 1.38 2007/03/01 22:29:49 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas BERNARD - * copyright (c) 2005 Thomas Bernard - * This software is subjet to the conditions detailed in the - * provided LICENCE file. */ -#include -#include -#include -#ifdef WIN32 -#include -#include -#include -#define snprintf _snprintf -#define strncasecmp memicmp -#define MAXHOSTNAMELEN 64 -#else -#include -#include -#include -#include -#include -#include -#include -#include -#define closesocket close -#endif -#include "miniupnpc.h" -#include "miniwget.h" -#include "minisoap.h" -#include "minixml.h" -#include "upnpcommands.h" - -/* Uncomment the following to transmit the msearch from the same port - * as the UPnP multicast port. With WinXP this seems to result in the - * responses to the msearch being lost, thus if things dont work then - * comment this out. */ -/* #define TX_FROM_UPNP_PORT */ - -#ifdef WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - -/* root description parsing */ -void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data) -{ - struct xmlparser parser; - /* xmlparser object */ - parser.xmlstart = buffer; - parser.xmlsize = bufsize; - parser.data = data; - parser.starteltfunc = IGDstartelt; - parser.endeltfunc = IGDendelt; - parser.datafunc = IGDdata; - parser.attfunc = 0; - parsexml(&parser); -#ifndef NDEBUG - printIGD(data); -#endif -} - -/* Content-length: nnn */ -static int getcontentlenfromline(const char * p, int n) -{ - static const char contlenstr[] = "content-length"; - const char * p2 = contlenstr; - int a = 0; - while(*p2) - { - if(n==0) - return -1; - if(*p2 != *p && *p2 != (*p + 32)) - return -1; - p++; p2++; n--; - } - if(n==0) - return -1; - if(*p != ':') - return -1; - p++; n--; - while(*p == ' ') - { - if(n==0) - return -1; - p++; n--; - } - while(*p >= '0' && *p <= '9') - { - if(n==0) - return -1; - a = (a * 10) + (*p - '0'); - p++; n--; - } - return a; -} - -static void -getContentLengthAndHeaderLength(char * p, int n, - int * contentlen, int * headerlen) -{ - char * line; - int linelen; - int r; - line = p; - while(line < p + n) - { - linelen = 0; - while(line[linelen] != '\r' && line[linelen] != '\r') - { - if(line+linelen >= p+n) - return; - linelen++; - } - r = getcontentlenfromline(line, linelen); - if(r>0) - *contentlen = r; - line = line + linelen + 2; - if(line[0] == '\r' && line[1] == '\n') - { - *headerlen = (line - p) + 2; - return; - } - } -} - -/* simpleUPnPcommand : - * not so simple ! - * */ -int simpleUPnPcommand(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - char * buffer, int * bufsize) -{ - struct sockaddr_in dest; - char hostname[MAXHOSTNAMELEN+1]; - unsigned short port = 0; - char * path; - char soapact[128]; - char soapbody[2048]; - int soapbodylen; - char * buf; - int buffree; - int n; - int contentlen, headerlen; /* for the response */ - snprintf(soapact, sizeof(soapact), "%s#%s", service, action); - if(args==NULL) - { - soapbodylen = snprintf(soapbody, sizeof(soapbody), - "\r\n" - "" - "" - "" - "" - "\r\n", action, service); - } - else - { - char * p; - const char * pe, * pv; - soapbodylen = snprintf(soapbody, sizeof(soapbody), - "\r\n" - "" - "" - "", - action, service); - p = soapbody + soapbodylen; - while(args->elt) - { - /* check that we are never overflowing the string... */ - if(soapbody + sizeof(soapbody) <= p + 100) - { - /* we keep a margin of at least 100 bytes */ - *bufsize = 0; - return -1; - } - *(p++) = '<'; - pe = args->elt; - while(*pe) - *(p++) = *(pe++); - *(p++) = '>'; - if((pv = args->val)) - { - while(*pv) - *(p++) = *(pv++); - } - *(p++) = '<'; - *(p++) = '/'; - pe = args->elt; - while(*pe) - *(p++) = *(pe++); - *(p++) = '>'; - args++; - } - *(p++) = '<'; - *(p++) = '/'; - *(p++) = 'm'; - *(p++) = ':'; - pe = action; - while(*pe) - *(p++) = *(pe++); - strncpy(p, ">\r\n", - soapbody + sizeof(soapbody) - p); - } - if(!parseURL(url, hostname, &port, &path)) return -1; - if(s<0) - { - s = socket(PF_INET, SOCK_STREAM, 0); - dest.sin_family = AF_INET; - dest.sin_port = htons(port); - dest.sin_addr.s_addr = inet_addr(hostname); - if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0) - { - PRINT_SOCKET_ERROR("connect"); - *bufsize = 0; - return -1; - } - } - - n = soapPostSubmit(s, path, hostname, port, soapact, soapbody); - - contentlen = -1; - headerlen = -1; - buf = buffer; - buffree = *bufsize; - *bufsize = 0; - while ((n = ReceiveData(s, buf, buffree, 5000)) > 0) { - buffree -= n; - buf += n; - *bufsize += n; - getContentLengthAndHeaderLength(buffer, *bufsize, - &contentlen, &headerlen); -#ifdef DEBUG - printf("n=%d bufsize=%d ContLen=%d HeadLen=%d\n", - n, *bufsize, contentlen, headerlen); -#endif - if(contentlen > 0 && headerlen > 0 && *bufsize >= contentlen+headerlen) - break; - } - - closesocket(s); - return -1; -} - -/* parseMSEARCHReply() - * the last 4 arguments are filled during the parsing : - * - location/locationsize : "location:" field of the SSDP reply packet - * - st/stsize : "st:" field of the SSDP reply packet. - * The strings are NOT null terminated */ -static void -parseMSEARCHReply(const char * reply, int size, - const char * * location, int * locationsize, - const char * * st, int * stsize) -{ - int a, b, i; - i = 0; - a = i; /* start of the line */ - b = 0; - while(ipNext = devlist; - tmp->descURL = tmp->buffer; - tmp->st = tmp->buffer + 1 + urlsize; - memcpy(tmp->buffer, descURL, urlsize); - tmp->buffer[urlsize] = '\0'; - memcpy(tmp->buffer + urlsize + 1, st, stsize); - tmp->buffer[urlsize+1+stsize] = '\0'; - devlist = tmp; - } - } - } -} - -/* freeUPNPDevlist() should be used to - * free the chained list returned by upnpDiscover() */ -void freeUPNPDevlist(struct UPNPDev * devlist) -{ - struct UPNPDev * next; - while(devlist) - { - next = devlist->pNext; - free(devlist); - devlist = next; - } -} - -static void -url_cpy_or_cat(char * dst, const char * src, int n) -{ - if( (src[0] == 'h') - &&(src[1] == 't') - &&(src[2] == 't') - &&(src[3] == 'p') - &&(src[4] == ':') - &&(src[5] == '/') - &&(src[6] == '/')) - { - strncpy(dst, src, n); - } - else - { - if(src[0] != '/') - strncat(dst, "/", n-1); - strncat(dst, src, n-1); - } -} - -/* Prepare the Urls for usage... - */ -void GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data, - const char * descURL) -{ - char * p; - int n1, n2, n3; - n1 = strlen(data->urlbase); - if(n1==0) - n1 = strlen(descURL); - n1++; - n2 = n1; n3 = n1; - n1 += strlen(data->scpdurl); - n2 += strlen(data->controlurl); - n3 += strlen(data->controlurl_CIF); - - urls->ipcondescURL = (char *)malloc(n1); - urls->controlURL = (char *)malloc(n2); - urls->controlURL_CIF = (char *)malloc(n3); - /* maintenant on chope la desc du WANIPConnection */ - if(data->urlbase[0] != '\0') - strncpy(urls->ipcondescURL, data->urlbase, n1); - else - strncpy(urls->ipcondescURL, descURL, n1); - p = strchr(urls->ipcondescURL+7, '/'); - if(p) p[0] = '\0'; - strncpy(urls->controlURL, urls->ipcondescURL, n2); - strncpy(urls->controlURL_CIF, urls->ipcondescURL, n3); - - url_cpy_or_cat(urls->ipcondescURL, data->scpdurl, n1); - - url_cpy_or_cat(urls->controlURL, data->controlurl, n2); - - url_cpy_or_cat(urls->controlURL_CIF, data->controlurl_CIF, n3); - -#ifdef DEBUG - printf("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL, - strlen(urls->ipcondescURL), n1); - printf("urls->controlURL='%s' %d n2=%d\n", urls->controlURL, - strlen(urls->controlURL), n2); - printf("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF, - strlen(urls->controlURL_CIF), n3); -#endif -} - -void -FreeUPNPUrls(struct UPNPUrls * urls) -{ - if(!urls) - return; - free(urls->controlURL); - urls->controlURL = 0; - free(urls->ipcondescURL); - urls->ipcondescURL = 0; - free(urls->controlURL_CIF); - urls->controlURL_CIF = 0; -} - - -int ReceiveData(int socket, char * data, int length, int timeout) -{ - int n; -#ifndef WIN32 - struct pollfd fds[1]; /* for the poll */ - fds[0].fd = socket; - fds[0].events = POLLIN; - n = poll(fds, 1, timeout); - if(n < 0) - { - PRINT_SOCKET_ERROR("poll"); - return -1; - } - else if(n == 0) - { - return 0; - } -#else - fd_set socketSet; - TIMEVAL timeval; - FD_ZERO(&socketSet); - FD_SET(socket, &socketSet); - timeval.tv_sec = timeout / 1000; - timeval.tv_usec = (timeout % 1000) * 1000; - n = select(0, &socketSet, NULL, NULL, &timeval); - if(n < 0) - { - PRINT_SOCKET_ERROR("poll"); - return -1; - } - else if(n == 0) - { - return 0; - } -#endif - n = recv(socket, data, length, 0); - if(n<0) - { - PRINT_SOCKET_ERROR("recv"); - } - return n; -} - -int -UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) -{ - char status[64]; - unsigned int uptime; - status[0] = '\0'; - UPNP_GetStatusInfo(urls->controlURL, data->servicetype, status, &uptime); - if(0 == strcmp("Connected", status)) - { - return 1; - } - else - return 0; -} - - -/* UPNP_GetValidIGD() : - * return values : - * 0 = NO IGD found - * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as - * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD - * - * In any non zero return case, the urls and data structures - * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to - * free allocated memory. - */ -int -UPNP_GetValidIGD(struct UPNPDev * devlist, - struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen) -{ - char * descXML; - int descXMLsize = 0; - struct UPNPDev * dev; - int ndev = 0; - int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ - if(!devlist) - { -#ifdef DEBUG - printf("Empty devlist\n"); -#endif - return 0; - } - for(state = 1; state <= 3; state++) - { - for(dev = devlist; dev; dev = dev->pNext) - { - /* we should choose an internet gateway device. - * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ - if((state >= 3) || strstr(dev->st, "InternetGatewayDevice")) - { - descXML = miniwget_getaddr(dev->descURL, &descXMLsize, - lanaddr, lanaddrlen); - if(descXML) - { - ndev++; - memset(data, 0, sizeof(struct IGDdatas)); - memset(urls, 0, sizeof(struct UPNPUrls)); - parserootdesc(descXML, descXMLsize, data); - free(descXML); - descXML = NULL; - GetUPNPUrls(urls, data, dev->descURL); - -#ifdef DEBUG - printf("UPNPIGD_IsConnected(%s) = %d\n", - urls->controlURL, - UPNPIGD_IsConnected(urls, data)); -#endif - if((state >= 2) || UPNPIGD_IsConnected(urls, data)) - return state; - FreeUPNPUrls(urls); - memset(data, 0, sizeof(struct IGDdatas)); - } -#ifdef DEBUG - else - { - printf("error getting XML description %s\n", dev->descURL); - } -#endif - } - } - } - return 0; -} - - - diff --git a/miniupnpc/miniupnpc.h b/miniupnpc/miniupnpc.h deleted file mode 100644 index 939c286a8..000000000 --- a/miniupnpc/miniupnpc.h +++ /dev/null @@ -1,81 +0,0 @@ -/* $Id: miniupnpc.h,v 1.13 2007/01/29 20:27:23 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ - * Author: Thomas Bernard - * Copyright (c) 2005-2006 Thomas Bernard - * This software is subjects to the conditions detailed - * in the LICENCE file provided within this distribution */ -#ifndef __MINIUPNPC_H__ -#define __MINIUPNPC_H__ - -#include "declspec.h" -#include "igd_desc_parse.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct UPNParg { const char * elt; const char * val; }; - -int simpleUPnPcommand(int, const char *, const char *, - const char *, struct UPNParg *, - char *, int *); - -struct UPNPDev { - struct UPNPDev * pNext; - char * descURL; - char * st; - char buffer[2]; -}; - -/* discover UPnP devices on the network */ -LIBSPEC struct UPNPDev * upnpDiscover(int); -/* free returned list from above function */ -LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); - -LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); - -/* structure used to get fast access to urls - * controlURL: controlURL of the WANIPConnection - * ipcondescURL: url of the description of the WANIPConnection - * controlURL_CIF: controlURL of the WANCOmmonInterfaceConfig - */ -struct UPNPUrls { - char * controlURL; - char * ipcondescURL; - char * controlURL_CIF; -}; - -/* UPNP_GetValidIGD() : - * return values : - * 0 = NO IGD found - * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as - * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD - * - * In any non zero return case, the urls and data structures - * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to - * free allocated memory. - */ -LIBSPEC int -UPNP_GetValidIGD(struct UPNPDev * devlist, - struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen); - -LIBSPEC void GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, const char *); - -LIBSPEC void FreeUPNPUrls(struct UPNPUrls *); - -/* Reads data from the specified socket. - * Returns the number of bytes read if successful, zero if no bytes were - * read or if we timed out. Returns negative if there was an error. */ -int ReceiveData(int socket, char * data, int length, int timeout); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/miniupnpc/miniwget.c b/miniupnpc/miniwget.c deleted file mode 100644 index b8295984c..000000000 --- a/miniupnpc/miniwget.c +++ /dev/null @@ -1,212 +0,0 @@ -/* $Id: miniwget.c,v 1.17 2006/12/03 17:22:09 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#include -#include -#include -#include "miniupnpc.h" -#ifdef WIN32 -#include -#include -#define MAXHOSTNAMELEN 64 -#define MIN(x,y) (((x)<(y))?(x):(y)) -#define snprintf _snprintf -#define herror -#define socklen_t int -#else -#include -#include -#include -#include -#include -#include -#define closesocket close -#endif -/* for MIN() macro : */ -#if defined(__sun) || defined(sun) -#include -#endif - -/* miniwget2() : - * */ -static void * -miniwget2(const char * url, const char * host, - unsigned short port, const char * path, - int * size, char * addr_str, int addr_str_len) -{ - char buf[2048]; - int s; - struct sockaddr_in dest; - struct hostent *hp; - *size = 0; - hp = gethostbyname(host); - if(hp==NULL) - { - herror(host); - return NULL; - } - memcpy((char *)&dest.sin_addr, hp->h_addr, hp->h_length); - memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); - s = socket(PF_INET, SOCK_STREAM, 0); - dest.sin_family = AF_INET; - dest.sin_port = htons(port); - if(connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr))<0) - { - perror("connect"); - return NULL; - } - - /* get address for caller ! */ - if(addr_str) - { - struct sockaddr_in saddr; - socklen_t len; - - len = sizeof(saddr); - getsockname(s, (struct sockaddr *)&saddr, &len); -#ifndef WIN32 - inet_ntop(AF_INET, &saddr.sin_addr, addr_str, addr_str_len); -#else - /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD); - * But his function make a string with the port : nn.nn.nn.nn:port */ -/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr), - NULL, addr_str, (DWORD *)&addr_str_len)) - { - printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError()); - }*/ - strncpy(addr_str, inet_ntoa(saddr.sin_addr), addr_str_len); -#endif -#ifdef DEBUG - printf("address miniwget : %s\n", addr_str); -#endif - } - - snprintf(buf, 2048, - "GET %s HTTP/1.1\r\n" - "Host: %s:%d\r\n" - "Connection: Close\r\n" - "\r\n", - path, host, port); - /*write(s, buf, strlen(buf));*/ - send(s, buf, strlen(buf), 0); - { - int n, headers=1; - char * respbuffer = NULL; - int allreadyread = 0; - /*while((n = recv(s, buf, 2048, 0)) > 0)*/ - while((n = ReceiveData(s, buf, 2048, 5000)) > 0) - { - if(headers) - { - int i=0; - while(ip3)) - { - strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1))); - *port = 80; - } - else - { - strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1))); - *port = 0; - p2++; - while( (*p2 >= '0') && (*p2 <= '9')) - { - *port *= 10; - *port += (unsigned short)(*p2 - '0'); - p2++; - } - } - *path = p3; - return 1; -} - -void * miniwget(const char * url, int * size) -{ - unsigned short port; - char * path; - /* protocol://host:port/chemin */ - char hostname[MAXHOSTNAMELEN+1]; - *size = 0; - if(!parseURL(url, hostname, &port, &path)) - return NULL; - return miniwget2(url, hostname, port, path, size, 0, 0); -} - -void * miniwget_getaddr(const char * url, int * size, char * addr, int addrlen) -{ - unsigned short port; - char * path; - /* protocol://host:port/chemin */ - char hostname[MAXHOSTNAMELEN+1]; - *size = 0; - if(addr) - addr[0] = '\0'; - if(!parseURL(url, hostname, &port, &path)) - return NULL; - return miniwget2(url, hostname, port, path, size, addr, addrlen); -} - diff --git a/miniupnpc/miniwget.h b/miniupnpc/miniwget.h deleted file mode 100644 index 12c062c6d..000000000 --- a/miniupnpc/miniwget.h +++ /dev/null @@ -1,28 +0,0 @@ -/* $Id: miniwget.h,v 1.5 2007/01/29 20:27:23 nanard Exp $ */ -/* Project : miniupnp - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#ifndef __MINIWGET_H__ -#define __MINIWGET_H__ - -#include "declspec.h" - -#ifdef __cplusplus -extern "C" { -#endif - -LIBSPEC void * miniwget(const char *, int *); - -LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int); - -int parseURL(const char *, char *, unsigned short *, char * *); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/miniupnpc/minixml.c b/miniupnpc/minixml.c deleted file mode 100644 index dd92f5806..000000000 --- a/miniupnpc/minixml.c +++ /dev/null @@ -1,185 +0,0 @@ -/* $Id: minixml.c,v 1.5 2007/01/27 14:20:00 nanard Exp $ */ -/* minixml.c : the minimum size a xml parser can be ! */ -/* Project : miniupnp - * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * Author : Thomas Bernard - -Copyright (c) 2005-2006, Thomas BERNARD -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. - * The name of the author may not 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 "minixml.h" - -/* parseatt : used to parse the argument list - * return 0 (false) in case of success and -1 (true) if the end - * of the xmlbuffer is reached. */ -int parseatt(struct xmlparser * p) -{ - const char * attname; - int attnamelen; - const char * attvalue; - int attvaluelen; - while(p->xml < p->xmlend) - { - if(*p->xml=='/' || *p->xml=='>') - return 0; - if( !IS_WHITE_SPACE(*p->xml) ) - { - char sep; - attname = p->xml; - attnamelen = 0; - while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) ) - { - attnamelen++; p->xml++; - if(p->xml >= p->xmlend) - return -1; - } - while(*(p->xml++) != '=') - { - if(p->xml >= p->xmlend) - return -1; - } - while(IS_WHITE_SPACE(*p->xml)) - { - p->xml++; - if(p->xml >= p->xmlend) - return -1; - } - sep = *p->xml; - if(sep=='\'' || sep=='\"') - { - p->xml++; - if(p->xml >= p->xmlend) - return -1; - attvalue = p->xml; - attvaluelen = 0; - while(*p->xml != sep) - { - attvaluelen++; p->xml++; - if(p->xml >= p->xmlend) - return -1; - } - } - else - { - attvalue = p->xml; - attvaluelen = 0; - while( !IS_WHITE_SPACE(*p->xml) - && *p->xml != '>' && *p->xml != '/') - { - attvaluelen++; p->xml++; - if(p->xml >= p->xmlend) - return -1; - } - } - /*printf("%.*s='%.*s'\n", - attnamelen, attname, attvaluelen, attvalue);*/ - if(p->attfunc) - p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen); - } - p->xml++; - } - return -1; -} - -/* parseelt parse the xml stream and - * call the callback functions when needed... */ -void parseelt(struct xmlparser * p) -{ - int i; - const char * elementname; - while(p->xml < (p->xmlend - 1)) - { - if((p->xml)[0]=='<' && (p->xml)[1]!='?') - { - i = 0; elementname = ++p->xml; - while( !IS_WHITE_SPACE(*p->xml) - && (*p->xml!='>') && (*p->xml!='/') - ) - { - i++; p->xml++; - if (p->xml >= p->xmlend) - return; - } - if(i>0) - { - if(p->starteltfunc) - p->starteltfunc(p->data, elementname, i); - if(parseatt(p)) - return; - if(*p->xml!='/') - { - const char * data; - i = 0; data = ++p->xml; - if (p->xml >= p->xmlend) - return; - while( IS_WHITE_SPACE(*p->xml) ) - { - p->xml++; - if (p->xml >= p->xmlend) - return; - } - while(*p->xml!='<') - { - i++; p->xml++; - if (p->xml >= p->xmlend) - return; - } - if(i>0 && p->datafunc) - p->datafunc(p->data, data, i); - } - } - else if(*p->xml == '/') - { - i = 0; elementname = ++p->xml; - if (p->xml >= p->xmlend) - return; - while((*p->xml != '>')) - { - i++; p->xml++; - if (p->xml >= p->xmlend) - return; - } - if(p->endeltfunc) - p->endeltfunc(p->data, elementname, i); - p->xml++; - } - } - else - { - p->xml++; - } - } -} - -/* the parser must be initialized before calling this function */ -void parsexml(struct xmlparser * parser) -{ - parser->xml = parser->xmlstart; - parser->xmlend = parser->xmlstart + parser->xmlsize; - parseelt(parser); -} - - diff --git a/miniupnpc/minixml.h b/miniupnpc/minixml.h deleted file mode 100644 index 857c70ee9..000000000 --- a/miniupnpc/minixml.h +++ /dev/null @@ -1,37 +0,0 @@ -/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */ -/* minimal xml parser - * - * Project : miniupnp - * Website : http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided in this distribution. - * */ -#ifndef __MINIXML_H__ -#define __MINIXML_H__ -#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) - -/* if a callback function pointer is set to NULL, - * the function is not called */ -struct xmlparser { - const char *xmlstart; - const char *xmlend; - const char *xml; /* pointer to current character */ - int xmlsize; - void * data; - void (*starteltfunc) (void *, const char *, int); - void (*endeltfunc) (void *, const char *, int); - void (*datafunc) (void *, const char *, int); - void (*attfunc) (void *, const char *, int, const char *, int); -}; - -/* parsexml() - * the xmlparser structure must be initialized before the call - * the following structure members have to be initialized : - * xmlstart, xmlsize, data, *func - * xml is for internal usage, xmlend is computed automatically */ -void parsexml(struct xmlparser *); - -#endif - diff --git a/miniupnpc/minixmlvalid.c b/miniupnpc/minixmlvalid.c deleted file mode 100644 index e7967befa..000000000 --- a/miniupnpc/minixmlvalid.c +++ /dev/null @@ -1,149 +0,0 @@ -/* $Id: minixmlvalid.c,v 1.2 2006/11/30 11:31:55 nanard Exp $ */ -/* MiniUPnP Project - * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ - * minixmlvalid.c : - * validation program for the minixml parser - * - * (c) 2006 Thomas Bernard */ - -#include -#include -#include -#include "minixml.h" - -/* xml event structure */ -struct event { - enum { ELTSTART, ELTEND, ATT, CHARDATA } type; - const char * data; - int len; -}; - -struct eventlist { - int n; - struct event * events; -}; - -/* compare 2 xml event lists - * return 0 if the two lists are equals */ -int evtlistcmp(struct eventlist * a, struct eventlist * b) -{ - int i; - struct event * ae, * be; - if(a->n != b->n) - return 1; - for(i=0; in; i++) - { - ae = a->events + i; - be = b->events + i; - if( (ae->type != be->type) - ||(ae->len != be->len) - ||memcmp(ae->data, be->data, ae->len)) - { - printf("Found a difference : %d '%.*s' != %d '%.*s'\n", - ae->type, ae->len, ae->data, - be->type, be->len, be->data); - return 1; - } - } - return 0; -} - -/* Test data */ -static const char xmldata[] = -"\n" -" " -"character data" -" \n \t" -"" -" \tchardata1chardata2" -""; - -static const struct event evtref[] = -{ - {ELTSTART, "xmlroot", 7}, - {ELTSTART, "elt1", 4}, - /* attributes */ - {CHARDATA, "character data", 14}, - {ELTEND, "elt1", 4}, - {ELTSTART, "elt1b", 5}, - {ELTSTART, "elt2a", 5}, - {ELTSTART, "elt2b", 5}, - {CHARDATA, "chardata1", 9}, - {ELTEND, "elt2b", 5}, - {ELTSTART, "elt2b", 5}, - {CHARDATA, "chardata2", 9}, - {ELTEND, "elt2b", 5}, - {ELTEND, "elt2a", 5}, - {ELTEND, "xmlroot", 7} -}; - -void startelt(void * data, const char * p, int l) -{ - struct eventlist * evtlist = data; - struct event * evt; - evt = evtlist->events + evtlist->n; - /*printf("startelt : %.*s\n", l, p);*/ - evt->type = ELTSTART; - evt->data = p; - evt->len = l; - evtlist->n++; -} - -void endelt(void * data, const char * p, int l) -{ - struct eventlist * evtlist = data; - struct event * evt; - evt = evtlist->events + evtlist->n; - /*printf("endelt : %.*s\n", l, p);*/ - evt->type = ELTEND; - evt->data = p; - evt->len = l; - evtlist->n++; -} - -void chardata(void * data, const char * p, int l) -{ - struct eventlist * evtlist = data; - struct event * evt; - evt = evtlist->events + evtlist->n; - /*printf("chardata : '%.*s'\n", l, p);*/ - evt->type = CHARDATA; - evt->data = p; - evt->len = l; - evtlist->n++; -} - -int testxmlparser(const char * xml, int size) -{ - int r; - struct eventlist evtlist; - struct eventlist evtlistref; - struct xmlparser parser; - evtlist.n = 0; - evtlist.events = malloc(sizeof(struct event)*100); - memset(&parser, 0, sizeof(parser)); - parser.xmlstart = xml; - parser.xmlsize = size; - parser.data = &evtlist; - parser.starteltfunc = startelt; - parser.endeltfunc = endelt; - parser.datafunc = chardata; - parsexml(&parser); - printf("%d events\n", evtlist.n); - /* compare */ - evtlistref.n = sizeof(evtref)/sizeof(struct event); - evtlistref.events = (struct event *)evtref; - r = evtlistcmp(&evtlistref, &evtlist); - free(evtlist.events); - return r; -} - -int main(int argc, char * * argv) -{ - int r; - r = testxmlparser(xmldata, sizeof(xmldata)-1); - if(r) - printf("minixml validation test failed\n"); - return r; -} - diff --git a/miniupnpc/testminixml.c b/miniupnpc/testminixml.c deleted file mode 100644 index 3d82527b7..000000000 --- a/miniupnpc/testminixml.c +++ /dev/null @@ -1,88 +0,0 @@ -/* $Id: testminixml.c,v 1.6 2006/11/19 22:32:35 nanard Exp $ - * testminixml.c - * test program for the "minixml" functions. - * Author : Thomas Bernard. - */ -#include -#include -#include -#include "minixml.h" -#include "igd_desc_parse.h" - -#ifdef WIN32 -#define NO_BZERO -#endif - -#ifdef NO_BZERO -#define bzero(p, n) memset(p, 0, n) -#endif - -/* ---------------------------------------------------------------------- */ -void printeltname1(void * d, const char * name, int l) -{ - int i; - printf("element "); - for(i=0;i -#include -#include -#ifdef WIN32 -#include -#define snprintf _snprintf -#endif -#include "miniwget.h" -#include "miniupnpc.h" -#include "upnpcommands.h" - -/* protofix() checks if protocol is "UDP" or "TCP" - * returns NULL if not */ -const char * protofix(const char * proto) -{ - static const char proto_tcp[4] = { 'T', 'C', 'P', 0}; - static const char proto_udp[4] = { 'U', 'D', 'P', 0}; - int i, b; - for(i=0, b=1; i<4; i++) - b = b && ( (proto[i] == proto_tcp[i]) - || (proto[i] == (proto_tcp[i] | 32)) ); - if(b) - return proto_tcp; - for(i=0, b=1; i<4; i++) - b = b && ( (proto[i] == proto_udp[i]) - || (proto[i] == (proto_udp[i] | 32)) ); - if(b) - return proto_udp; - return 0; -} - -static void DisplayInfos(struct UPNPUrls * urls, - struct IGDdatas * data) -{ - char connectionType[64]; - char status[64]; - unsigned int uptime; - unsigned int brUp, brDown; - UPNP_GetConnectionTypeInfo(urls->controlURL, - data->servicetype, - connectionType); - if(connectionType[0]) - printf("Connection Type : %s\n", connectionType); - else - printf("GetConnectionTypeInfo failed.\n"); - UPNP_GetStatusInfo(urls->controlURL, data->servicetype, status, &uptime); - printf("Status : %s, uptime=%u\n", status, uptime); - UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->servicetype_CIF, - &brDown, &brUp); - printf("MaxBitRateDown : %u bps MaxBitRateUp %u bps\n", brDown, brUp); -} - -static void GetConnectionStatus(struct UPNPUrls * urls, - struct IGDdatas * data) -{ - unsigned int bytessent, bytesreceived, packetsreceived, packetssent; - DisplayInfos(urls, data); - bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->servicetype_CIF); - bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->servicetype_CIF); - packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->servicetype_CIF); - packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->servicetype_CIF); - printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived); - printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived); -} - -static void ListRedirections(struct UPNPUrls * urls, - struct IGDdatas * data) -{ - int r; - int i = 0; - char index[6]; - char intClient[16]; - char intPort[6]; - char extPort[6]; - char protocol[4]; - char desc[80]; - char enabled[6]; - char rHost[64]; - char duration[16]; - /*unsigned int num=0; - UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num); - printf("PortMappingNumberOfEntries : %u\n", num);*/ - do { - snprintf(index, 6, "%d", i); - rHost[0] = '\0'; enabled[0] = '\0'; - duration[0] = '\0'; desc[0] = '\0'; - extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0'; - r = UPNP_GetGenericPortMappingEntry(urls->controlURL, data->servicetype, - index, - extPort, intClient, intPort, - protocol, desc, enabled, - rHost, duration); - if(r==0) - printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n" - " desc='%s' rHost='%s'\n", - i, protocol, extPort, intClient, intPort, - enabled, duration, - desc, rHost); - i++; - } while(r==0); -} - -/* Test function - * 1 - get connection type - * 2 - get extenal ip address - * 3 - Add port mapping - * 4 - get this port mapping from the IGD */ -static void SetRedirectAndTest(struct UPNPUrls * urls, - struct IGDdatas * data, - const char * iaddr, - const char * iport, - const char * eport, - const char * proto) -{ - char externalIPAddress[16]; - char intClient[16]; - char intPort[6]; - int r; - - if(!iaddr || !iport || !eport || !proto) - { - fprintf(stderr, "Wrong arguments\n"); - return; - } - proto = protofix(proto); - if(!proto) - { - fprintf(stderr, "invalid protocol\n"); - return; - } - - UPNP_GetExternalIPAddress(urls->controlURL, - data->servicetype, - externalIPAddress); - if(externalIPAddress[0]) - printf("ExternalIPAddress = %s\n", externalIPAddress); - else - printf("GetExternalIPAddress failed.\n"); - - r = UPNP_AddPortMapping(urls->controlURL, data->servicetype, - eport, iport, iaddr, 0, proto); - if(r==0) - printf("AddPortMapping(%s, %s, %s) failed\n", eport, iport, iaddr); - - UPNP_GetSpecificPortMappingEntry(urls->controlURL, - data->servicetype, - eport, proto, - intClient, intPort); - if(intClient[0]) - printf("InternalIP:Port = %s:%s\n", intClient, intPort); - else - printf("GetSpecificPortMappingEntry failed.\n"); - - printf("external %s:%s is redirected to internal %s:%s\n", - externalIPAddress, eport, intClient, intPort); -} - -static void -RemoveRedirect(struct UPNPUrls * urls, - struct IGDdatas * data, - const char * eport, - const char * proto) -{ - if(!proto || !eport) - { - fprintf(stderr, "invalid arguments\n"); - return; - } - proto = protofix(proto); - if(!proto) - { - fprintf(stderr, "protocol invalid\n"); - return; - } - UPNP_DeletePortMapping(urls->controlURL, data->servicetype, eport, proto); -} - - -/* sample upnp client program */ -int main(int argc, char ** argv) -{ - char command = 0; - struct UPNPDev * devlist; - char lanaddr[16]; /* my ip address on the LAN */ - int i; - -#ifdef WIN32 - WSADATA wsaData; - int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); - if(nResult != NO_ERROR) - { - fprintf(stderr, "WSAStartup() failed.\n"); - return -1; - } -#endif - printf("upnpc : miniupnp test client. (c) 2006 Thomas Bernard\n"); - printf("Go to http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/\n" - "for more information.\n"); - if(argc>=2 && (argv[1][0] == '-')) - command = argv[1][1]; - - if(!command || argc<2 || (command == 'a' && argc<6) - || (command == 'd' && argc<4) - || (command == 'r' && argc<4)) - { - fprintf(stderr, "Usage :\t%s -a ip port external_port protocol\tAdd port redirection\n", argv[0]); - fprintf(stderr, " \t%s -d external_port protocol\tDelete port redirection\n", argv[0]); - fprintf(stderr, " \t%s -s\t\t\t\tGet Connection status\n", argv[0]); - fprintf(stderr, " \t%s -l\t\t\t\tList redirections\n", argv[0]); - fprintf(stderr, " \t%s -r port1 protocol1 [port2 protocol2] [...]\n\t\t\t\tAdd all redirections to the current host\n", argv[0]); - fprintf(stderr, "\nprotocol is UDP or TCP\n"); - return 1; - } - - devlist = upnpDiscover(2000); - if(devlist) - { - struct UPNPDev * device; - struct UPNPUrls urls; - struct IGDdatas data; - printf("List of UPNP devices found on the network :\n"); - for(device = devlist; device; device = device->pNext) - { - printf("\n desc: %s\n st: %s\n", - device->descURL, device->st); - } - putchar('\n'); - if(UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))) - { - printf("Found valid IGD : %s\n", urls.controlURL); - printf("Local LAN ip address : %s\n", lanaddr); - #if 0 - printf("getting \"%s\"\n", urls.ipcondescURL); - descXML = miniwget(urls.ipcondescURL, &descXMLsize); - if(descXML) - { - /*fwrite(descXML, 1, descXMLsize, stdout);*/ - free(descXML); descXML = NULL; - } - #endif - - switch(command) - { - case 'l': - DisplayInfos(&urls, &data); - ListRedirections(&urls, &data); - break; - case 'a': - SetRedirectAndTest(&urls, &data, argv[2], argv[3], argv[4], argv[5]); - break; - case 'd': - RemoveRedirect(&urls, &data, argv[2], argv[3]); - break; - case 's': - GetConnectionStatus(&urls, &data); - break; - case 'r': - for(i=2; i -#include -#include -#include "upnpcommands.h" -#include "miniupnpc.h" - -static unsigned int -my_atoui(const char * s) -{ - return (unsigned int)strtoul(s, NULL, 0); -} - -/* - * */ -unsigned int -UPNP_GetTotalBytesSent(const char * controlURL, - const char * servicetype) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - unsigned int r = 0; - char * p; - simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesSent", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - /*DisplayNameValueList(buffer, bufsize);*/ - p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent"); - if(p) - r = my_atoui(p); - ClearNameValueList(&pdata); - return r; -} - -/* - * */ -unsigned int -UPNP_GetTotalBytesReceived(const char * controlURL, - const char * servicetype) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - unsigned int r = 0; - char * p; - simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalBytesReceived", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - /*DisplayNameValueList(buffer, bufsize);*/ - p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived"); - if(p) - r = my_atoui(p); - ClearNameValueList(&pdata); - return r; -} - -/* - * */ -unsigned int -UPNP_GetTotalPacketsSent(const char * controlURL, - const char * servicetype) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - unsigned int r = 0; - char * p; - simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsSent", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - /*DisplayNameValueList(buffer, bufsize);*/ - p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent"); - if(p) - r = my_atoui(p); - ClearNameValueList(&pdata); - return r; -} - -/* - * */ -unsigned int -UPNP_GetTotalPacketsReceived(const char * controlURL, - const char * servicetype) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - unsigned int r = 0; - char * p; - simpleUPnPcommand(-1, controlURL, servicetype, "GetTotalPacketsReceived", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - /*DisplayNameValueList(buffer, bufsize);*/ - p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived"); - if(p) - r = my_atoui(p); - ClearNameValueList(&pdata); - return r; -} - -/* UPNP_GetStatusInfo() call the corresponding UPNP method - * returns the current status and uptime */ -void UPNP_GetStatusInfo(const char * controlURL, - const char * servicetype, - char * status, - unsigned int * uptime) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - char * p; - char* up; - - if(!status && !uptime) - return; - - simpleUPnPcommand(-1, controlURL, servicetype, "GetStatusInfo", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - /*DisplayNameValueList(buffer, bufsize);*/ - up = GetValueFromNameValueList(&pdata, "NewUptime"); - p = GetValueFromNameValueList(&pdata, "NewConnectionStatus"); - - if(status) - { - if(p){ - strncpy(status, p, 64 ); - status[63] = '\0'; - }else - status[0]= '\0'; - } - - if(uptime){ - if(p) - sscanf(up,"%u",uptime); - else - uptime = 0; - } - - ClearNameValueList(&pdata); -} - -/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method - * returns the connection type */ -void UPNP_GetConnectionTypeInfo(const char * controlURL, - const char * servicetype, - char * connectionType) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - char * p; - - if(!connectionType) - return; - - - simpleUPnPcommand(-1, controlURL, servicetype, - "GetConnectionTypeInfo", 0, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - p = GetValueFromNameValueList(&pdata, "NewConnectionType"); - /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/ - /* PossibleConnectionTypes will have several values.... */ - if(connectionType) - { - if(p){ - strncpy(connectionType, p, 64 ); - connectionType[63] = '\0'; - } else - connectionType[0] = '\0'; - } - ClearNameValueList(&pdata); -} - -/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method. - * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth. - * One of the values can be null - * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only - * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */ -void UPNP_GetLinkLayerMaxBitRates(const char * controlURL, const char * servicetype, unsigned int * bitrateDown, unsigned int* bitrateUp) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - char * down; - char* up; - - if(!bitrateDown && !bitrateUp) - return; - - /* shouldn't we use GetCommonLinkProperties ? */ - simpleUPnPcommand(-1, controlURL, servicetype, - "GetCommonLinkProperties", 0, buffer, &bufsize); - /*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize);*/ - /*DisplayNameValueList(buffer, bufsize);*/ - ParseNameValue(buffer, bufsize, &pdata); - /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/ - /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/ - down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate"); - up = GetValueFromNameValueList(&pdata, "NewLayer1UpstreamMaxBitRate"); - /*GetValueFromNameValueList(&pdata, "NewWANAccessType");*/ - /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkSatus");*/ - - if(bitrateDown) - { - if(down) - sscanf(down,"%u",bitrateDown); - else - *bitrateDown = 0; - } - - if(bitrateUp) - { - if(up) - sscanf(up,"%u",bitrateUp); - else - *bitrateUp = 0; - } - ClearNameValueList(&pdata); -} - - -/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. - * if the third arg is not null the value is copied to it. - * at least 16 bytes must be available */ -void UPNP_GetExternalIPAddress(const char * controlURL, const char * servicetype, char * extIpAdd) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - char * p; - - if(!extIpAdd) - return; - - simpleUPnPcommand(-1, controlURL, servicetype, "GetExternalIPAddress", 0, buffer, &bufsize); - /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetExternalIPAddress", 0, buffer, &bufsize);*/ - /*DisplayNameValueList(buffer, bufsize);*/ - ParseNameValue(buffer, bufsize, &pdata); - /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/ - p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress"); - - if(p){ - strncpy(extIpAdd, p, 16 ); - extIpAdd[15] = '\0'; - }else - extIpAdd[0] = '\0'; - - ClearNameValueList(&pdata); -} - -int -UPNP_AddPortMapping(const char * controlURL, const char * servicetype, - const char * extPort, - const char * inPort, - const char * inClient, - const char * desc, - const char * proto) -{ - struct UPNParg * AddPortMappingArgs; - char buffer[4096]; - int bufsize = 4096; - struct NameValueParserData pdata; - const char * resVal; - int ret; - - if(!inPort || !inClient) - return 0; - - AddPortMappingArgs = calloc(9, sizeof(struct UPNParg)); - AddPortMappingArgs[0].elt = "NewRemoteHost"; - AddPortMappingArgs[1].elt = "NewExternalPort"; - AddPortMappingArgs[1].val = extPort; - AddPortMappingArgs[2].elt = "NewProtocol"; - AddPortMappingArgs[2].val = proto; - AddPortMappingArgs[3].elt = "NewInternalPort"; - AddPortMappingArgs[3].val = inPort; - AddPortMappingArgs[4].elt = "NewInternalClient"; - AddPortMappingArgs[4].val = inClient; - AddPortMappingArgs[5].elt = "NewEnabled"; - AddPortMappingArgs[5].val = "1"; - AddPortMappingArgs[6].elt = "NewPortMappingDescription"; - AddPortMappingArgs[6].val = desc?desc:"libminiupnpc"; - AddPortMappingArgs[7].elt = "NewLeaseDuration"; - AddPortMappingArgs[7].val = "0"; - simpleUPnPcommand(-1, controlURL, servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize); - /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "AddPortMapping", AddPortMappingArgs, buffer, &bufsize);*/ - /*DisplayNameValueList(buffer, bufsize);*/ - /*buffer[bufsize] = '\0';*/ - /*puts(buffer);*/ - ParseNameValue(buffer, bufsize, &pdata); - resVal = GetValueFromNameValueList(&pdata, "errorCode"); - ret = resVal?0:1; - /* Do something with resVal if not null ! */ - /*printf("AddPortMapping errorCode = '%s'\n", resVal); */ - free(AddPortMappingArgs); - return ret; -} - -void UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, - const char * extPort, const char * proto) -{ - /*struct NameValueParserData pdata;*/ - struct UPNParg * DeletePortMappingArgs; - char buffer[4096]; - int bufsize = 4096; - - if(!extPort) - return; - - DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg)); - DeletePortMappingArgs[0].elt = "NewRemoteHost"; - DeletePortMappingArgs[1].elt = "NewExternalPort"; - DeletePortMappingArgs[1].val = extPort; - DeletePortMappingArgs[2].elt = "NewProtocol"; - DeletePortMappingArgs[2].val = proto; - simpleUPnPcommand(-1, controlURL, servicetype, - "DeletePortMapping", - DeletePortMappingArgs, buffer, &bufsize); - /*DisplayNameValueList(buffer, bufsize);*/ - free(DeletePortMappingArgs); -} - -int UPNP_GetGenericPortMappingEntry(const char * controlURL, - const char * servicetype, - const char * index, - char * extPort, - char * intClient, - char * intPort, - char * protocol, - char * desc, - char * enabled, - char * rHost, - char * duration) -{ - struct NameValueParserData pdata; - struct UPNParg * GetPortMappingArgs; - char buffer[4096]; - int bufsize = 4096; - char * p; - int r = -1; - intClient[0] = '\0'; - intPort[0] = '\0'; - GetPortMappingArgs = calloc(2, sizeof(struct UPNParg)); - GetPortMappingArgs[0].elt = "NewPortMappingIndex"; - GetPortMappingArgs[0].val = index; - simpleUPnPcommand(-1, controlURL, servicetype, - "GetGenericPortMappingEntry", - GetPortMappingArgs, buffer, &bufsize); - ParseNameValue(buffer, bufsize, &pdata); - p = GetValueFromNameValueList(&pdata, "NewRemoteHost"); - if(p && rHost) - { - strncpy(rHost, p, 64); - rHost[63] = '\0'; - } - p = GetValueFromNameValueList(&pdata, "NewExternalPort"); - if(p && extPort) - { - strncpy(extPort, p, 6); - extPort[5] = '\0'; - r = 0; - } - p = GetValueFromNameValueList(&pdata, "NewProtocol"); - if(p && protocol) - { - strncpy(protocol, p, 4); - protocol[3] = '\0'; - } - p = GetValueFromNameValueList(&pdata, "NewInternalClient"); - if(p && intClient) - { - strncpy(intClient, p, 16); - intClient[15] = '\0'; - r = 0; - } - p = GetValueFromNameValueList(&pdata, "NewInternalPort"); - if(p && intPort) - { - strncpy(intPort, p, 6); - intPort[5] = '\0'; - } - p = GetValueFromNameValueList(&pdata, "NewEnabled"); - if(p && enabled) - { - strncpy(enabled, p, 4); - enabled[3] = '\0'; - } - p = GetValueFromNameValueList(&pdata, "NewPortMappingDescription"); - if(p && desc) - { - strncpy(desc, p, 80); - desc[79] = '\0'; - } - p = GetValueFromNameValueList(&pdata, "NewLeaseDuration"); - if(p && duration) - { - strncpy(duration, p, 16); - duration[15] = '\0'; - } - ClearNameValueList(&pdata); - free(GetPortMappingArgs); - return r; -} - -void UPNP_GetPortMappingNumberOfEntries(const char * controlURL, const char * servicetype, unsigned int * numEntries) -{ - struct NameValueParserData pdata; - char buffer[4096]; - int bufsize = 4096; - char* p; - simpleUPnPcommand(-1, controlURL, servicetype, "GetPortMappingNumberOfEntries", 0, buffer, &bufsize); -#ifndef NDEBUG - DisplayNameValueList(buffer, bufsize); -#endif - ParseNameValue(buffer, bufsize, &pdata); - p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries"); - - if(numEntries && p) - { - sscanf(p,"%u",numEntries); - } - ClearNameValueList(&pdata); -} - -/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping - * the result is returned in the intClient and intPort strings - * please provide 16 and 6 bytes of data */ -void -UPNP_GetSpecificPortMappingEntry(const char * controlURL, - const char * servicetype, - const char * extPort, - const char * proto, - char * intClient, - char * intPort) -{ - struct NameValueParserData pdata; - struct UPNParg * GetPortMappingArgs; - char buffer[4096]; - int bufsize = 4096; - char * p; - - if(!intPort && !intClient && !extPort) - return; - - GetPortMappingArgs = calloc(4, sizeof(struct UPNParg)); - GetPortMappingArgs[0].elt = "NewRemoteHost"; - GetPortMappingArgs[1].elt = "NewExternalPort"; - GetPortMappingArgs[1].val = extPort; - GetPortMappingArgs[2].elt = "NewProtocol"; - GetPortMappingArgs[2].val = proto; - simpleUPnPcommand(-1, controlURL, servicetype, - "GetSpecificPortMappingEntry", - GetPortMappingArgs, buffer, &bufsize); - /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetSpecificPortMappingEntry", AddPortMappingArgs, buffer, &bufsize); */ - /*DisplayNameValueList(buffer, bufsize);*/ - ParseNameValue(buffer, bufsize, &pdata); - p = GetValueFromNameValueList(&pdata, "NewInternalClient"); - - if(intClient) - { - if(p){ - strncpy(intClient, p, 16); - intClient[15] = '\0'; - }else - intClient[0] = '\0'; - } - - p = GetValueFromNameValueList(&pdata, "NewInternalPort"); - if(intPort) - { - if(p){ - strncpy(intPort, p, 6); - intPort[5] = '\0'; - }else - intPort[0] = '\0'; - } - - ClearNameValueList(&pdata); - free(GetPortMappingArgs); -} - - diff --git a/miniupnpc/upnpcommands.h b/miniupnpc/upnpcommands.h deleted file mode 100644 index ae980a0a3..000000000 --- a/miniupnpc/upnpcommands.h +++ /dev/null @@ -1,104 +0,0 @@ -/* $Id: upnpcommands.h,v 1.10 2007/01/29 20:27:24 nanard Exp $ */ -/* Miniupnp project : http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2005-2006 Thomas Bernard - * This software is subject to the conditions detailed in the - * LICENCE file provided within this distribution */ -#ifndef __UPNPCOMMANDS_H__ -#define __UPNPCOMMANDS_H__ - -#include "upnpreplyparse.h" -#include "declspec.h" - -#ifdef __cplusplus -extern "C" { -#endif - -LIBSPEC unsigned int -UPNP_GetTotalBytesSent(const char * controlURL, - const char * servicetype); - -LIBSPEC unsigned int -UPNP_GetTotalBytesReceived(const char * controlURL, - const char * servicetype); - -LIBSPEC unsigned int -UPNP_GetTotalPacketsSent(const char * controlURL, - const char * servicetype); - -LIBSPEC unsigned int -UPNP_GetTotalPacketsReceived(const char * controlURL, - const char * servicetype); - -LIBSPEC void -UPNP_GetStatusInfo(const char * controlURL, - const char * servicetype, - char * status, - unsigned int * uptime); - -LIBSPEC void -UPNP_GetConnectionTypeInfo(const char * controlURL, - const char * servicetype, - char * connectionType); - -/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. - * if the third arg is not null the value is copied to it. - * at least 16 bytes must be available */ -LIBSPEC void -UPNP_GetExternalIPAddress(const char * controlURL, - const char * servicetype, - char * extIpAdd); - -LIBSPEC void -UPNP_GetLinkLayerMaxBitRates(const char* controlURL, - const char* servicetype, - unsigned int * bitrateDown, - unsigned int * bitrateUp); - -/* Returns zero if unable to add the port mapping, otherwise non-zero - * to indicate success */ -LIBSPEC int -UPNP_AddPortMapping(const char * controlURL, const char * servicetype, - const char * extPort, - const char * inPort, - const char * inClient, - const char * desc, - const char * proto); - -LIBSPEC void -UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, - const char * extPort, const char * proto); - -LIBSPEC void -UPNP_GetPortMappingNumberOfEntries(const char* controlURL, const char* servicetype, unsigned int * num); - -/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping - * the result is returned in the intClient and intPort strings - * please provide 16 and 6 bytes of data */ -LIBSPEC void -UPNP_GetSpecificPortMappingEntry(const char * controlURL, - const char * servicetype, - const char * extPort, - const char * proto, - char * intClient, - char * intPort); - -LIBSPEC int -UPNP_GetGenericPortMappingEntry(const char * controlURL, - const char * servicetype, - const char * index, - char * extPort, - char * intClient, - char * intPort, - char * protocol, - char * desc, - char * enabled, - char * rHost, - char * duration); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/miniupnpc/upnpreplyparse.c b/miniupnpc/upnpreplyparse.c deleted file mode 100644 index 9edc15808..000000000 --- a/miniupnpc/upnpreplyparse.c +++ /dev/null @@ -1,122 +0,0 @@ -/* $Id: upnpreplyparse.c,v 1.8 2007/02/07 22:38:09 nanard Exp $ */ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006 Thomas Bernard - * This software is subject to the conditions detailed - * in the LICENCE file provided within the distribution */ - -#include -#include -#include - -#include "upnpreplyparse.h" -#include "minixml.h" - -static void -NameValueParserStartElt(void * d, const char * name, int l) -{ - struct NameValueParserData * data = (struct NameValueParserData *)d; - if(l>63) - l = 63; - memcpy(data->curelt, name, l); - data->curelt[l] = '\0'; -} - -static void -NameValueParserGetData(void * d, const char * datas, int l) -{ - struct NameValueParserData * data = (struct NameValueParserData *)d; - struct NameValue * nv; - nv = malloc(sizeof(struct NameValue)); - if(l>63) - l = 63; - strncpy(nv->name, data->curelt, 64); - nv->name[63] = '\0'; - memcpy(nv->value, datas, l); - nv->value[l] = '\0'; - LIST_INSERT_HEAD( &(data->head), nv, entries); -} - -void -ParseNameValue(const char * buffer, int bufsize, - struct NameValueParserData * data) -{ - struct xmlparser parser; - LIST_INIT(&(data->head)); - /* init xmlparser object */ - parser.xmlstart = buffer; - parser.xmlsize = bufsize; - parser.data = data; - parser.starteltfunc = NameValueParserStartElt; - parser.endeltfunc = 0; - parser.datafunc = NameValueParserGetData; - parser.attfunc = 0; - parsexml(&parser); -} - -void -ClearNameValueList(struct NameValueParserData * pdata) -{ - struct NameValue * nv; - while((nv = pdata->head.lh_first) != NULL) - { - LIST_REMOVE(nv, entries); - free(nv); - } -} - -char * -GetValueFromNameValueList(struct NameValueParserData * pdata, - const char * Name) -{ - struct NameValue * nv; - char * p = NULL; - for(nv = pdata->head.lh_first; - (nv != NULL) && (p == NULL); - nv = nv->entries.le_next) - { - if(strcmp(nv->name, Name) == 0) - p = nv->value; - } - return p; -} - -char * -GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, - const char * Name) -{ - struct NameValue * nv; - char * p = NULL; - char * pname; - for(nv = pdata->head.lh_first; - (nv != NULL) && (p == NULL); - nv = nv->entries.le_next) - { - pname = strrchr(nv->name, ':'); - if(pname) - pname++; - else - pname = nv->name; - if(strcmp(pname, Name)==0) - p = nv->value; - } - return p; -} - -/* debug all-in-one function - * do parsing then display to stdout */ -void -DisplayNameValueList(char * buffer, int bufsize) -{ - struct NameValueParserData pdata; - struct NameValue * nv; - ParseNameValue(buffer, bufsize, &pdata); - for(nv = pdata.head.lh_first; - nv != NULL; - nv = nv->entries.le_next) - { - printf("%s = %s\n", nv->name, nv->value); - } - ClearNameValueList(&pdata); -} - diff --git a/miniupnpc/upnpreplyparse.h b/miniupnpc/upnpreplyparse.h deleted file mode 100644 index beb1686b5..000000000 --- a/miniupnpc/upnpreplyparse.h +++ /dev/null @@ -1,60 +0,0 @@ -/* $Id: upnpreplyparse.h,v 1.6 2007/02/07 22:38:09 nanard Exp $ */ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006 Thomas Bernard - * This software is subject to the conditions detailed - * in the LICENCE file provided within the distribution */ - -#ifndef __UPNPREPLYPARSE_H__ -#define __UPNPREPLYPARSE_H__ - -#if defined(sun) || defined(__sun) || defined(WIN32) -#include "bsdqueue.h" -#else -#include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct NameValue { - LIST_ENTRY(NameValue) entries; - char name[64]; - char value[64]; -}; - -struct NameValueParserData { - LIST_HEAD(listhead, NameValue) head; - char curelt[64]; -}; - -/* ParseNameValue() */ -void -ParseNameValue(const char * buffer, int bufsize, - struct NameValueParserData * data); - -/* ClearNameValueList() */ -void -ClearNameValueList(struct NameValueParserData * pdata); - -/* GetValueFromNameValueList() */ -char * -GetValueFromNameValueList(struct NameValueParserData * pdata, - const char * Name); - -/* GetValueFromNameValueListIgnoreNS() */ -char * -GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, - const char * Name); - -/* DisplayNameValueList() */ -void -DisplayNameValueList(char * buffer, int bufsize); - -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/miniupnpc/validateminixml b/miniupnpc/validateminixml deleted file mode 100644 index e69de29bb..000000000 diff --git a/setup.py b/setup.py index 6ce477e52..95997ef2c 100644 --- a/setup.py +++ b/setup.py @@ -146,21 +146,28 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/allocate_resources.cpp', 'libtorrent/src/bandwidth_manager.cpp', 'libtorrent/src/bt_peer_connection.cpp', + 'libtorrent/src/connection_queue.cpp', 'libtorrent/src/entry.cpp', 'libtorrent/src/escape_string.cpp', 'libtorrent/src/file.cpp', 'libtorrent/src/file_pool.cpp', + 'libtorrent/src/http_connection.cpp', + 'libtorrent/src/http_stream.cpp', 'libtorrent/src/http_tracker_connection.cpp', 'libtorrent/src/identify_client.cpp', + 'libtorrent/src/instantiate_connection.cpp', 'libtorrent/src/ip_filter.cpp', 'libtorrent/src/logger.cpp', + 'libtorrent/src/lsd.cpp', 'libtorrent/src/metadata_transfer.cpp', + 'libtorrent/src/natpmp.cpp', 'libtorrent/src/peer_connection.cpp', 'libtorrent/src/piece_picker.cpp', 'libtorrent/src/policy.cpp', 'libtorrent/src/session.cpp', 'libtorrent/src/session_impl.cpp', 'libtorrent/src/sha1.cpp', + 'libtorrent/src/socks5_stream.cpp', 'libtorrent/src/stat.cpp', 'libtorrent/src/storage.cpp', 'libtorrent/src/torrent.cpp', @@ -168,6 +175,7 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/torrent_info.cpp', 'libtorrent/src/tracker_manager.cpp', 'libtorrent/src/udp_tracker_connection.cpp', + 'libtorrent/src/upnp.cpp', 'libtorrent/src/ut_pex.cpp', 'libtorrent/src/web_peer_connection.cpp', 'libtorrent/src/kademlia/closest_nodes.cpp', @@ -180,18 +188,6 @@ deluge_core = Extension('deluge_core', 'libtorrent/src/kademlia/rpc_manager.cpp', 'libtorrent/src/kademlia/traversal_algorithm.cpp']) -upnp = Extension('upnp', - include_dirs = ['./miniupnpc'], - sources=['src/python-upnp.c', - 'miniupnpc/igd_desc_parse.c', - 'miniupnpc/minisoap.c', - 'miniupnpc/miniupnpc.c', - 'miniupnpc/miniwget.c', - 'miniupnpc/minixml.c', - 'miniupnpc/minixmlvalid.c', - 'miniupnpc/upnpcommands.c', - 'miniupnpc/upnpreplyparse.c']) - # Thanks to Iain Nicol for code to save the location for installed prefix # At runtime, we need to know where we installed the data to. @@ -313,6 +309,6 @@ setup(name=NAME, fullname=FULLNAME, version=VERSION, package_dir = {'deluge': 'src'}, data_files=data, ext_package='deluge', - ext_modules=[deluge_core, upnp], + ext_modules=[deluge_core], cmdclass=cmdclass ) diff --git a/src/deluge_core.cpp b/src/deluge_core.cpp index 6079f16e1..18a6d1dcb 100644 --- a/src/deluge_core.cpp +++ b/src/deluge_core.cpp @@ -35,12 +35,10 @@ #include -#include -#include #include -#include #include #include +#include #include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" @@ -53,6 +51,8 @@ #include "libtorrent/file_pool.hpp" #include "libtorrent/file.hpp" #include "libtorrent/torrent_info.hpp" +#include "libtorrent/upnp.hpp" +#include "libtorrent/natpmp.hpp" #include "libtorrent/extensions/metadata_transfer.hpp" #include "libtorrent/extensions/ut_pex.hpp" @@ -1090,6 +1090,11 @@ static PyObject *torrent_get_DHT_info(PyObject *self, PyObject *args) // The following function contains code by Christophe Dumez and Arvid Norberg static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) { + using namespace libtorrent; + using namespace boost::filesystem; + + path::default_name_check(no_check); + char *destination, *comment, *creator_str, *input, *trackers; python_long piece_size; if (!PyArg_ParseTuple(args, "ssssis", @@ -1104,11 +1109,12 @@ static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) boost::filesystem::path full_path = complete(boost::filesystem::path(input)); boost::filesystem::ofstream out(complete(boost::filesystem::path(destination)), std::ios_base::binary); - file_pool fp; internal_add_files(t, full_path.branch_path(), full_path.leaf()); t.set_piece_size(piece_size); - storage st(t, full_path.branch_path(), fp); + file_pool fp; + boost::scoped_ptr st( + default_storage_constructor(t, full_path.branch_path(), fp)); std::string stdTrackers(trackers); unsigned long index = 0, next = stdTrackers.find("\n"); @@ -1127,7 +1133,7 @@ static PyObject *torrent_create_torrent(PyObject *self, PyObject *args) std::vector buf(piece_size); for (int i = 0; i < num; ++i) { - st.read(&buf[0], i, 0, t.piece_size(i)); + st->read(&buf[0], i, 0, t.piece_size(i)); hasher h(&buf[0], t.piece_size(i)); t.set_hash(i, h.final()); } diff --git a/src/python-upnp.c b/src/python-upnp.c deleted file mode 100644 index 81b53bd9b..000000000 --- a/src/python-upnp.c +++ /dev/null @@ -1,113 +0,0 @@ -/* python-miniupnp - * - * Copyright (C) Zach Tibbitts 2007 - * - * - * You may redistribute it and/or modify it under the terms of the - * GNU General Public License, as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * python-miniupnp is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with python-miniupnp. If not, write to: - * The Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor - * Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -// upnpDiscover() -static PyObject* -_upnpDiscover(PyObject* self, PyObject* args) -{ - int delay, i, k; - if (!PyArg_ParseTuple(args, "i", &delay)) - return NULL; - struct UPNPDev* discover = upnpDiscover(delay); - struct UPNPDev* next = discover; - // First, we have to find out how many elements are in the list - i = 0; - while(next) - { - i++; - next = next->pNext; - } - // Turn the linked list into a python object - PyObject* list = PyList_New(i); - for(k=0;kdescURL, - "st", discover->st, - "buffer", discover->buffer); - PyList_SET_ITEM(list, k, tmp); - } - return list; -} - -static PyObject* -_miniwget(PyObject* self, PyObject* args) -{ - char* url; - int* size; - if (!PyArg_ParseTuple(args, "si", &url, &size)) - return NULL; - miniwget(url, size); - return NULL; -} - -static PyObject* -_parserootdesc(PyObject* self, PyObject* args) -{ - return NULL; -} - -static PyObject* -_GetUPNPUrls(PyObject* self, PyObject* args) -{ - return NULL; -} - -static PyObject* -_simpleUPnPcommand(PyObject* self, PyObject* args) -{ - int s; - const char * url; - const char * service; - const char * action; - struct UPNParg * arguments; - char * buffer; - int * bufsize; - if (!PyArg_ParseTuple(args, "isssOsi", &s, &url, &service, &action, - &arguments, &buffer, &bufsize)) - return NULL; - int result; - result = simpleUPnPcommand(s, url, service, action, arguments, buffer, bufsize); - return Py_BuildValue("i", result); -} - -static PyMethodDef upnp_methods[] = { - {"simple_upnp_command", (PyCFunction)_simpleUPnPcommand, METH_VARARGS, "."}, - {"discover", (PyCFunction)_upnpDiscover, METH_VARARGS, "."}, - {"miniwget", (PyCFunction)_miniwget, METH_VARARGS, "."}, - {"parse_root_desc", (PyCFunction)_parserootdesc, METH_VARARGS, "."}, - {"get_upnp_urls", (PyCFunction)_GetUPNPUrls, METH_VARARGS, "."}, - {NULL} -}; - -void -initupnp(void) -{ - Py_InitModule("upnp", upnp_methods); -}