diff --git a/ChangeLog b/ChangeLog index cfde3cc00..374ce19a7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -50,6 +50,11 @@ * resume data no longer has timestamps of files * require C++11 to build libtorrent + * storage optimization to peer classes + * fix torrent name in alerts of builds with deprecated functions + * make torrent_info::is_valid() return false if torrent failed to load + * fix per-torrent rate limits for >256 peer classes + * don't load user_agent and peer_fingerprint from session_state * fix file rename issue with name prefix matching torrent name * fix division by zero when setting tick_interval > 1000 * fix move_storage() to its own directory (would delete the files) diff --git a/bindings/python/src/fingerprint.cpp b/bindings/python/src/fingerprint.cpp index 3abdc68d3..a4bcf391a 100644 --- a/bindings/python/src/fingerprint.cpp +++ b/bindings/python/src/fingerprint.cpp @@ -10,6 +10,8 @@ void bind_fingerprint() using namespace boost::python; using namespace libtorrent; + def("generate_fingerprint", &generate_fingerprint); + #ifndef TORRENT_NO_DEPRECATE #ifdef __GNUC__ #pragma GCC diagnostic push diff --git a/bindings/python/test.py b/bindings/python/test.py index f0a811b9e..8237dd197 100644 --- a/bindings/python/test.py +++ b/bindings/python/test.py @@ -297,6 +297,9 @@ class test_session(unittest.TestCase): self.assertEqual(s.get_settings()['num_want'], 66) self.assertEqual(s.get_settings()['user_agent'], 'test123') + def test_fingerprint(self): + self.assertEqual(lt.generate_fingerprint('LT', 0, 1, 2, 3), '-LT0123-') + self.assertEqual(lt.generate_fingerprint('..', 10, 1, 2, 3), '-..A123-') if __name__ == '__main__': print(lt.__version__) diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index a2280466a..a80199144 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -164,11 +164,6 @@ namespace libtorrent return bind_ep.address(); } - // TODO: function not used and not exported in release? - // returns true if the given device exists - TORRENT_EXTRA_EXPORT bool has_interface(char const* name, io_service& ios - , error_code& ec); - // returns the device name whose local address is ``addr``. If // no such device is found, an empty string is returned. TORRENT_EXTRA_EXPORT std::string device_for_address(address addr diff --git a/include/libtorrent/peer_class.hpp b/include/libtorrent/peer_class.hpp index 1333d2c9d..9842e78a9 100644 --- a/include/libtorrent/peer_class.hpp +++ b/include/libtorrent/peer_class.hpp @@ -37,13 +37,14 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/assert.hpp" #include +#include #include #include #include namespace libtorrent { - typedef std::uint16_t peer_class_t; + typedef std::uint32_t peer_class_t; struct peer_class_info { @@ -82,12 +83,13 @@ namespace libtorrent int download_priority; }; - struct TORRENT_EXTRA_EXPORT peer_class : std::enable_shared_from_this + struct TORRENT_EXTRA_EXPORT peer_class { friend struct peer_class_pool; explicit peer_class(std::string const& l) - : ignore_unchoke_slots(false) + : in_use(true) + , ignore_unchoke_slots(false) , connection_limit_factor(100) , label(l) , references(1) @@ -96,6 +98,12 @@ namespace libtorrent priority[1] = 1; } + void clear() + { + in_use = false; + label.clear(); + } + void set_info(peer_class_info const* pci); void get_info(peer_class_info* pci) const; @@ -106,6 +114,9 @@ namespace libtorrent // keeps track of the current quotas bandwidth_channel channel[2]; + // this is set to false when this slot is not in use for a peer_class + bool in_use; + bool ignore_unchoke_slots; int connection_limit_factor; @@ -119,7 +130,6 @@ namespace libtorrent private: int references; - }; struct TORRENT_EXTRA_EXPORT peer_class_pool @@ -134,7 +144,7 @@ namespace libtorrent // state for peer classes (a peer can belong to multiple classes) // this can control - std::vector> m_peer_classes; + std::deque m_peer_classes; // indices in m_peer_classes that are no longer used std::vector m_free_list; diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index f89be6859..9689b8e92 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -128,6 +128,11 @@ namespace libtorrent // The ``flags`` argument is used to filter which parts of the session // state to save or load. By default, all state is saved/restored (except // for the individual torrents). see save_state_flags_t + // + // When saving settings, there are two fields that are *not* loaded. + // ``peer_fingerprint`` and ``user_agent``. Those are left as configured + // by the ``session_settings`` passed to the session constructor or + // subsequently set via apply_settings(). void save_state(entry& e, std::uint32_t flags = 0xffffffff) const; void load_state(bdecode_node const& e, std::uint32_t flags = 0xffffffff); diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index be5e89b52..2916b3d35 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -87,8 +87,13 @@ namespace libtorrent void set_int(int name, int val); void set_bool(int name, bool val); bool has_val(int name) const; + + // clear the settings pack from all settings void clear(); + // clear a specific setting from the pack + void clear(int name); + std::string const& get_str(int name) const; int get_int(int name) const; bool get_bool(int name) const; diff --git a/src/alert.cpp b/src/alert.cpp index f757fc002..5b78e637b 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -77,17 +77,13 @@ namespace libtorrent { } #ifndef TORRENT_NO_DEPRECATE - name = torrent_name(); + name = m_alloc.get().ptr(m_name_idx); #endif } char const* torrent_alert::torrent_name() const { -#ifndef TORRENT_NO_DEPRECATE - return name.c_str(); -#else return m_alloc.get().ptr(m_name_idx); -#endif } std::string torrent_alert::message() const @@ -125,11 +121,7 @@ namespace libtorrent { char const* tracker_alert::tracker_url() const { -#ifndef TORRENT_NO_DEPRECATE - return url.c_str(); -#else return m_alloc.get().ptr(m_url_idx); -#endif } std::string tracker_alert::message() const @@ -201,11 +193,7 @@ namespace libtorrent { char const* file_renamed_alert::new_name() const { -#ifndef TORRENT_NO_DEPRECATE - return name.c_str(); -#else return m_alloc.get().ptr(m_name_idx); -#endif } std::string file_renamed_alert::message() const @@ -299,11 +287,7 @@ namespace libtorrent { char const* tracker_error_alert::error_message() const { -#ifndef TORRENT_NO_DEPRECATE - return msg.c_str(); -#else return m_alloc.get().ptr(m_msg_idx); -#endif } std::string tracker_error_alert::message() const @@ -329,11 +313,7 @@ namespace libtorrent { char const* tracker_warning_alert::warning_message() const { -#ifndef TORRENT_NO_DEPRECATE - return msg.c_str(); -#else return m_alloc.get().ptr(m_msg_idx); -#endif } std::string tracker_warning_alert::message() const @@ -384,12 +364,8 @@ namespace libtorrent { char const* scrape_failed_alert::error_message() const { -#ifndef TORRENT_NO_DEPRECATE - return msg.c_str(); -#else if (m_msg_idx == aux::allocation_slot()) return ""; else return m_alloc.get().ptr(m_msg_idx); -#endif } std::string scrape_failed_alert::message() const @@ -655,11 +631,7 @@ namespace libtorrent { char const* storage_moved_alert::storage_path() const { -#ifndef TORRENT_NO_DEPRECATE - return path.c_str(); -#else return m_alloc.get().ptr(m_path_idx); -#endif } storage_moved_failed_alert::storage_moved_failed_alert( @@ -676,11 +648,7 @@ namespace libtorrent { char const* storage_moved_failed_alert::file_path() const { -#ifndef TORRENT_NO_DEPRECATE - return file.c_str(); -#else return m_alloc.get().ptr(m_file_idx); -#endif } std::string storage_moved_failed_alert::message() const @@ -1032,11 +1000,7 @@ namespace libtorrent { char const* portmap_log_alert::log_message() const { -#ifndef TORRENT_NO_DEPRECATE - return msg.c_str(); -#else return m_alloc.get().ptr(m_log_idx); -#endif } std::string portmap_log_alert::message() const @@ -1075,11 +1039,7 @@ namespace libtorrent { char const* fastresume_rejected_alert::file_path() const { -#ifndef TORRENT_NO_DEPRECATE - return file.c_str(); -#else return m_alloc.get().ptr(m_path_idx); -#endif } peer_blocked_alert::peer_blocked_alert(aux::stack_allocator& alloc @@ -1246,11 +1206,7 @@ namespace libtorrent { char const* trackerid_alert::tracker_id() const { -#ifndef TORRENT_NO_DEPRECATE - return trackerid.c_str(); -#else return m_alloc.get().ptr(m_tracker_idx); -#endif } std::string trackerid_alert::message() const @@ -1837,12 +1793,8 @@ namespace libtorrent { char const* url_seed_alert::error_message() const { -#ifndef TORRENT_NO_DEPRECATE - return msg.c_str(); -#else if (m_msg_idx == aux::allocation_slot()) return ""; return m_alloc.get().ptr(m_msg_idx); -#endif } file_error_alert::file_error_alert(aux::stack_allocator& alloc diff --git a/src/create_torrent.cpp b/src/create_torrent.cpp index c9dcfc0ad..36c634aa7 100644 --- a/src/create_torrent.cpp +++ b/src/create_torrent.cpp @@ -380,6 +380,7 @@ namespace libtorrent TORRENT_ASSERT(ti.num_files() > 0); TORRENT_ASSERT(ti.total_size() > 0); + if (!ti.is_valid()) return; if (ti.creation_date() > 0) m_creation_date = ti.creation_date(); if (!ti.creator().empty()) set_creator(ti.creator().c_str()); diff --git a/src/enum_net.cpp b/src/enum_net.cpp index 90eb2bebc..705909901 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -1107,17 +1107,6 @@ namespace libtorrent return ret; } - // returns true if the given device exists - bool has_interface(char const* name, io_service& ios, error_code& ec) - { - std::vector ifs = enum_net_interfaces(ios, ec); - if (ec) return false; - - for (auto const& iface : ifs) - if (iface.name == name) return true; - return false; - } - // returns the device name whose local address is ``addr``. If // no such device is found, an empty string is returned. std::string device_for_address(address addr, io_service& ios, error_code& ec) diff --git a/src/peer_class.cpp b/src/peer_class.cpp index 4f38d2f29..2768636a5 100644 --- a/src/peer_class.cpp +++ b/src/peer_class.cpp @@ -80,47 +80,47 @@ namespace libtorrent { ret = m_free_list.back(); m_free_list.pop_back(); + m_peer_classes[ret] = peer_class(label); } else { - TORRENT_ASSERT(m_peer_classes.size() < 0x10000); + TORRENT_ASSERT(m_peer_classes.size() < 0x100000000); ret = peer_class_t(m_peer_classes.size()); - m_peer_classes.push_back(std::shared_ptr()); + m_peer_classes.push_back(peer_class(label)); } - TORRENT_ASSERT(m_peer_classes[ret].get() == nullptr); - m_peer_classes[ret] = std::make_shared(label); return ret; } void peer_class_pool::decref(peer_class_t c) { TORRENT_ASSERT(c < m_peer_classes.size()); - TORRENT_ASSERT(m_peer_classes[c].get()); + TORRENT_ASSERT(m_peer_classes[c].in_use); + TORRENT_ASSERT(m_peer_classes[c].references > 0); - --m_peer_classes[c]->references; - if (m_peer_classes[c]->references) return; - m_peer_classes[c].reset(); + --m_peer_classes[c].references; + if (m_peer_classes[c].references) return; + m_peer_classes[c].clear(); m_free_list.push_back(c); } void peer_class_pool::incref(peer_class_t c) { TORRENT_ASSERT(c < m_peer_classes.size()); - TORRENT_ASSERT(m_peer_classes[c].get()); + TORRENT_ASSERT(m_peer_classes[c].in_use); - ++m_peer_classes[c]->references; + ++m_peer_classes[c].references; } peer_class* peer_class_pool::at(peer_class_t c) { - if (c >= m_peer_classes.size()) return nullptr; - return m_peer_classes[c].get(); + if (c >= m_peer_classes.size() || !m_peer_classes[c].in_use) return nullptr; + return &m_peer_classes[c]; } peer_class const* peer_class_pool::at(peer_class_t c) const { - if (c >= m_peer_classes.size()) return nullptr; - return m_peer_classes[c].get(); + if (c >= m_peer_classes.size() || !m_peer_classes[c].in_use) return nullptr; + return &m_peer_classes[c]; } } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 7f00ebed8..b163a3280 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -790,6 +790,12 @@ namespace aux { { // apply_settings_pack will update dht and proxy settings_pack pack = load_pack_from_dict(settings); + + // these settings are not loaded from state + // they are set by the client software, not configured by users + pack.clear(settings_pack::user_agent); + pack.clear(settings_pack::peer_fingerprint); + apply_settings_pack_impl(pack); #ifndef TORRENT_DISABLE_DHT need_update_dht = false; diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index 0df742750..bbaa4b457 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -666,4 +666,38 @@ namespace libtorrent m_ints.clear(); m_bools.clear(); } + + void settings_pack::clear(int const name) + { + switch (name & type_mask) + { + case string_type_base: + { + std::pair v(name, std::string()); + std::vector >::iterator i + = std::lower_bound(m_strings.begin(), m_strings.end(), v + , &compare_first); + if (i != m_strings.end() && i->first == name) m_strings.erase(i); + break; + } + case int_type_base: + { + std::pair v(name, 0); + std::vector >::iterator i + = std::lower_bound(m_ints.begin(), m_ints.end(), v + , &compare_first); + if (i != m_ints.end() && i->first == name) m_ints.erase(i); + break; + } + case bool_type_base: + { + std::pair v(name, false); + std::vector >::iterator i + = std::lower_bound(m_bools.begin(), m_bools.end(), v + , &compare_first); + if (i != m_bools.end() && i->first == name) m_bools.erase(i); + break; + } + } + } } diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index 68e93e8b8..e0e89e5dc 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -1081,6 +1081,8 @@ namespace libtorrent if (!name_ent) { ec = errors::torrent_missing_name; + // mark the torrent as invalid + m_files.set_piece_length(0); return false; } @@ -1097,14 +1099,22 @@ namespace libtorrent // this is the counter used to name pad files int pad_file_cnt = 0; if (!extract_single_file(info, files, "", info_ptr_diff, true, pad_file_cnt, ec)) + { + // mark the torrent as invalid + m_files.set_piece_length(0); return false; + } m_flags &= ~multifile; } else { if (!extract_files(files_node, files, name, info_ptr_diff, ec)) + { + // mark the torrent as invalid + m_files.set_piece_length(0); return false; + } m_flags |= multifile; } TORRENT_ASSERT(!files.name().empty()); @@ -1121,6 +1131,8 @@ namespace libtorrent if (!pieces && !root_hash) { ec = errors::torrent_missing_pieces; + // mark the torrent as invalid + m_files.set_piece_length(0); return false; } @@ -1129,6 +1141,8 @@ namespace libtorrent if (pieces.string_length() != files.num_pieces() * 20) { ec = errors::torrent_invalid_hashes; + // mark the torrent as invalid + m_files.set_piece_length(0); return false; } @@ -1142,11 +1156,15 @@ namespace libtorrent if (root_hash.string_length() != 20) { ec = errors::torrent_invalid_hashes; + // mark the torrent as invalid + m_files.set_piece_length(0); return false; } if (files.num_pieces() >= std::numeric_limits::max()/2) { ec = errors::too_many_pieces_in_torrent; + // mark the torrent as invalid + m_files.set_piece_length(0); return false; } int const num_leafs = merkle_num_leafs(files.num_pieces()); diff --git a/test/test_peer_classes.cpp b/test/test_peer_classes.cpp index 08b022144..984fb51d6 100644 --- a/test/test_peer_classes.cpp +++ b/test/test_peer_classes.cpp @@ -66,11 +66,11 @@ TORRENT_TEST(peer_class) TEST_CHECK(id3 == id2 + 1); // make sure refcounting works - TEST_CHECK(class_name(id3, pool) == "test3"); + TEST_EQUAL(class_name(id3, pool), "test3"); pool.incref(id3); - TEST_CHECK(class_name(id3, pool) == "test3"); + TEST_EQUAL(class_name(id3, pool), "test3"); pool.decref(id3); - TEST_CHECK(class_name(id3, pool) == "test3"); + TEST_EQUAL(class_name(id3, pool), "test3"); pool.decref(id3); // it should have been deleted now TEST_CHECK(pool.at(id3) == nullptr); @@ -81,8 +81,8 @@ TORRENT_TEST(peer_class) peer_class_info i; pool.at(id2)->get_info(&i); - TEST_CHECK(i.upload_limit == 1000); - TEST_CHECK(i.download_limit == 2000); + TEST_EQUAL(i.upload_limit, 1000); + TEST_EQUAL(i.download_limit, 2000); // test peer_class_type_filter peer_class_type_filter filter; diff --git a/test/test_session.cpp b/test/test_session.cpp index d391eac6e..e570b17f0 100644 --- a/test/test_session.cpp +++ b/test/test_session.cpp @@ -304,3 +304,47 @@ TORRENT_TEST(session_shutdown) lt::session ses(pack); } +// make sure we don't restore peer_id from session state +TORRENT_TEST(save_state_peer_id) +{ + lt::settings_pack pack; + pack.set_str(settings_pack::peer_fingerprint, "AAA"); + lt::session ses(pack); + lt::peer_id const pid1 = ses.id(); + TEST_CHECK(pid1[0] == 'A'); + TEST_CHECK(pid1[1] == 'A'); + TEST_CHECK(pid1[2] == 'A'); + + lt::entry st; + ses.save_state(st); + + pack.set_str(settings_pack::peer_fingerprint, "foobar"); + ses.apply_settings(pack); + + lt::peer_id const pid2 = ses.id(); + TEST_CHECK(pid2[0] == 'f'); + TEST_CHECK(pid2[1] == 'o'); + TEST_CHECK(pid2[2] == 'o'); + TEST_CHECK(pid2[3] == 'b'); + TEST_CHECK(pid2[4] == 'a'); + TEST_CHECK(pid2[5] == 'r'); + + + std::vector buf; + bencode(std::back_inserter(buf), st); + bdecode_node state; + error_code ec; + int ret = bdecode(buf.data(), buf.data() + buf.size() + , state, ec, nullptr, 100, 1000); + TEST_EQUAL(ret, 0); + ses.load_state(state); + + lt::peer_id const pid3 = ses.id(); + TEST_CHECK(pid3[0] == 'f'); + TEST_CHECK(pid3[1] == 'o'); + TEST_CHECK(pid3[2] == 'o'); + TEST_CHECK(pid3[3] == 'b'); + TEST_CHECK(pid3[4] == 'a'); + TEST_CHECK(pid3[5] == 'r'); +} + diff --git a/test/test_settings_pack.cpp b/test/test_settings_pack.cpp index 33d9a24fd..855eaa45d 100644 --- a/test/test_settings_pack.cpp +++ b/test/test_settings_pack.cpp @@ -132,6 +132,42 @@ TORRENT_TEST(clear) TEST_EQUAL(pack.has_val(settings_pack::user_agent), false); } +TORRENT_TEST(clear_single_int) +{ + settings_pack sp; + sp.set_int(settings_pack::max_out_request_queue, 1337); + + TEST_EQUAL(sp.get_int(settings_pack::max_out_request_queue), 1337); + + sp.clear(settings_pack::max_out_request_queue); + + TEST_EQUAL(sp.get_int(settings_pack::max_out_request_queue), 0); +} + +TORRENT_TEST(clear_single_bool) +{ + settings_pack sp; + sp.set_bool(settings_pack::send_redundant_have, true); + + TEST_EQUAL(sp.get_bool(settings_pack::send_redundant_have), true); + + sp.clear(settings_pack::send_redundant_have); + + TEST_EQUAL(sp.get_bool(settings_pack::send_redundant_have), false); +} + +TORRENT_TEST(clear_single_string) +{ + settings_pack sp; + sp.set_str(settings_pack::user_agent, "foobar"); + + TEST_EQUAL(sp.get_str(settings_pack::user_agent), "foobar"); + + sp.clear(settings_pack::user_agent); + + TEST_EQUAL(sp.get_str(settings_pack::user_agent), std::string()); +} + TORRENT_TEST(duplicates) { settings_pack p; diff --git a/test/test_torrent_info.cpp b/test/test_torrent_info.cpp index 849913af8..96b0f4eb9 100644 --- a/test/test_torrent_info.cpp +++ b/test/test_torrent_info.cpp @@ -762,6 +762,7 @@ TORRENT_TEST(parse_torrents) std::printf("E: \"%s\"\nexpected: \"%s\"\n", ec.message().c_str() , test_error_torrents[i].error.message().c_str()); TEST_CHECK(ec.message() == test_error_torrents[i].error.message()); + TEST_EQUAL(ti->is_valid(), false); } }