diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 125650716..05e5bd16a 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -213,16 +213,12 @@ class TorrentManager(component.Component): self.config["state_location"], torrent_id + ".fastresume"), "rb") - try: - fastresume = lt.bdecode(_file.read()) - except RuntimeError, e: - log.warning("Unable to bdecode fastresume file: %s", e) - + fastresume = _file.read() _file.close() except IOError, e: log.debug("Unable to load .fastresume: %s", e) - - return fastresume + + return str(fastresume) def add(self, torrent_info=None, state=None, options=None, save_state=True, filedump=None, filename=None): @@ -291,7 +287,7 @@ class TorrentManager(component.Component): options[key] = self.config[key] add_torrent_params["ti"] = torrent_info - add_torrent_params["resume_data"] = None + add_torrent_params["resume_data"] = "" #log.info("Adding torrent: %s", filename) log.debug("options: %s", options) diff --git a/libtorrent/bindings/python/src/peer_plugin.cpp b/libtorrent/bindings/python/src/peer_plugin.cpp index ea431c3d5..eaca35484 100755 --- a/libtorrent/bindings/python/src/peer_plugin.cpp +++ b/libtorrent/bindings/python/src/peer_plugin.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ namespace return this->peer_plugin::on_handshake(reserved_bits); } - bool on_extension_handshake(entry const& e) + bool on_extension_handshake(lazy_entry const& e) { if (override f = this->get_override("on_extension_handshake")) return f(e); @@ -50,7 +51,7 @@ namespace return peer_plugin::on_extension_handshake(e); } - bool default_on_extension_handshake(entry const& e) + bool default_on_extension_handshake(lazy_entry const& e) { return this->peer_plugin::on_extension_handshake(e); } diff --git a/libtorrent/bindings/python/src/session.cpp b/libtorrent/bindings/python/src/session.cpp index 50d433dd0..294d9d534 100755 --- a/libtorrent/bindings/python/src/session.cpp +++ b/libtorrent/bindings/python/src/session.cpp @@ -97,35 +97,38 @@ namespace allow_threading_guard guard; s.add_extension(invoke_extension_factory(e)); } - + torrent_handle add_torrent(session& s, dict params) { add_torrent_params p; - + if (params.has_key("ti")) - { - boost::intrusive_ptr ti = new torrent_info( - extract(params["ti"])); - p.ti = ti; - } + p.ti = new torrent_info(extract(params["ti"])); + + std::string url; if (params.has_key("tracker_url")) { - std::string url = extract(params["tracker_url"]); + url = extract(params["tracker_url"]); p.tracker_url = url.c_str(); } if (params.has_key("info_hash")) - { - sha1_hash info_hash = extract(params["info_hash"]); - p.info_hash = info_hash; - } + p.info_hash = extract(params["info_hash"]); + std::string name; if (params.has_key("name")) { - std::string name = extract(params["name"]); + name = extract(params["name"]); p.name = name.c_str(); } p.save_path = fs::path(extract(params["save_path"])); - entry resume = extract(params["resume_data"]); - p.resume_data = &resume; + + std::vector resume_buf; + if (params.has_key("resume_data")) + { + std::string resume = extract(params["resume_data"]); + resume_buf.resize(resume.size()); + std::memcpy(&resume_buf[0], &resume[0], resume.size()); + p.resume_data = &resume_buf; + } p.storage_mode = extract(params["storage_mode"]); p.paused = params["paused"]; p.auto_managed = params["auto_managed"]; @@ -133,7 +136,7 @@ namespace return s.add_torrent(p); } - + void start_natpmp(session& s) { allow_threading_guard guard; @@ -147,7 +150,7 @@ namespace s.start_upnp(); return; } - + list get_torrents(session& s) { list ret; @@ -334,6 +337,9 @@ void bind_session() .def("set_ip_filter", allow_threads(&session::set_ip_filter), session_set_ip_filter_doc) .def("find_torrent", allow_threads(&session::find_torrent)) .def("get_torrents", &get_torrents) + .def("pause", allow_threads(&session::pause)) + .def("resume", allow_threads(&session::resume)) + .def("is_paused", allow_threads(&session::is_paused)) ; register_ptr_to_python >(); diff --git a/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index 22ed68d49..fa4f0d276 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/include/libtorrent/aux_/session_impl.hpp @@ -189,6 +189,10 @@ namespace libtorrent , int nat_transport); bool is_aborted() const { return m_abort; } + bool is_paused() const { return m_paused; } + + void pause(); + void resume(); void set_ip_filter(ip_filter const& f); void set_port_filter(port_filter const& f); @@ -454,6 +458,9 @@ namespace libtorrent // should exit volatile bool m_abort; + // is true if the session is paused + bool m_paused; + // the max number of unchoked peers as set by the user int m_max_uploads; diff --git a/libtorrent/include/libtorrent/bt_peer_connection.hpp b/libtorrent/include/libtorrent/bt_peer_connection.hpp index 09b665b4a..c45f7cb31 100755 --- a/libtorrent/include/libtorrent/bt_peer_connection.hpp +++ b/libtorrent/include/libtorrent/bt_peer_connection.hpp @@ -389,7 +389,7 @@ private: // initialized during write_pe1_2_dhkey, and destroyed on // creation of m_RC4_handler. Cannot reinitialize once // initialized. - boost::scoped_ptr m_DH_key_exchange; + boost::scoped_ptr m_dh_key_exchange; // if RC4 is negotiated, this is used for // encryption/decryption during the entire session. Destroyed diff --git a/libtorrent/include/libtorrent/extensions.hpp b/libtorrent/include/libtorrent/extensions.hpp index 3e85b125b..b06a0fc74 100644 --- a/libtorrent/include/libtorrent/extensions.hpp +++ b/libtorrent/include/libtorrent/extensions.hpp @@ -56,6 +56,7 @@ namespace libtorrent struct peer_request; class peer_connection; class entry; + struct lazy_entry; struct disk_buffer_holder; struct bitfield; @@ -107,7 +108,7 @@ namespace libtorrent // 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; } + virtual bool on_extension_handshake(lazy_entry const& h) { return true; } // returning true from any of the message handlers // indicates that the plugin has handeled the message. diff --git a/libtorrent/include/libtorrent/file.hpp b/libtorrent/include/libtorrent/file.hpp index 8a03c5294..a0d3ef1c0 100755 --- a/libtorrent/include/libtorrent/file.hpp +++ b/libtorrent/include/libtorrent/file.hpp @@ -34,7 +34,8 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_FILE_HPP_INCLUDED #include -#include +#include +#include #ifdef _MSC_VER #pragma warning(push, 1) @@ -54,11 +55,6 @@ namespace libtorrent { namespace fs = boost::filesystem; - struct TORRENT_EXPORT file_error: std::runtime_error - { - file_error(std::string const& msg): std::runtime_error(msg) {} - }; - class TORRENT_EXPORT file: public boost::noncopyable { public: diff --git a/libtorrent/include/libtorrent/lazy_entry.hpp b/libtorrent/include/libtorrent/lazy_entry.hpp index c40d87bcc..ffd4337e4 100644 --- a/libtorrent/include/libtorrent/lazy_entry.hpp +++ b/libtorrent/include/libtorrent/lazy_entry.hpp @@ -170,7 +170,7 @@ namespace libtorrent return m_size; } - // end points one byte passed end + // end points one byte passed last byte void set_end(char const* end) { TORRENT_ASSERT(end > m_begin); diff --git a/libtorrent/include/libtorrent/pch.hpp b/libtorrent/include/libtorrent/pch.hpp index 16fb27f3c..de937c596 100644 --- a/libtorrent/include/libtorrent/pch.hpp +++ b/libtorrent/include/libtorrent/pch.hpp @@ -1,3 +1,35 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + #ifdef BOOST_BUILD_PCH_ENABLED #include diff --git a/libtorrent/include/libtorrent/pe_crypto.hpp b/libtorrent/include/libtorrent/pe_crypto.hpp index 5db77f6c7..dd1260497 100644 --- a/libtorrent/include/libtorrent/pe_crypto.hpp +++ b/libtorrent/include/libtorrent/pe_crypto.hpp @@ -44,31 +44,30 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { - class DH_key_exchange + class dh_key_exchange { public: - DH_key_exchange (); - ~DH_key_exchange (); + dh_key_exchange(); + ~dh_key_exchange(); + bool good() const { return m_dh; } // Get local public key, always 96 bytes - char const* get_local_key (void) const; + char const* get_local_key() const; // read remote_pubkey, generate and store shared secret in - // m_dh_secret - void compute_secret (const char* remote_pubkey); + // m_dh_secret. + int compute_secret(const char* remote_pubkey); - const char* get_secret (void) const; + const char* get_secret() const; private: - int get_local_key_size () const + int get_local_key_size() const { - TORRENT_ASSERT(m_DH); - return BN_num_bytes (m_DH->pub_key); + TORRENT_ASSERT(m_dh); + return BN_num_bytes(m_dh->pub_key); } - DH* m_DH; - static const unsigned char m_dh_prime[96]; - static const unsigned char m_dh_generator[1]; + DH* m_dh; char m_dh_local_key[96]; char m_dh_secret[96]; @@ -78,24 +77,24 @@ namespace libtorrent { public: // Input longkeys must be 20 bytes - RC4_handler (const sha1_hash& rc4_local_longkey, + RC4_handler(const sha1_hash& rc4_local_longkey, const sha1_hash& rc4_remote_longkey) { - RC4_set_key (&m_local_key, 20, + RC4_set_key(&m_local_key, 20, reinterpret_cast(rc4_local_longkey.begin())); - RC4_set_key (&m_remote_key, 20, + RC4_set_key(&m_remote_key, 20, reinterpret_cast(rc4_remote_longkey.begin())); // Discard first 1024 bytes char buf[1024]; - encrypt (buf, 1024); - decrypt (buf, 1024); + encrypt(buf, 1024); + decrypt(buf, 1024); }; - ~RC4_handler () {}; + ~RC4_handler() {}; - void encrypt (char* pos, int len) + void encrypt(char* pos, int len) { TORRENT_ASSERT(len >= 0); TORRENT_ASSERT(pos); @@ -104,7 +103,7 @@ namespace libtorrent reinterpret_cast(pos)); } - void decrypt (char* pos, int len) + void decrypt(char* pos, int len) { TORRENT_ASSERT(len >= 0); TORRENT_ASSERT(pos); @@ -122,3 +121,4 @@ namespace libtorrent #endif // TORRENT_PE_CRYPTO_HPP_INCLUDED #endif // TORRENT_DISABLE_ENCRYPTION + diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 669187d61..f43857d5e 100755 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -202,8 +202,6 @@ namespace libtorrent void set_upload_only(bool u) { m_upload_only = u; } bool upload_only() const { return m_upload_only; } - bool has_timed_out() const; - // will send a keep-alive message to the peer void keep_alive(); @@ -569,6 +567,12 @@ namespace libtorrent ptime m_last_receive; ptime m_last_sent; + // the time when the first entry in the + // request queue was requested, increased + // for each entry that is popped from the + // download queue. Used for request timeout + ptime m_requested; + // a timestamp when the remote download rate // was last updated ptime m_remote_dl_update; @@ -825,6 +829,11 @@ namespace libtorrent // set to true when this peer is only uploading bool m_upload_only:1; + // set to true when a piece request times out. The + // result is that the desired pending queue size + // is set to 1 + bool m_snubbed:1; + #ifndef NDEBUG public: bool m_in_constructor:1; diff --git a/libtorrent/include/libtorrent/peer_info.hpp b/libtorrent/include/libtorrent/peer_info.hpp index c778bf8d4..d447b51f8 100755 --- a/libtorrent/include/libtorrent/peer_info.hpp +++ b/libtorrent/include/libtorrent/peer_info.hpp @@ -56,7 +56,8 @@ namespace libtorrent queued = 0x100, on_parole = 0x200, seed = 0x400, - optimistic_unchoke = 0x800 + optimistic_unchoke = 0x800, + snubbed = 0x1000 #ifndef TORRENT_DISABLE_ENCRYPTION , rc4_encrypted = 0x100000, plaintext_encrypted = 0x200000 @@ -105,6 +106,10 @@ namespace libtorrent // time since last download or upload time_duration last_active; + // the number of seconds until the current + // pending request times out + int request_timeout; + // the size of the send buffer for this peer, in bytes int send_buffer_size; // the number bytes that's actually used of the send buffer diff --git a/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index fcae0be32..d95782d79 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/include/libtorrent/session.hpp @@ -140,7 +140,7 @@ namespace libtorrent sha1_hash info_hash; char const* name; fs::path save_path; - entry const* resume_data; + std::vector* resume_data; storage_mode_t storage_mode; bool paused; bool auto_managed; @@ -212,6 +212,10 @@ namespace libtorrent session_proxy abort() { return session_proxy(m_impl); } + void pause(); + void resume(); + bool is_paused() const; + session_status status() const; cache_status get_cache_status() const; diff --git a/libtorrent/include/libtorrent/session_settings.hpp b/libtorrent/include/libtorrent/session_settings.hpp index 8306fc101..c7aee4a0f 100644 --- a/libtorrent/include/libtorrent/session_settings.hpp +++ b/libtorrent/include/libtorrent/session_settings.hpp @@ -88,6 +88,7 @@ namespace libtorrent , stop_tracker_timeout(5) , tracker_maximum_response_length(1024*1024) , piece_timeout(10) + , request_timeout(40) , request_queue_time(3.f) , max_allowed_in_request_queue(250) , max_out_request_queue(200) @@ -168,6 +169,11 @@ namespace libtorrent // it times out if no piece response is returned. int piece_timeout; + // the number of seconds one block (16kB) is expected + // to be received within. If it's not, the block is + // requested from a different peer + int request_timeout; + // the length of the request queue given in the number // of seconds it should take for the other end to send // all the pieces. i.e. the actual number of requests diff --git a/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index 8a7bb15a8..1733abe8a 100755 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/include/libtorrent/storage.hpp @@ -133,7 +133,7 @@ namespace libtorrent virtual bool move_storage(fs::path save_path) = 0; // verify storage dependent fast resume entries - virtual bool verify_resume_data(entry const& rd, std::string& error) = 0; + virtual bool verify_resume_data(lazy_entry const& rd, std::string& error) = 0; // write storage dependent fast resume entries virtual bool write_resume_data(entry& rd) const = 0; @@ -212,7 +212,7 @@ namespace libtorrent boost::intrusive_ptr info() const { return m_info; } void write_resume_data(entry& rd) const; - void async_check_fastresume(entry const* resume_data + void async_check_fastresume(lazy_entry const* resume_data , boost::function const& handler); void async_check_files(boost::function const& handler); @@ -259,7 +259,7 @@ namespace libtorrent fs::path save_path() const; - bool verify_resume_data(entry const& rd, std::string& error) + bool verify_resume_data(lazy_entry const& rd, std::string& error) { return m_storage->verify_resume_data(rd, error); } bool is_allocating() const @@ -282,7 +282,7 @@ namespace libtorrent // the error message indicates that the fast resume data was rejected // if 'fatal_disk_error' is returned, the error message indicates what // when wrong in the disk access - int check_fastresume(entry const& rd, std::string& error); + int check_fastresume(lazy_entry const& rd, std::string& error); // this function returns true if the checking is complete int check_files(int& current_slot, int& have_piece, std::string& error); diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index c0ddb798a..cf834e833 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -107,7 +107,7 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const* resume_data + , std::vector* resume_data , int seq , bool auto_managed); @@ -124,12 +124,14 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const* resume_data + , std::vector* resume_data , int seq , bool auto_managed); ~torrent(); + void parse_resume_data(std::vector* resume_data); + // starts the announce timer void start(); @@ -195,7 +197,12 @@ namespace libtorrent bool has_error() const { return !m_error.empty(); } void pause(); void resume(); - bool is_paused() const { return m_paused; } + + void do_pause(); + void do_resume(); + + bool is_paused() const; + bool is_torrent_paused() const { return m_paused; } void force_recheck(); void save_resume_data(); @@ -559,7 +566,7 @@ namespace libtorrent torrent_handle get_handle(); void write_resume_data(entry& rd) const; - void read_resume_data(entry const& rd); + void read_resume_data(lazy_entry const& rd); // LOGGING #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING @@ -782,7 +789,9 @@ namespace libtorrent // error message std::string m_error; - entry m_resume_data; + // used if there is any resume data + std::vector m_resume_data; + lazy_entry m_resume_entry; // if the torrent is started without metadata, it may // still be given a name until the metadata is received diff --git a/libtorrent/include/libtorrent/torrent_info.hpp b/libtorrent/include/libtorrent/torrent_info.hpp index 148d77c8f..0cc9b93b2 100755 --- a/libtorrent/include/libtorrent/torrent_info.hpp +++ b/libtorrent/include/libtorrent/torrent_info.hpp @@ -64,6 +64,7 @@ namespace libtorrent { namespace pt = boost::posix_time; namespace gr = boost::gregorian; + namespace fs = boost::filesystem; struct TORRENT_EXPORT announce_entry { @@ -79,6 +80,8 @@ namespace libtorrent }; #endif + int TORRENT_EXPORT load_file(fs::path const& filename, std::vector& v); + class TORRENT_EXPORT torrent_info : public intrusive_ptr_base { public: @@ -86,7 +89,7 @@ namespace libtorrent torrent_info(sha1_hash const& info_hash); torrent_info(lazy_entry const& torrent_file); torrent_info(char const* buffer, int size); - torrent_info(char const* filename); + torrent_info(fs::path const& filename); ~torrent_info(); file_storage const& files() const { return m_files; } diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index 307bcd4d2..16e63728a 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -192,6 +192,7 @@ namespace libtorrent if (out_enc_policy == pe_settings::forced) { write_pe1_2_dhkey(); + if (is_disconnecting()) return; m_state = read_pe_dhkey; reset_recv_buffer(dh_key_len); @@ -214,6 +215,7 @@ namespace libtorrent fast_reconnect(true); write_pe1_2_dhkey(); + if (is_disconnecting()) return; m_state = read_pe_dhkey; reset_recv_buffer(dh_key_len); setup_receive(); @@ -372,7 +374,7 @@ namespace libtorrent TORRENT_ASSERT(!m_encrypted); TORRENT_ASSERT(!m_rc4_encrypted); - TORRENT_ASSERT(!m_DH_key_exchange.get()); + TORRENT_ASSERT(!m_dh_key_exchange.get()); TORRENT_ASSERT(!m_sent_handshake); #ifdef TORRENT_VERBOSE_LOGGING @@ -380,7 +382,12 @@ namespace libtorrent (*m_logger) << " initiating encrypted handshake\n"; #endif - m_DH_key_exchange.reset(new DH_key_exchange); + m_dh_key_exchange.reset(new (std::nothrow) dh_key_exchange); + if (!m_dh_key_exchange || !m_dh_key_exchange->good()) + { + disconnect("out of memory"); + return; + } int pad_size = std::rand() % 512; @@ -389,11 +396,15 @@ namespace libtorrent #endif buffer::interval send_buf = allocate_send_buffer(dh_key_len + pad_size); - if (send_buf.begin == 0) return; // out of memory + if (send_buf.begin == 0) + { + disconnect("out of memory"); + return; + } - std::copy(m_DH_key_exchange->get_local_key(), - m_DH_key_exchange->get_local_key() + dh_key_len, - send_buf.begin); + std::copy(m_dh_key_exchange->get_local_key(), + m_dh_key_exchange->get_local_key() + dh_key_len, + send_buf.begin); std::generate(send_buf.begin + dh_key_len, send_buf.end, std::rand); setup_send(); @@ -417,7 +428,7 @@ namespace libtorrent hasher h; sha1_hash const& info_hash = t->torrent_file().info_hash(); - char const* const secret = m_DH_key_exchange->get_secret(); + char const* const secret = m_dh_key_exchange->get_secret(); int pad_size = rand() % 512; @@ -452,7 +463,7 @@ namespace libtorrent // Discard DH key exchange data, setup RC4 keys init_pe_RC4_handler(secret, info_hash); - m_DH_key_exchange.reset(); // secret should be invalid at this point + m_dh_key_exchange.reset(); // secret should be invalid at this point // write the verification constant and crypto field TORRENT_ASSERT(send_buf.left() == 8 + 4 + 2 + pad_size + 2); @@ -1235,9 +1246,9 @@ namespace libtorrent buffer::const_interval recv_buffer = receive_buffer(); - entry root; - root = bdecode(recv_buffer.begin + 2, recv_buffer.end); - if (root.type() == entry::undefined_t) + lazy_entry root; + lazy_bdecode(recv_buffer.begin + 2, recv_buffer.end, root); + if (root.type() != lazy_entry::dict_t) { #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << "invalid extended handshake\n"; @@ -1246,14 +1257,12 @@ namespace libtorrent } #ifdef TORRENT_VERBOSE_LOGGING - std::stringstream ext; - root.print(ext); - (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << ext.str(); + (*m_logger) << "<== EXTENDED HANDSHAKE: \n" << root; #endif #ifndef TORRENT_DISABLE_EXTENSIONS - for (extension_list_t::iterator i = m_extensions.begin() - , end(m_extensions.end()); i != end;) + for (extension_list_t::iterator i = m_extensions.begin(); + !m_extensions.empty() && i != m_extensions.end();) { // a false return value means that the extension // isn't supported by the other end. So, it is removed. @@ -1265,56 +1274,39 @@ namespace libtorrent #endif // there is supposed to be a remote listen port - if (entry* listen_port = root.find_key("p")) + int listen_port = root.dict_find_int_value("p"); + if (listen_port > 0 && peer_info_struct() != 0) { - if (listen_port->type() == entry::int_t - && peer_info_struct() != 0) - { - t->get_policy().update_peer_port(int(listen_port->integer()) - , peer_info_struct(), peer_info::incoming); - } + t->get_policy().update_peer_port(listen_port + , peer_info_struct(), peer_info::incoming); } // there should be a version too // but where do we put that info? - if (entry* client_info = root.find_key("v")) - { - if (client_info->type() == entry::string_t) - m_client_version = client_info->string(); - } + std::string client_info = root.dict_find_string_value("v"); + if (!client_info.empty()) m_client_version = client_info; - if (entry* reqq = root.find_key("reqq")) - { - if (reqq->type() == entry::int_t) - m_max_out_request_queue = int(reqq->integer()); - if (m_max_out_request_queue < 1) - m_max_out_request_queue = 1; - } + int reqq = root.dict_find_int_value("reqq"); + if (reqq > 0) m_max_out_request_queue = reqq; - if (entry* upload_only = root.find_key("upload_only")) - { - if (upload_only->type() == entry::int_t && upload_only->integer() != 0) - set_upload_only(true); - } + if (root.dict_find_int_value("upload_only")) + set_upload_only(true); - if (entry* myip = root.find_key("yourip")) + std::string myip = root.dict_find_string_value("yourip"); + if (!myip.empty()) { // TODO: don't trust this blindly - if (myip->type() == entry::string_t) + if (myip.size() == address_v4::bytes_type::static_size) { - std::string const& my_ip = myip->string().c_str(); - if (my_ip.size() == address_v4::bytes_type::static_size) - { - address_v4::bytes_type bytes; - std::copy(my_ip.begin(), my_ip.end(), bytes.begin()); - m_ses.set_external_address(address_v4(bytes)); - } - else if (my_ip.size() == address_v6::bytes_type::static_size) - { - address_v6::bytes_type bytes; - std::copy(my_ip.begin(), my_ip.end(), bytes.begin()); - m_ses.set_external_address(address_v6(bytes)); - } + address_v4::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + m_ses.set_external_address(address_v4(bytes)); + } + else if (myip.size() == address_v6::bytes_type::static_size) + { + address_v6::bytes_type bytes; + std::copy(myip.begin(), myip.end(), bytes.begin()); + m_ses.set_external_address(address_v6(bytes)); } } @@ -1752,13 +1744,17 @@ namespace libtorrent if (!packet_finished()) return; - // write our dh public key. m_DH_key_exchange is + // write our dh public key. m_dh_key_exchange is // initialized in write_pe1_2_dhkey() - if (!is_local()) - write_pe1_2_dhkey(); + if (!is_local()) write_pe1_2_dhkey(); + if (is_disconnecting()) return; // read dh key, generate shared secret - m_DH_key_exchange->compute_secret (recv_buffer.begin); // TODO handle errors + if (m_dh_key_exchange->compute_secret(recv_buffer.begin) == -1) + { + disconnect("out of memory"); + return; + } #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " received DH key\n"; @@ -1822,7 +1818,7 @@ namespace libtorrent // compute synchash (hash('req1',S)) h.update("req1", 4); - h.update(m_DH_key_exchange->get_secret(), dh_key_len); + h.update(m_dh_key_exchange->get_secret(), dh_key_len); m_sync_hash.reset(new sha1_hash(h.final())); } @@ -1895,7 +1891,7 @@ namespace libtorrent h.reset(); h.update("req3", 4); - h.update(m_DH_key_exchange->get_secret(), dh_key_len); + h.update(m_dh_key_exchange->get_secret(), dh_key_len); obfs_hash = h.final(); obfs_hash ^= skey_hash; @@ -1912,7 +1908,7 @@ namespace libtorrent TORRENT_ASSERT(t); } - init_pe_RC4_handler(m_DH_key_exchange->get_secret(), info_hash); + init_pe_RC4_handler(m_dh_key_exchange->get_secret(), info_hash); #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << " stream key found, torrent located.\n"; #endif @@ -2641,7 +2637,7 @@ namespace libtorrent void bt_peer_connection::check_invariant() const { #ifndef TORRENT_DISABLE_ENCRYPTION - TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_DH_key_exchange.get()) + TORRENT_ASSERT( (bool(m_state != read_pe_dhkey) || m_dh_key_exchange.get()) || !is_local()); TORRENT_ASSERT(!m_rc4_encrypted || m_RC4_handler.get()); diff --git a/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index 2845acee7..adebdcf13 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/src/disk_io_thread.cpp @@ -1048,7 +1048,7 @@ namespace libtorrent #ifdef TORRENT_DISK_STATS m_log << log_time() << " check fastresume" << std::endl; #endif - entry const* rd = (entry const*)j.buffer; + lazy_entry const* rd = (lazy_entry const*)j.buffer; TORRENT_ASSERT(rd != 0); ret = j.storage->check_fastresume(*rd, j.str); break; diff --git a/libtorrent/src/file.cpp b/libtorrent/src/file.cpp index 46d0e24e3..c6b79dce5 100755 --- a/libtorrent/src/file.cpp +++ b/libtorrent/src/file.cpp @@ -70,6 +70,7 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); #include "libtorrent/file.hpp" #include #include +#include #ifndef O_BINARY #define O_BINARY 0 @@ -130,7 +131,6 @@ namespace namespace libtorrent { - namespace fs = boost::filesystem; const file::open_mode file::in(mode_in); @@ -160,7 +160,6 @@ namespace libtorrent bool open(fs::path const& path, int mode) { - TORRENT_ASSERT(path.is_complete()); close(); #if defined(_WIN32) && defined(UNICODE) std::wstring wpath(safe_convert(path.native_file_string())); diff --git a/libtorrent/src/logger.cpp b/libtorrent/src/logger.cpp index b33816a59..1ecba5045 100644 --- a/libtorrent/src/logger.cpp +++ b/libtorrent/src/logger.cpp @@ -81,11 +81,10 @@ namespace virtual void add_handshake(entry&) {} // called when the extension handshake from the other end is received - virtual bool on_extension_handshake(entry const& h) + virtual bool on_extension_handshake(lazy_entry const& h) { log_timestamp(); - m_file << "<== EXTENSION_HANDSHAKE\n"; - h.print(m_file); + m_file << "<== EXTENSION_HANDSHAKE\n" << h; return true; } diff --git a/libtorrent/src/mapped_storage.cpp b/libtorrent/src/mapped_storage.cpp index 97d961ab9..90fadd5b1 100644 --- a/libtorrent/src/mapped_storage.cpp +++ b/libtorrent/src/mapped_storage.cpp @@ -560,34 +560,32 @@ namespace libtorrent return false; } - bool verify_resume_data(entry const& rd, std::string& error) + bool verify_resume_data(lazy_entry const& rd, std::string& error) { - if (rd.type() != entry::dictionary_t) + if (rd.type() != lazy_entry::dict_t) { - error = "invalid fastresume file"; + error = "invalid fastresume file (not a dictionary)"; return true; } std::vector > file_sizes; - entry const* file_sizes_ent = rd.find_key("file sizes"); - if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t) + lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes"); + if (file_sizes_ent == 0) { error = "missing or invalid 'file sizes' entry in resume data"; return false; } - entry::list_type const& l = file_sizes_ent->list(); - - for (entry::list_type::const_iterator i = l.begin(); - i != l.end(); ++i) + for (int i = 0; i < file_sizes_ent->list_size(); ++i) { - if (i->type() != entry::list_t) break; - entry::list_type const& pair = i->list(); - if (pair.size() != 2 || pair.front().type() != entry::int_t - || pair.back().type() != entry::int_t) - break; + lazy_entry const* e = file_sizes_ent->list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::int_t) + continue; file_sizes.push_back(std::pair( - pair.front().integer(), pair.back().integer())); + e->list_int_value_at(0), std::time_t(e->list_int_value_at(1)))); } if (file_sizes.empty()) @@ -596,24 +594,30 @@ namespace libtorrent return false; } - entry const* slots_ent = rd.find_key("slots"); - if (slots_ent == 0 || slots_ent->type() != entry::list_t) + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots == 0) { error = "missing or invalid 'slots' entry in resume data"; return false; } - entry::list_type const& slots = slots_ent->list(); - bool seed = int(slots.size()) == files().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 seed = false; + + if (int(slots->list_size()) == m_files.num_pieces()) + { + bool seed = true; + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->list_int_value_at(i, -1) >= 0) continue; + seed = false; + break; + } + } bool full_allocation_mode = false; - entry const* allocation_mode = rd.find_key("allocation"); - if (allocation_mode && allocation_mode->type() == entry::string_t) - full_allocation_mode = allocation_mode->string() == "full"; + if (rd.dict_find_string_value("allocation") == "full") + full_allocation_mode = true; if (seed) { @@ -640,7 +644,6 @@ namespace libtorrent return false; } } - return true; } return match_filesizes(files(), m_save_path, file_sizes diff --git a/libtorrent/src/metadata_transfer.cpp b/libtorrent/src/metadata_transfer.cpp index 8938cfb7b..8e27eeabf 100644 --- a/libtorrent/src/metadata_transfer.cpp +++ b/libtorrent/src/metadata_transfer.cpp @@ -278,15 +278,16 @@ namespace libtorrent { namespace } // called when the extension handshake from the other end is received - virtual bool on_extension_handshake(entry const& h) + virtual bool on_extension_handshake(lazy_entry const& h) { m_message_index = 0; - entry const* messages = h.find_key("m"); - if (!messages || messages->type() != entry::dictionary_t) return false; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; - entry const* index = messages->find_key("LT_metadata"); - if (!index || index->type() != entry::int_t) return false; - m_message_index = int(index->integer()); + int index = messages->dict_find_int_value("LT_metadata", -1); + if (index == -1) return false; + m_message_index = index; return true; } diff --git a/libtorrent/src/pe_crypto.cpp b/libtorrent/src/pe_crypto.cpp index d9298e989..f20535610 100644 --- a/libtorrent/src/pe_crypto.cpp +++ b/libtorrent/src/pe_crypto.cpp @@ -42,29 +42,46 @@ POSSIBILITY OF SUCH DAMAGE. namespace libtorrent { + namespace + { + const unsigned char dh_prime[96] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 + }; + + const unsigned char dh_generator[1] = { 2 }; + } // Set the prime P and the generator, generate local public key - DH_key_exchange::DH_key_exchange() + dh_key_exchange::dh_key_exchange() { - m_DH = DH_new(); - if (m_DH == 0) throw std::bad_alloc(); + m_dh = DH_new(); + if (m_dh == 0) return; - m_DH->p = BN_bin2bn(m_dh_prime, sizeof(m_dh_prime), NULL); - m_DH->g = BN_bin2bn(m_dh_generator, sizeof(m_dh_generator), NULL); - if (m_DH->p == 0 || m_DH->g == 0) + m_dh->p = BN_bin2bn(dh_prime, sizeof(dh_prime), 0); + m_dh->g = BN_bin2bn(dh_generator, sizeof(dh_generator), 0); + if (m_dh->p == 0 || m_dh->g == 0) { - DH_free(m_DH); - throw std::bad_alloc(); + DH_free(m_dh); + m_dh = 0; + return; } - m_DH->length = 160l; + m_dh->length = 160l; - TORRENT_ASSERT(sizeof(m_dh_prime) == DH_size(m_DH)); + TORRENT_ASSERT(sizeof(dh_prime) == DH_size(m_dh)); - if (DH_generate_key(m_DH) == 0 || m_DH->pub_key == 0) + if (DH_generate_key(m_dh) == 0 || m_dh->pub_key == 0) { - DH_free(m_DH); - throw std::bad_alloc(); + DH_free(m_dh); + m_dh = 0; + return; } // DH can generate key sizes that are smaller than the size of @@ -72,42 +89,53 @@ namespace libtorrent // the msb's of m_dh_local_key need to be zeroed // appropriately. int key_size = get_local_key_size(); - int len_dh = sizeof(m_dh_prime); // must equal DH_size(m_DH) + int len_dh = sizeof(dh_prime); // must equal DH_size(m_DH) if (key_size != len_dh) { TORRENT_ASSERT(key_size > 0 && key_size < len_dh); int pad_zero_size = len_dh - key_size; std::fill(m_dh_local_key, m_dh_local_key + pad_zero_size, 0); - BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key + pad_zero_size); + if (BN_bn2bin(m_dh->pub_key, (unsigned char*)m_dh_local_key + pad_zero_size) == 0) + { + DH_free(m_dh); + m_dh = 0; + return; + } } else - BN_bn2bin(m_DH->pub_key, (unsigned char*)m_dh_local_key); // TODO Check return value + { + if (BN_bn2bin(m_dh->pub_key, (unsigned char*)m_dh_local_key) == 0) + { + DH_free(m_dh); + m_dh = 0; + return; + } + } } - DH_key_exchange::~DH_key_exchange() + dh_key_exchange::~dh_key_exchange() { - TORRENT_ASSERT(m_DH); - DH_free(m_DH); + if (m_dh) DH_free(m_dh); } - char const* DH_key_exchange::get_local_key() const + char const* dh_key_exchange::get_local_key() const { return m_dh_local_key; } // compute shared secret given remote public key - void DH_key_exchange::compute_secret(char const* remote_pubkey) + int dh_key_exchange::compute_secret(char const* remote_pubkey) { TORRENT_ASSERT(remote_pubkey); BIGNUM* bn_remote_pubkey = BN_bin2bn ((unsigned char*)remote_pubkey, 96, NULL); - if (bn_remote_pubkey == 0) throw std::bad_alloc(); + if (bn_remote_pubkey == 0) return -1; char dh_secret[96]; int secret_size = DH_compute_key((unsigned char*)dh_secret - , bn_remote_pubkey, m_DH); - if (secret_size < 0 || secret_size > 96) throw std::bad_alloc(); + , bn_remote_pubkey, m_dh); + if (secret_size < 0 || secret_size > 96) return -1; if (secret_size != 96) { @@ -116,26 +144,14 @@ namespace libtorrent } std::copy(dh_secret, dh_secret + secret_size, m_dh_secret + 96 - secret_size); BN_free(bn_remote_pubkey); + return 0; } - char const* DH_key_exchange::get_secret() const + char const* dh_key_exchange::get_secret() const { return m_dh_secret; } - const unsigned char DH_key_exchange::m_dh_prime[96] = { - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, - 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, - 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, - 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, - 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, - 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, - 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, - 0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63 - }; - - const unsigned char DH_key_exchange::m_dh_generator[1] = { 2 }; - } // namespace libtorrent #endif // #ifndef TORRENT_DISABLE_ENCRYPTION diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index 0b842a8f3..da82bbf15 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -81,6 +81,7 @@ namespace libtorrent , m_last_unchoke(min_time()) , m_last_receive(time_now()) , m_last_sent(time_now()) + , m_requested(min_time()) , m_remote_dl_update(time_now()) , m_became_uninterested(time_now()) , m_became_uninteresting(time_now()) @@ -126,6 +127,7 @@ namespace libtorrent , m_queued(true) , m_request_large_blocks(false) , m_upload_only(false) + , m_snubbed(false) #ifndef NDEBUG , m_in_constructor(true) #endif @@ -166,7 +168,7 @@ namespace libtorrent // incoming connection peer_connection::peer_connection( session_impl& ses - , boost::shared_ptr s + , shared_ptr s , tcp::endpoint const& endp , policy::peer* peerinfo) : @@ -182,6 +184,7 @@ namespace libtorrent , m_last_unchoke(min_time()) , m_last_receive(time_now()) , m_last_sent(time_now()) + , m_requested(min_time()) , m_remote_dl_update(time_now()) , m_became_uninterested(time_now()) , m_became_uninteresting(time_now()) @@ -226,6 +229,7 @@ namespace libtorrent , m_queued(false) , m_request_large_blocks(false) , m_upload_only(false) + , m_snubbed(false) #ifndef NDEBUG , m_in_constructor(true) #endif @@ -1433,7 +1437,7 @@ namespace libtorrent { disconnect("out of memory"); return; - } + } disk_buffer_holder holder(m_ses, buffer); std::memcpy(buffer, data, p.length); incoming_piece(p, holder); @@ -1562,12 +1566,19 @@ namespace libtorrent TORRENT_ASSERT(*b == block_finished); } + if (total_seconds(time_now() - m_requested) < m_ses.settings().request_timeout) + m_snubbed = false; + // if the block we got is already finished, then ignore it if (picker.is_downloaded(block_finished)) { t->received_redundant_data(p.length); m_download_queue.erase(b); + + if (!m_download_queue.empty()) + m_requested = time_now(); + request_a_block(*t, *this); send_block_requests(); return; @@ -1579,6 +1590,9 @@ namespace libtorrent TORRENT_ASSERT(m_channel_state[download_channel] == peer_info::bw_idle); m_download_queue.erase(b); + if (!m_download_queue.empty()) + m_requested = time_now(); + // did we request this block from any other peers? bool multi = picker.num_peers(block_finished) > 1; picker.mark_as_writing(block_finished, peer_info_struct()); @@ -2073,6 +2087,8 @@ namespace libtorrent if ((int)m_download_queue.size() >= m_desired_queue_size) return; + bool empty_download_queue = m_download_queue.empty(); + while (!m_request_queue.empty() && (int)m_download_queue.size() < m_desired_queue_size) { @@ -2166,6 +2182,13 @@ namespace libtorrent #endif } m_last_piece = time_now(); + + if (!m_download_queue.empty() + && empty_download_queue) + { + // This means we just added a request to this connection + m_requested = time_now(); + } } void peer_connection::timed_out() @@ -2321,6 +2344,8 @@ namespace libtorrent { TORRENT_ASSERT(!associated_torrent().expired()); + ptime now = time_now(); + p.download_rate_peak = m_download_rate_peak; p.upload_rate_peak = m_upload_rate_peak; p.rtt = m_rtt; @@ -2333,6 +2358,8 @@ namespace libtorrent p.pending_disk_bytes = m_outstanding_writing_bytes; p.send_quota = m_bandwidth_limit[upload_channel].quota_left(); p.receive_quota = m_bandwidth_limit[download_channel].quota_left(); + if (m_download_queue.empty()) p.request_timeout = -1; + else p.request_timeout = total_seconds(m_requested - now) + m_ses.settings().request_timeout; #ifndef TORRENT_DISABLE_GEO_IP p.inet_as_name = m_inet_as_name; #endif @@ -2377,7 +2404,6 @@ namespace libtorrent } p.pieces = get_bitfield(); - ptime now = time_now(); p.last_request = now - m_last_request; p.last_active = now - (std::max)(m_last_sent, m_last_receive); @@ -2386,6 +2412,7 @@ namespace libtorrent get_specific_peer_info(p); p.flags |= is_seed() ? peer_info::seed : 0; + p.flags |= m_snubbed ? peer_info::snubbed : 0; if (peer_info_struct()) { policy::peer* pi = peer_info_struct(); @@ -2483,11 +2510,13 @@ namespace libtorrent void peer_connection::second_tick(float tick_interval) { - INVARIANT_CHECK; - ptime now(time_now()); boost::intrusive_ptr me(self()); + // the invariant check must be run before me is destructed + // in case the peer got disconnected + INVARIANT_CHECK; + boost::shared_ptr t = m_torrent.lock(); if (!t || m_disconnecting) { @@ -2505,8 +2534,103 @@ namespace libtorrent { (*i)->tick(); } + if (is_disconnecting()) return; #endif + // if the peer hasn't said a thing for a certain + // time, it is considered to have timed out + time_duration d; + d = now - m_last_receive; + if (d > seconds(m_timeout) && !m_connecting) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " *** LAST ACTIVITY [ " + << total_seconds(d) << " seconds ago ] ***\n"; +#endif + disconnect("timed out: inactivity"); + return; + } + + // do not stall waiting for a handshake + if (!m_connecting + && in_handshake() + && d > seconds(m_ses.settings().handshake_timeout)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " *** NO HANDSHAKE [ waited " + << total_seconds(d) << " seconds ] ***\n"; +#endif + disconnect("timed out: no handshake"); + return; + } + + // disconnect peers that we unchoked, but + // they didn't send a request within 20 seconds. + // but only if we're a seed + d = now - (std::max)(m_last_unchoke, m_last_incoming_request); + if (!m_connecting + && m_requests.empty() + && !m_choked + && m_peer_interested + && t && t->is_finished() + && d > seconds(20)) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " *** NO REQUEST [ t: " + << total_seconds(d) << " ] ***\n"; +#endif + disconnect("timed out: no request when unchoked"); + return; + } + + // if the peer hasn't become interested and we haven't + // become interested in the peer for 10 minutes, it + // has also timed out. + time_duration d1; + time_duration d2; + d1 = now - m_became_uninterested; + d2 = now - m_became_uninteresting; + time_duration time_limit = seconds( + m_ses.settings().inactivity_timeout); + + // don't bother disconnect peers we haven't been interested + // in (and that hasn't been interested in us) for a while + // unless we have used up all our connection slots + if (!m_interesting + && !m_peer_interested + && d1 > time_limit + && d2 > time_limit + && (m_ses.num_connections() >= m_ses.max_connections() + || (t && t->num_peers() >= t->max_connections()))) + { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_logger) << time_now_string() << " *** MUTUAL NO INTEREST [ " + "t1: " << total_seconds(d1) << " | " + "t2: " << total_seconds(d2) << " ] ***\n"; +#endif + disconnect("timed out: no interest"); + return; + } + + if (!m_download_queue.empty() + && now > m_requested + seconds(m_ses.settings().request_timeout) + && t->has_picker()) + { + m_snubbed = true; + m_desired_queue_size = 1; + piece_picker& picker = t->picker(); + // the front request timed out! + picker.abort_download(m_download_queue[0]); + m_download_queue.pop_front(); + if (!m_download_queue.empty()) + m_requested = time_now(); + request_a_block(*t, *this); + send_block_requests(); + } + + // if we haven't sent something in too long, send a keep-alive + keep_alive(); + m_ignore_bandwidth_limits = m_ses.settings().ignore_limits_on_local_network && on_local_network(); @@ -2544,12 +2668,19 @@ namespace libtorrent ? t->torrent_file().piece_length() : t->block_size(); TORRENT_ASSERT(block_size > 0); - m_desired_queue_size = static_cast(queue_time - * statistics().download_rate() / block_size); - if (m_desired_queue_size > m_max_out_request_queue) - m_desired_queue_size = m_max_out_request_queue; - if (m_desired_queue_size < min_request_queue) - m_desired_queue_size = min_request_queue; + if (m_snubbed) + { + m_desired_queue_size = 1; + } + else + { + m_desired_queue_size = static_cast(queue_time + * statistics().download_rate() / block_size); + if (m_desired_queue_size > m_max_out_request_queue) + m_desired_queue_size = m_max_out_request_queue; + if (m_desired_queue_size < min_request_queue) + m_desired_queue_size = min_request_queue; + } if (!m_download_queue.empty() && now - m_last_piece > seconds(m_ses.settings().piece_timeout)) @@ -2564,6 +2695,9 @@ namespace libtorrent << " " << total_seconds(now - m_last_piece) << "] ***\n"; #endif + m_snubbed = true; + m_desired_queue_size = 1; + if (t->is_seed()) { m_download_queue.clear(); @@ -3521,94 +3655,6 @@ namespace libtorrent } #endif - bool peer_connection::has_timed_out() const - { - // TODO: the timeout should be called by an event - INVARIANT_CHECK; - - ptime now(time_now()); - - // if the socket is still connecting, don't - // consider it timed out. Because Windows XP SP2 - // may delay connection attempts. - if (m_connecting) return false; - - // if the peer hasn't said a thing for a certain - // time, it is considered to have timed out - time_duration d; - d = now - m_last_receive; - if (d > seconds(m_timeout)) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " *** LAST ACTIVITY [ " - << total_seconds(d) << " seconds ago ] ***\n"; -#endif - return true; - } - - // do not stall waiting for a handshake - if (in_handshake() && d > seconds(m_ses.settings().handshake_timeout)) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " *** NO HANDSHAKE [ waited " - << total_seconds(d) << " seconds ] ***\n"; -#endif - return true; - } - - // disconnect peers that we unchoked, but - // they didn't send a request within 20 seconds. - // but only if we're a seed - boost::shared_ptr t = m_torrent.lock(); - d = now - (std::max)(m_last_unchoke, m_last_incoming_request); - if (m_requests.empty() - && !m_choked - && m_peer_interested - && t && t->is_finished() - && d > seconds(20)) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " *** NO REQUEST [ t: " - << total_seconds(d) << " ] ***\n"; -#endif - return true; - } - - // TODO: as long as we have less than 95% of the - // global (or local) connection limit, connections should - // never time out for another reason - - // if the peer hasn't become interested and we haven't - // become interested in the peer for 10 minutes, it - // has also timed out. - time_duration d1; - time_duration d2; - d1 = now - m_became_uninterested; - d2 = now - m_became_uninteresting; - time_duration time_limit = seconds( - m_ses.settings().inactivity_timeout); - - // don't bother disconnect peers we haven't been interested - // in (and that hasn't been interested in us) for a while - // unless we have used up all our connection slots - if (!m_interesting - && !m_peer_interested - && d1 > time_limit - && d2 > time_limit - && (m_ses.num_connections() >= m_ses.max_connections() - || (t && t->num_peers() >= t->max_connections()))) - { -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_logger) << time_now_string() << " *** MUTUAL NO INTEREST [ " - "t1: " << total_seconds(d1) << " | " - "t2: " << total_seconds(d2) << " ] ***\n"; -#endif - return true; - } - - return false; - } - peer_connection::peer_speed_t peer_connection::peer_speed() { shared_ptr t = m_torrent.lock(); diff --git a/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index bfceb7024..194af5b73 100755 --- a/libtorrent/src/session.cpp +++ b/libtorrent/src/session.cpp @@ -257,7 +257,12 @@ namespace libtorrent add_torrent_params p(sc); p.ti = tip; p.save_path = save_path; - p.resume_data = &resume_data; + std::vector buf; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(buf), resume_data); + p.resume_data = &buf; + } p.storage_mode = storage_mode; p.paused = paused; return m_impl->add_torrent(p); @@ -275,7 +280,12 @@ namespace libtorrent add_torrent_params p(sc); p.ti = ti; p.save_path = save_path; - p.resume_data = &resume_data; + std::vector buf; + if (resume_data.type() != entry::undefined_t) + { + bencode(std::back_inserter(buf), resume_data); + p.resume_data = &buf; + } p.storage_mode = storage_mode; p.paused = paused; p.userdata = userdata; @@ -324,6 +334,10 @@ namespace libtorrent return m_impl->status(); } + void session::pause() { m_impl->pause(); } + void session::resume() { m_impl->resume(); } + bool session::is_paused() const { return m_impl->is_paused(); } + void session::get_cache_info(sha1_hash const& ih , std::vector& ret) const { diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index 59d1e65a6..0e33421e7 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -158,6 +158,7 @@ namespace aux { , m_listen_port_retries(listen_port_range.second - listen_port_range.first) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first) , m_abort(false) + , m_paused(false) , m_max_uploads(8) , m_allowed_upload_slots(8) , m_max_connections(200) @@ -389,6 +390,32 @@ namespace aux { } #endif + void session_impl::pause() + { + mutex_t::scoped_lock l(m_mutex); + if (m_paused) return; + m_paused = true; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + if (!t.is_torrent_paused()) t.do_pause(); + } + } + + void session_impl::resume() + { + mutex_t::scoped_lock l(m_mutex); + if (!m_paused) return; + m_paused = false; + for (torrent_map::iterator i = m_torrents.begin() + , end(m_torrents.end()); i != end; ++i) + { + torrent& t = *i->second; + t.do_resume(); + } + } + void session_impl::abort() { mutex_t::scoped_lock l(m_mutex); @@ -1207,32 +1234,6 @@ namespace aux { } } - // do the second_tick() on each connection - // this will update their statistics (download and upload speeds) - // also purge sockets that have timed out - // and keep sockets open by keeping them alive. - for (connection_map::iterator i = m_connections.begin(); - i != m_connections.end();) - { - // we need to do like this because j->second->disconnect() will - // erase the connection from the map we're iterating - connection_map::iterator j = i; - ++i; - // if this socket has timed out - // close it. - peer_connection& c = *j->get(); - if (c.has_timed_out()) - { -#if defined(TORRENT_VERBOSE_LOGGING) - (*c.m_logger) << "*** CONNECTION TIMED OUT\n"; -#endif - c.disconnect("timed out: inactive", 1); - continue; - } - - c.keep_alive(); - } - // -------------------------------------------------------------- // auto managed torrent // -------------------------------------------------------------- @@ -1389,7 +1390,6 @@ namespace aux { && t->state() != torrent_status::checking_files) { --num_downloaders; - --num_seeds; if (t->is_paused()) t->resume(); } } @@ -1412,7 +1412,6 @@ namespace aux { if (num_seeds > 0 && hard_limit > 0) { --hard_limit; - --num_downloaders; --num_seeds; if (t->is_paused()) t->resume(); } diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index af71fcfaf..d6ecd4e71 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -422,7 +422,7 @@ namespace libtorrent bool move_slot(int src_slot, int dst_slot); bool swap_slots(int slot1, int slot2); bool swap_slots3(int slot1, int slot2, int slot3); - bool verify_resume_data(entry const& rd, std::string& error); + bool verify_resume_data(lazy_entry const& rd, std::string& error); bool write_resume_data(entry& rd) const; sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size); @@ -685,34 +685,32 @@ namespace libtorrent return false; } - bool storage::verify_resume_data(entry const& rd, std::string& error) + bool storage::verify_resume_data(lazy_entry const& rd, std::string& error) { - if (rd.type() != entry::dictionary_t) + if (rd.type() != lazy_entry::dict_t) { error = "invalid fastresume file (not a dictionary)"; return true; } std::vector > file_sizes; - entry const* file_sizes_ent = rd.find_key("file sizes"); - if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t) + lazy_entry const* file_sizes_ent = rd.dict_find_list("file sizes"); + if (file_sizes_ent == 0) { error = "missing or invalid 'file sizes' entry in resume data"; return false; } - entry::list_type const& l = file_sizes_ent->list(); - - for (entry::list_type::const_iterator i = l.begin(); - i != l.end(); ++i) + for (int i = 0; i < file_sizes_ent->list_size(); ++i) { - if (i->type() != entry::list_t) break; - entry::list_type const& pair = i->list(); - if (pair.size() != 2 || pair.front().type() != entry::int_t - || pair.back().type() != entry::int_t) - break; + lazy_entry const* e = file_sizes_ent->list_at(i); + if (e->type() != lazy_entry::list_t + || e->list_size() != 2 + || e->list_at(0)->type() != lazy_entry::int_t + || e->list_at(1)->type() != lazy_entry::int_t) + continue; file_sizes.push_back(std::pair( - pair.front().integer(), std::time_t(pair.back().integer()))); + e->list_int_value_at(0), std::time_t(e->list_int_value_at(1)))); } if (file_sizes.empty()) @@ -721,32 +719,38 @@ namespace libtorrent return false; } - entry const* slots_ent = rd.find_key("slots"); - if (slots_ent == 0 || slots_ent->type() != entry::list_t) + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots == 0) { error = "missing or invalid 'slots' entry in resume data"; return false; } - entry::list_type const& slots = slots_ent->list(); - bool seed = int(slots.size()) == m_files.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 seed = false; + + if (int(slots->list_size()) == m_files.num_pieces()) + { + bool seed = true; + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->list_int_value_at(i, -1) >= 0) continue; + seed = false; + break; + } + } bool full_allocation_mode = false; - entry const* allocation_mode = rd.find_key("allocation"); - if (allocation_mode && allocation_mode->type() == entry::string_t) - full_allocation_mode = allocation_mode->string() == "full"; + if (rd.dict_find_string_value("allocation") == "full") + full_allocation_mode = true; if (seed) { - if (m_files.num_files() != (int)file_sizes.size()) + if (files().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_files.num_files()) + ")"; + + boost::lexical_cast(files().num_files()) + ")"; return false; } @@ -1300,7 +1304,7 @@ namespace libtorrent m_io_thread.add_job(j, handler); } - void piece_manager::async_check_fastresume(entry const* resume_data + void piece_manager::async_check_fastresume(lazy_entry const* resume_data , boost::function const& handler) { TORRENT_ASSERT(resume_data != 0); @@ -1783,7 +1787,7 @@ namespace libtorrent // isn't return false and the full check // will be run int piece_manager::check_fastresume( - entry const& rd, std::string& error) + lazy_entry const& rd, std::string& error) { boost::recursive_mutex::scoped_lock lock(m_mutex); @@ -1792,29 +1796,25 @@ namespace libtorrent TORRENT_ASSERT(m_files.piece_length() > 0); // if we don't have any resume data, return - if (rd.type() == entry::undefined_t) return check_no_fastresume(error); + if (rd.type() == lazy_entry::none_t) return check_no_fastresume(error); - if (rd.type() != entry::dictionary_t) + if (rd.type() != lazy_entry::dict_t) { - error = "invalid fastresume data (not a bencoded dictionary)"; + error = "invalid fastresume data (not a dictionary)"; return check_no_fastresume(error); } int block_size = (std::min)(16 * 1024, m_files.piece_length()); - entry const* blocks_per_piece_ent = rd.find_key("blocks per piece"); - if (blocks_per_piece_ent != 0 - && blocks_per_piece_ent->type() == entry::int_t - && blocks_per_piece_ent->integer() != m_files.piece_length() / block_size) + int blocks_per_piece = rd.dict_find_int_value("blocks per piece", -1); + if (blocks_per_piece != -1 + && blocks_per_piece != m_files.piece_length() / block_size) { error = "invalid 'blocks per piece' entry"; return check_no_fastresume(error); } storage_mode_t storage_mode = storage_mode_compact; - entry const* allocation = rd.find_key("allocation"); - if (allocation != 0 - && allocation->type() == entry::string_t - && allocation->string() != "compact") + if (rd.dict_find_string_value("allocation") != "compact") storage_mode = storage_mode_sparse; // assume no piece is out of place (i.e. in a slot @@ -1823,20 +1823,20 @@ namespace libtorrent // if we don't have a piece map, we need the slots // if we're in compact mode, we also need the slots map - if (storage_mode == storage_mode_compact || rd.find_key("pieces") == 0) + if (storage_mode == storage_mode_compact || rd.dict_find("pieces") == 0) { // read slots map - entry const* slots = rd.find_key("slots"); - if (slots == 0 || slots->type() != entry::list_t) + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots == 0) { error = "missing slot list"; return check_no_fastresume(error); } - if ((int)slots->list().size() > m_files.num_pieces()) + if ((int)slots->list_size() > m_files.num_pieces()) { error = "file has more slots than torrent (slots: " - + boost::lexical_cast(slots->list().size()) + " size: " + + boost::lexical_cast(slots->list_size()) + " size: " + boost::lexical_cast(m_files.num_pieces()) + " )"; return check_no_fastresume(error); } @@ -1846,17 +1846,16 @@ namespace libtorrent int num_pieces = int(m_files.num_pieces()); m_slot_to_piece.resize(num_pieces, unallocated); m_piece_to_slot.resize(num_pieces, has_no_slot); - int slot = 0; - for (entry::list_type::const_iterator i = slots->list().begin(); - i != slots->list().end(); ++i, ++slot) + for (int i = 0; i < slots->list_size(); ++i) { - if (i->type() != entry::int_t) + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) { error = "invalid entry type in slot list"; return check_no_fastresume(error); } - int index = int(i->integer()); + int index = int(e->int_value()); if (index >= num_pieces || index < -2) { error = "too high index number in slot map (index: " @@ -1866,37 +1865,36 @@ namespace libtorrent } if (index >= 0) { - m_slot_to_piece[slot] = index; - m_piece_to_slot[index] = slot; - if (slot != index) out_of_place = true; + m_slot_to_piece[i] = index; + m_piece_to_slot[index] = i; + if (i != index) out_of_place = true; } else if (index == unassigned) { if (m_storage_mode == storage_mode_compact) - m_free_slots.push_back(slot); + m_free_slots.push_back(i); } else { TORRENT_ASSERT(index == unallocated); if (m_storage_mode == storage_mode_compact) - m_unallocated_slots.push_back(slot); + m_unallocated_slots.push_back(i); } } } else { - int slot = 0; - for (entry::list_type::const_iterator i = slots->list().begin(); - i != slots->list().end(); ++i, ++slot) + for (int i = 0; i < slots->list_size(); ++i) { - if (i->type() != entry::int_t) + lazy_entry const* e = slots->list_at(i); + if (e->type() != lazy_entry::int_t) { error = "invalid entry type in slot list"; return check_no_fastresume(error); } - int index = int(i->integer()); - if (index != slot && index >= 0) + int index = int(e->int_value()); + if (index != i && index >= 0) { error = "invalid slot index"; return check_no_fastresume(error); @@ -1937,17 +1935,17 @@ namespace libtorrent else if (m_storage_mode == storage_mode_compact) { // read piece map - entry const* pieces = rd.find_key("pieces"); - if (pieces == 0 || pieces->type() != entry::string_t) + lazy_entry const* pieces = rd.dict_find("pieces"); + if (pieces == 0 || pieces->type() != lazy_entry::string_t) { error = "missing pieces entry"; return check_no_fastresume(error); } - if ((int)pieces->string().size() != m_files.num_pieces()) + if ((int)pieces->string_length() != m_files.num_pieces()) { error = "file has more slots than torrent (slots: " - + boost::lexical_cast(pieces->string().size()) + " size: " + + boost::lexical_cast(pieces->string_length()) + " size: " + boost::lexical_cast(m_files.num_pieces()) + " )"; return check_no_fastresume(error); } @@ -1955,7 +1953,7 @@ namespace libtorrent int num_pieces = int(m_files.num_pieces()); m_slot_to_piece.resize(num_pieces, unallocated); m_piece_to_slot.resize(num_pieces, has_no_slot); - std::string const& have_pieces = pieces->string(); + char const* have_pieces = pieces->string_ptr(); for (int i = 0; i < num_pieces; ++i) { if (have_pieces[i] & 1) diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index cb9a74175..048b9de41 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -144,7 +144,7 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const* resume_data + , std::vector* resume_data , int seq , bool auto_managed) : m_policy(this) @@ -203,7 +203,7 @@ namespace libtorrent , m_has_incoming(false) , m_files_checked(false) { - if (resume_data) m_resume_data = *resume_data; + parse_resume_data(resume_data); } torrent::torrent( @@ -217,7 +217,7 @@ namespace libtorrent , int block_size , storage_constructor_type sc , bool paused - , entry const* resume_data + , std::vector* resume_data , int seq , bool auto_managed) : m_policy(this) @@ -274,7 +274,7 @@ namespace libtorrent , m_connections_initialized(false) , m_has_incoming(false) { - if (resume_data) m_resume_data = *resume_data; + parse_resume_data(resume_data); #ifndef NDEBUG m_files_checked = false; #endif @@ -289,6 +289,25 @@ namespace libtorrent } } + void torrent::parse_resume_data(std::vector* resume_data) + { + if (!resume_data) return; + m_resume_data.swap(*resume_data); + if (lazy_bdecode(&m_resume_data[0], &m_resume_data[0] + + m_resume_data.size(), m_resume_entry) != 0) + { + std::vector().swap(m_resume_data); + if (m_ses.m_alerts.should_post(alert::warning)) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert(get_handle(), "parse failed")); +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING + (*m_ses.m_logger) << "fastresume data for " + << torrent_file().name() << " rejected: parse failed\n"; +#endif + } + } + } + void torrent::start() { boost::weak_ptr self(shared_from_this()); @@ -429,18 +448,17 @@ namespace libtorrent m_state = torrent_status::queued_for_checking; - if (m_resume_data.type() == entry::dictionary_t) + if (m_resume_entry.type() == lazy_entry::dict_t) { char const* error = 0; - entry const* file_format = m_resume_data.find_key("file-format"); - if (file_format->string() != "libtorrent resume file") + if (m_resume_entry.dict_find_string_value("file-format") != "libtorrent resume file") error = "invalid file format tag"; - entry const* info_hash = m_resume_data.find_key("info-hash"); - if (!error && (info_hash == 0 || info_hash->type() != entry::string_t)) + std::string info_hash = m_resume_entry.dict_find_string_value("info-hash"); + if (!error && info_hash.empty()) error = "missing info-hash"; - if (!error && sha1_hash(info_hash->string()) != m_torrent_file->info_hash()) + if (!error && sha1_hash(info_hash) != m_torrent_file->info_hash()) error = "mismatching info-hash"; if (error && m_ses.m_alerts.should_post(alert::warning)) @@ -453,11 +471,18 @@ namespace libtorrent #endif } - if (error) m_resume_data = entry(); - else read_resume_data(m_resume_data); + if (error) + { + std::vector().swap(m_resume_data); + m_resume_entry = lazy_entry(); + } + else + { + read_resume_data(m_resume_entry); + } } - m_storage->async_check_fastresume(&m_resume_data + m_storage->async_check_fastresume(&m_resume_entry , bind(&torrent::on_resume_data_checked , shared_from_this(), _1, _2)); } @@ -483,54 +508,41 @@ namespace libtorrent return; } - // parse out "peers" from the resume data and add them to the peer list - entry const* peers_entry = m_resume_data.find_key("peers"); - if (peers_entry && peers_entry->type() == entry::list_t) + if (m_resume_entry.type() == lazy_entry::dict_t) { - peer_id id; - std::fill(id.begin(), id.end(), 0); - entry::list_type const& peer_list = peers_entry->list(); - - for (entry::list_type::const_iterator i = peer_list.begin(); - i != peer_list.end(); ++i) + // parse out "peers" from the resume data and add them to the peer list + if (lazy_entry const* peers_entry = m_resume_entry.dict_find_list("peers")) { - if (i->type() != entry::dictionary_t) continue; - entry const* ip = i->find_key("ip"); - entry const* port = i->find_key("port"); - if (ip == 0 || port == 0 - || ip->type() != entry::string_t - || port->type() != entry::int_t) - continue; - tcp::endpoint a( - address::from_string(ip->string()) - , (unsigned short)port->integer()); - m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0); + peer_id id(0); + + for (int i = 0; i < peers_entry->list_size(); ++i) + { + lazy_entry const* e = peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + tcp::endpoint a(address::from_string(ip), (unsigned short)port); + m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0); + } } - } - // parse out "banned_peers" and add them as banned - entry const* banned_peers_entry = m_resume_data.find_key("banned_peers"); - if (banned_peers_entry != 0 && banned_peers_entry->type() == entry::list_t) - { - peer_id id; - std::fill(id.begin(), id.end(), 0); - entry::list_type const& peer_list = banned_peers_entry->list(); - - for (entry::list_type::const_iterator i = peer_list.begin(); - i != peer_list.end(); ++i) + // parse out "banned_peers" and add them as banned + if (lazy_entry const* banned_peers_entry = m_resume_entry.dict_find_list("banned_peers")) { - if (i->type() != entry::dictionary_t) continue; - entry const* ip = i->find_key("ip"); - entry const* port = i->find_key("port"); - if (ip == 0 || port == 0 - || ip->type() != entry::string_t - || port->type() != entry::int_t) - continue; - tcp::endpoint a( - address::from_string(ip->string()) - , (unsigned short)port->integer()); - policy::peer* p = m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0); - if (p) p->banned = true; + peer_id id(0); + + for (int i = 0; i < banned_peers_entry->list_size(); ++i) + { + lazy_entry const* e = banned_peers_entry->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + std::string ip = e->dict_find_string_value("ip"); + int port = e->dict_find_int_value("port"); + if (ip.empty() || port == 0) continue; + tcp::endpoint a(address::from_string(ip), (unsigned short)port); + policy::peer* p = m_policy.peer_from_tracker(a, id, peer_info::resume_data, 0); + if (p) p->banned = true; + } } } @@ -551,17 +563,15 @@ namespace libtorrent // there are either no files for this torrent // or the resume_data was accepted - if (!fastresume_rejected) + if (!fastresume_rejected && m_resume_entry.type() == lazy_entry::dict_t) { - TORRENT_ASSERT(m_resume_data.type() == entry::dictionary_t); - // parse have bitmask - entry const* pieces = m_resume_data.find_key("pieces"); - if (pieces && pieces->type() == entry::string_t - && int(pieces->string().length()) == m_torrent_file->num_pieces()) + lazy_entry const* pieces = m_resume_entry.dict_find("pieces"); + if (pieces && pieces->type() == lazy_entry::string_t + && int(pieces->string_length()) == m_torrent_file->num_pieces()) { - std::string const& pieces_str = pieces->string(); - for (int i = 0, end(pieces_str.size()); i < end; ++i) + char const* pieces_str = pieces->string_ptr(); + for (int i = 0, end(pieces->string_length()); i < end; ++i) { if ((pieces_str[i] & 1) == 0) continue; m_picker->we_have(i); @@ -572,27 +582,20 @@ namespace libtorrent int num_blocks_per_piece = static_cast(torrent_file().piece_length()) / block_size(); - entry const* unfinished_ent = m_resume_data.find_key("unfinished"); - if (unfinished_ent != 0 && unfinished_ent->type() == entry::list_t) + if (lazy_entry const* unfinished_ent = m_resume_entry.dict_find_list("unfinished")) { - entry::list_type const& unfinished = unfinished_ent->list(); - int index = 0; - for (entry::list_type::const_iterator i = unfinished.begin(); - i != unfinished.end(); ++i, ++index) + for (int i = 0; i < unfinished_ent->list_size(); ++i) { - if (i->type() != entry::dictionary_t) continue; - entry const* piece = i->find_key("piece"); - if (piece == 0 || piece->type() != entry::int_t) continue; - int piece_index = int(piece->integer()); - if (piece_index < 0 || piece_index >= torrent_file().num_pieces()) - continue; + lazy_entry const* e = unfinished_ent->list_at(i); + if (e->type() != lazy_entry::dict_t) continue; + int piece = e->dict_find_int_value("piece", -1); + if (piece < 0 || piece > torrent_file().num_pieces()) continue; - if (m_picker->have_piece(piece_index)) - m_picker->we_dont_have(piece_index); + if (m_picker->have_piece(piece)) + m_picker->we_dont_have(piece); - entry const* bitmask_ent = i->find_key("bitmask"); - if (bitmask_ent == 0 || bitmask_ent->type() != entry::string_t) break; - std::string const& bitmask = bitmask_ent->string(); + std::string bitmask = e->dict_find_string_value("bitmask"); + if (bitmask.empty()) continue; const int num_bitmask_bytes = (std::max)(num_blocks_per_piece / 8, 1); if ((int)bitmask.size() != num_bitmask_bytes) continue; @@ -605,10 +608,10 @@ namespace libtorrent const int bit = j * 8 + k; if (bits & (1 << k)) { - m_picker->mark_as_finished(piece_block(piece_index, bit), 0); - if (m_picker->is_piece_finished(piece_index)) - async_verify_piece(piece_index, bind(&torrent::piece_finished - , shared_from_this(), piece_index, _1)); + m_picker->mark_as_finished(piece_block(piece, bit), 0); + if (m_picker->is_piece_finished(piece)) + async_verify_piece(piece, bind(&torrent::piece_finished + , shared_from_this(), piece, _1)); } } } @@ -649,8 +652,9 @@ namespace libtorrent if (m_auto_managed) set_queue_position((std::numeric_limits::max)()); - m_resume_data = entry(); - m_storage->async_check_fastresume(&m_resume_data + std::vector().swap(m_resume_data); + m_resume_entry = lazy_entry(); + m_storage->async_check_fastresume(&m_resume_entry , bind(&torrent::on_force_recheck , shared_from_this(), _1, _2)); } @@ -761,8 +765,7 @@ namespace libtorrent bind(&torrent::on_announce_disp, self, _1)); // announce with the local discovery service - if (!m_paused) - m_ses.announce_lsd(m_torrent_file->info_hash()); + if (!is_paused()) m_ses.announce_lsd(m_torrent_file->info_hash()); } else { @@ -772,7 +775,7 @@ namespace libtorrent } #ifndef TORRENT_DISABLE_DHT - if (m_paused) return; + if (is_paused()) return; if (!m_ses.m_dht) return; ptime now = time_now(); if (should_announce_dht() && now - m_last_dht_announce > minutes(14)) @@ -842,7 +845,7 @@ namespace libtorrent m_just_paused = false; return true; } - return !m_paused && m_next_request < time_now(); + return !is_paused() && m_next_request < time_now(); } void torrent::tracker_warning(tracker_request const& req, std::string const& msg) @@ -1501,7 +1504,7 @@ namespace libtorrent m_abort = true; // if the torrent is paused, it doesn't need // to announce with even=stopped again. - if (!m_paused) + if (!is_paused()) m_event = tracker_request::stopped; // disconnect all peers and close all // files belonging to the torrents @@ -2414,31 +2417,14 @@ namespace libtorrent } #endif - void torrent::read_resume_data(entry const& rd) + void torrent::read_resume_data(lazy_entry const& rd) { - entry const* e = 0; - e = rd.find_key("total_uploaded"); - m_total_uploaded = (e != 0 && e->type() == entry::int_t)?e->integer():0; - e = rd.find_key("total_downloaded"); - m_total_downloaded = (e != 0 && e->type() == entry::int_t)?e->integer():0; - - e = rd.find_key("active_time"); - m_active_time = seconds((e != 0 && e->type() == entry::int_t)?e->integer():0); - e = rd.find_key("seeding_time"); - m_seeding_time = seconds((e != 0 && e->type() == entry::int_t)?e->integer():0); - - e = rd.find_key("num_seeds"); - m_complete = (e != 0 && e->type() == entry::int_t)?e->integer():-1; - e = rd.find_key("num_downloaders"); - m_incomplete = (e != 0 && e->type() == entry::int_t)?e->integer():-1; - /* - m_total_uploaded = rd.find_int_value("total_uploaded"); - m_total_downloaded = rd.find_inte_value("total_downloaded"); - m_active_time = rd.find_int_value("active_time"); - m_seeding_time = rd.find_int_value("seeding_time"); - m_complete = rd.find_int_value("num_seeds", -1); - m_incomplete = rd.find_int_value("num_downloaders", -1); - */ + m_total_uploaded = rd.dict_find_int_value("total_uploaded"); + m_total_downloaded = rd.dict_find_int_value("total_downloaded"); + m_active_time = seconds(rd.dict_find_int_value("active_time")); + m_seeding_time = seconds(rd.dict_find_int_value("seeding_time")); + m_complete = rd.dict_find_int_value("num_seeds", -1); + m_incomplete = rd.dict_find_int_value("num_downloaders", -1); } void torrent::write_resume_data(entry& ret) const @@ -2865,7 +2851,7 @@ namespace libtorrent bool torrent::want_more_peers() const { return int(m_connections.size()) < m_max_connections - && !m_paused + && !is_paused() && m_state != torrent_status::checking_files && (m_state != torrent_status::queued_for_checking || !valid_metadata()) @@ -3333,8 +3319,8 @@ namespace libtorrent { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - TORRENT_ASSERT(m_resume_data.type() == entry::dictionary_t - || m_resume_data.type() == entry::undefined_t); + TORRENT_ASSERT(m_resume_entry.type() == lazy_entry::dict_t + || m_resume_entry.type() == lazy_entry::none_t); TORRENT_ASSERT(m_bandwidth_queue[0].size() <= m_connections.size()); TORRENT_ASSERT(m_bandwidth_queue[1].size() <= m_connections.size()); @@ -3596,7 +3582,7 @@ namespace libtorrent #endif disconnect_all(); - if (!m_paused) + if (!is_paused()) m_just_paused = true; m_paused = true; // tell the tracker that we stopped @@ -3711,11 +3697,24 @@ namespace libtorrent } } + bool torrent::is_paused() const + { + return m_paused || m_ses.is_paused(); + } + void torrent::pause() { INVARIANT_CHECK; if (m_paused) return; + m_paused = true; + if (m_ses.is_paused()) return; + do_pause(); + } + + void torrent::do_pause() + { + if (!is_paused()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -3740,7 +3739,6 @@ namespace libtorrent #endif disconnect_all(); - m_paused = true; // tell the tracker that we stopped m_event = tracker_request::stopped; m_just_paused = true; @@ -3766,6 +3764,13 @@ namespace libtorrent INVARIANT_CHECK; if (!m_paused) return; + m_paused = false; + do_resume(); + } + + void torrent::do_resume() + { + if (is_paused()) return; #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() @@ -3781,7 +3786,6 @@ namespace libtorrent } #endif - m_paused = false; m_started = time_now(); m_error.clear(); diff --git a/libtorrent/src/torrent_info.cpp b/libtorrent/src/torrent_info.cpp index 8afd2e408..c6c222fa0 100755 --- a/libtorrent/src/torrent_info.cpp +++ b/libtorrent/src/torrent_info.cpp @@ -47,7 +47,6 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #ifdef _MSC_VER @@ -58,6 +57,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/bencode.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/entry.hpp" +#include "libtorrent/file.hpp" namespace gr = boost::gregorian; @@ -207,6 +207,21 @@ namespace namespace libtorrent { + + int load_file(fs::path const& filename, std::vector& v) + { + file f; + if (!f.open(filename, file::in)) return -1; + f.seek(0, file::end); + size_type s = f.tell(); + if (s > 5000000) return -2; + v.resize(s); + f.seek(0); + size_type read = f.read(&v[0], s); + if (read != s) return -3; + return 0; + } + // standard constructor that parses a torrent file torrent_info::torrent_info(entry const& torrent_file) : m_creation_date(pt::ptime(pt::not_a_date_time)) @@ -226,7 +241,7 @@ namespace libtorrent if (!parse_torrent_file(e, error)) throw invalid_torrent_file(); #else - read_torrent_info(e, error); + parse_torrent_file(e, error); #endif } @@ -242,7 +257,7 @@ namespace libtorrent if (!parse_torrent_file(torrent_file, error)) throw invalid_torrent_file(); #else - read_torrent_info(torrent_file, error); + parse_torrent_file(torrent_file, error); #endif } @@ -260,7 +275,7 @@ namespace libtorrent if (!parse_torrent_file(e, error)) throw invalid_torrent_file(); #else - read_torrent_info(e, error); + parse_torrent_file(e, error); #endif } @@ -277,26 +292,23 @@ namespace libtorrent , m_piece_hashes(0) {} - torrent_info::torrent_info(char const* filename) + torrent_info::torrent_info(fs::path const& filename) : m_creation_date(pt::ptime(pt::not_a_date_time)) , m_multifile(false) , m_private(false) { - size_type s = fs::file_size(fs::path(filename)); - // don't load torrent files larger than 2 MB - if (s > 2000000) return; - std::vector buf(s); - std::ifstream f(filename); - f.read(&buf[0], s); + std::vector buf; + int ret = load_file(filename, buf); + if (ret < 0) return; - std::string error; lazy_entry e; lazy_bdecode(&buf[0], &buf[0] + buf.size(), e); + std::string error; #ifndef BOOST_NO_EXCEPTIONS if (!parse_torrent_file(e, error)) throw invalid_torrent_file(); #else - read_torrent_info(e, error); + parse_torrent_file(e, error); #endif } diff --git a/libtorrent/src/upnp.cpp b/libtorrent/src/upnp.cpp index 5de9bf156..d4cc8a4ca 100644 --- a/libtorrent/src/upnp.cpp +++ b/libtorrent/src/upnp.cpp @@ -82,7 +82,7 @@ upnp::upnp(io_service& ios, connection_queue& cc if (state) { - upnp_state_t* s = (upnp_state_t*)s; + upnp_state_t* s = (upnp_state_t*)state; m_devices.swap(s->devices); m_mappings.swap(s->mappings); delete s; diff --git a/libtorrent/src/ut_metadata.cpp b/libtorrent/src/ut_metadata.cpp index 0a1f139d7..17246e5b6 100644 --- a/libtorrent/src/ut_metadata.cpp +++ b/libtorrent/src/ut_metadata.cpp @@ -235,18 +235,20 @@ namespace libtorrent { namespace } // called when the extension handshake from the other end is received - virtual bool on_extension_handshake(entry const& h) + virtual bool on_extension_handshake(lazy_entry const& h) { - entry const* metadata_size = h.find_key("metadata_size"); - if (metadata_size && metadata_size->type() == entry::int_t) - m_tp.metadata_size(metadata_size->integer()); + m_message_index = 0; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; - entry const* messages = h.find_key("m"); - if (!messages || messages->type() != entry::dictionary_t) return false; + int index = messages->dict_find_int_value("ut_metadata", -1); + if (index == -1) return false; + m_message_index = index; - entry const* index = messages->find_key("ut_metadata"); - if (!index || index->type() != entry::int_t) return false; - m_message_index = int(index->integer()); + int metadata_size = h.dict_find_int_value("metadata_size"); + if (metadata_size > 0) + m_tp.metadata_size(metadata_size); return true; } diff --git a/libtorrent/src/ut_pex.cpp b/libtorrent/src/ut_pex.cpp index 4341564f7..723f119d2 100644 --- a/libtorrent/src/ut_pex.cpp +++ b/libtorrent/src/ut_pex.cpp @@ -196,16 +196,16 @@ namespace libtorrent { namespace messages[extension_name] = extension_index; } - virtual bool on_extension_handshake(entry const& h) + virtual bool on_extension_handshake(lazy_entry const& h) { m_message_index = 0; - entry const* messages = h.find_key("m"); - if (!messages || messages->type() != entry::dictionary_t) return false; + if (h.type() != lazy_entry::dict_t) return false; + lazy_entry const* messages = h.dict_find("m"); + if (!messages || messages->type() != lazy_entry::dict_t) return false; - entry const* index = messages->find_key(extension_name); - if (!index || index->type() != entry::int_t) return false; - - m_message_index = int(index->integer()); + int index = messages->dict_find_int_value(extension_name, -1); + if (index == -1) return false; + m_message_index = index; return true; } @@ -222,58 +222,55 @@ namespace libtorrent { namespace if (body.left() < length) return true; - entry pex_msg = bdecode(body.begin, body.end); - if (pex_msg.type() == entry::undefined_t) + lazy_entry pex_msg; + int ret = lazy_bdecode(body.begin, body.end, pex_msg); + if (pex_msg.type() != lazy_entry::dict_t) { m_pc.disconnect("invalid bencoding in ut_metadata message", 2); return true; } - entry const* p = pex_msg.find_key("added"); - entry const* pf = pex_msg.find_key("added.f"); + lazy_entry const* p = pex_msg.dict_find("added"); + lazy_entry const* pf = pex_msg.dict_find("added.f"); - if (p != 0 && pf != 0 && p->type() == entry::string_t && pf->type() == entry::string_t) + if (p != 0 + && pf != 0 + && p->type() == lazy_entry::string_t + && pf->type() == lazy_entry::string_t + && pf->string_length() == p->string_length() / 6) { - std::string const& peers = p->string(); - std::string const& peer_flags = pf->string(); - - int num_peers = peers.length() / 6; - char const* in = peers.c_str(); - char const* fin = peer_flags.c_str(); - - if (int(peer_flags.size()) != num_peers) - return true; + int num_peers = pf->string_length(); + char const* in = p->string_ptr(); + char const* fin = pf->string_ptr(); 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); - char flags = detail::read_uint8(fin); + char flags = *fin++; p.peer_from_tracker(adr, pid, peer_info::pex, flags); } } - entry const* p6 = pex_msg.find_key("added6"); - entry const* p6f = pex_msg.find_key("added6.f"); - if (p6 && p6f && p6->type() == entry::string_t && p6f->type() == entry::string_t) + lazy_entry const* p6 = pex_msg.dict_find("added6"); + lazy_entry const* p6f = pex_msg.dict_find("added6.f"); + if (p6 != 0 + && p6f != 0 + && p6->type() == lazy_entry::string_t + && p6f->type() == lazy_entry::string_t + && p6f->string_length() == p6->string_length() / 18) { - std::string const& peers6 = p6->string(); - std::string const& peer6_flags = p6f->string(); - - int num_peers = peers6.length() / 18; - char const* in = peers6.c_str(); - char const* fin = peer6_flags.c_str(); - - if (int(peer6_flags.size()) != num_peers) - return true; + int num_peers = p6f->string_length(); + char const* in = p6->string_ptr(); + char const* fin = p6f->string_ptr(); peer_id pid(0); policy& p = m_torrent.get_policy(); for (int i = 0; i < num_peers; ++i) { tcp::endpoint adr = detail::read_v6_endpoint(in); - char flags = detail::read_uint8(fin); + char flags = *fin++; p.peer_from_tracker(adr, pid, peer_info::pex, flags); } }