diff --git a/CMakeLists.txt b/CMakeLists.txt index f1f7bf9de..da82db116 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ set(sources escape_string string_util file + fingerprint gzip hasher hex diff --git a/ChangeLog b/ChangeLog index 1325acaf3..1e73fb0d8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,7 @@ * resume data no longer has timestamps of files * require C++11 to build libtorrent + * add utility function for generating peer ID fingerprint * fix bug in last-seen-complete * remove file size limit in torrent_info filename constructor * fix tail-padding for last file in create_torrent diff --git a/Jamfile b/Jamfile index ca030c9eb..d42a31f50 100644 --- a/Jamfile +++ b/Jamfile @@ -605,6 +605,7 @@ SOURCES = escape_string string_util file + fingerprint gzip hasher hex diff --git a/bindings/python/src/fingerprint.cpp b/bindings/python/src/fingerprint.cpp index 27215fac4..b079ef3c5 100644 --- a/bindings/python/src/fingerprint.cpp +++ b/bindings/python/src/fingerprint.cpp @@ -10,6 +10,7 @@ void bind_fingerprint() using namespace boost::python; using namespace libtorrent; +#ifndef TORRENT_NO_DEPRECATE class_("fingerprint", no_init) .def( init( @@ -23,5 +24,6 @@ void bind_fingerprint() .def_readonly("revision_version", &fingerprint::revision_version) .def_readonly("tag_version", &fingerprint::tag_version) ; +#endif } diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 7b7c0b163..db735b8fa 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -949,7 +949,7 @@ bool handle_alert(libtorrent::session& ses, libtorrent::alert* a save_file(filename, buffer); files.insert(std::pair(filename, h)); - hash_to_filename.insert(std::make_pair(hash, filename)); + hash_to_filename[hash] = filename; non_files.erase(h); } } diff --git a/include/libtorrent/fingerprint.hpp b/include/libtorrent/fingerprint.hpp index 5f4cc4884..8d8bcb22f 100644 --- a/include/libtorrent/fingerprint.hpp +++ b/include/libtorrent/fingerprint.hpp @@ -38,90 +38,61 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" #include "libtorrent/peer_id.hpp" -#include "libtorrent/assert.hpp" #include "libtorrent/export.hpp" namespace libtorrent { + // This is a utility function to produce a client ID fingerprint formatted to + // the most common convention. + // + // The name string should contain exactly two characters. These are the + // characters unique to your client, used to identify it. Make sure not to + // clash with anybody else. Here are some taken id's: + // + // +----------+-----------------------+ + // | id chars | client | + // +==========+=======================+ + // | 'AZ' | Azureus | + // +----------+-----------------------+ + // | 'LT' | libtorrent (default) | + // +----------+-----------------------+ + // | 'BX' | BittorrentX | + // +----------+-----------------------+ + // | 'MT' | Moonlight Torrent | + // +----------+-----------------------+ + // | 'TS' | Torrent Storm | + // +----------+-----------------------+ + // | 'SS' | Swarm Scope | + // +----------+-----------------------+ + // | 'XT' | Xan Torrent | + // +----------+-----------------------+ + // + // There's an informal directory of client id's here_. + // + // .. _here: http://wiki.theory.org/BitTorrentSpecification#peer_id + // + // The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the + // version of your client. + TORRENT_EXPORT std::string generate_fingerprint(std::string name + , int major, int minor = 0, int revision = 0, int tag = 0); + // The fingerprint class represents information about a client and its version. It is used // to encode this information into the client's peer id. - struct TORRENT_DEPRECATED_EXPORT fingerprint + struct TORRENT_DEPRECATED TORRENT_DEPRECATED_EXPORT fingerprint { + fingerprint(const char* id_string, int major, int minor, int revision, int tag); - // The constructor takes a ``char const*`` that should point to a string constant containing - // exactly two characters. These are the characters that should be unique for your client. Make - // sure not to clash with anybody else. Here are some taken id's: - // - // +----------+-----------------------+ - // | id chars | client | - // +==========+=======================+ - // | 'AZ' | Azureus | - // +----------+-----------------------+ - // | 'LT' | libtorrent (default) | - // +----------+-----------------------+ - // | 'BX' | BittorrentX | - // +----------+-----------------------+ - // | 'MT' | Moonlight Torrent | - // +----------+-----------------------+ - // | 'TS' | Torrent Storm | - // +----------+-----------------------+ - // | 'SS' | Swarm Scope | - // +----------+-----------------------+ - // | 'XT' | Xan Torrent | - // +----------+-----------------------+ - // - // There's an informal directory of client id's here_. - // - // .. _here: http://wiki.theory.org/BitTorrentSpecification#peer_id - // - // The ``major``, ``minor``, ``revision`` and ``tag`` parameters are used to identify the - // version of your client. - fingerprint(const char* id_string, int major, int minor, int revision, int tag) - : major_version(major) - , minor_version(minor) - , revision_version(revision) - , tag_version(tag) - { - TORRENT_ASSERT(id_string); - TORRENT_ASSERT(major >= 0); - TORRENT_ASSERT(minor >= 0); - TORRENT_ASSERT(revision >= 0); - TORRENT_ASSERT(tag >= 0); - TORRENT_ASSERT(std::strlen(id_string) == 2); - name[0] = id_string[0]; - name[1] = id_string[1]; - } - +#ifndef TORRENT_NO_DEPRECATE // generates the actual string put in the peer-id, and return it. - std::string to_string() const - { - char s[100]; - std::snprintf(s, 100, "-%c%c%c%c%c%c-" - , name[0], name[1] - , version_to_char(major_version) - , version_to_char(minor_version) - , version_to_char(revision_version) - , version_to_char(tag_version)); - return s; - } + std::string to_string() const; +#endif char name[2]; int major_version; int minor_version; int revision_version; int tag_version; - - private: - - char version_to_char(int v) const - { - if (v >= 0 && v < 10) return char('0' + v); - else if (v >= 10) return char('A' + (v - 10)); - TORRENT_ASSERT_FAIL(); - return '0'; - } - }; } diff --git a/include/libtorrent/identify_client.hpp b/include/libtorrent/identify_client.hpp index 20c0de84f..68de09920 100644 --- a/include/libtorrent/identify_client.hpp +++ b/include/libtorrent/identify_client.hpp @@ -35,21 +35,19 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/config.hpp" +#ifndef TORRENT_NO_DEPRECATE #include "libtorrent/aux_/disable_warnings_push.hpp" - #include - #include "libtorrent/aux_/disable_warnings_pop.hpp" +#endif #include "libtorrent/peer_id.hpp" #include "libtorrent/fingerprint.hpp" +// TODO: hide this declaration when deprecated functions are disabled, and +// remove its internal use namespace libtorrent { - - // TODO: hide these declarations when deprecated functions are disabled, and - // expose them internally in a header under aux_. - // these functions don't really need to be public. This mechanism of // advertising client software and version is also out-dated. @@ -59,6 +57,16 @@ namespace libtorrent TORRENT_DEPRECATED_EXPORT TORRENT_DEPRECATED std::string identify_client(const peer_id& p); +#ifndef TORRENT_NO_DEPRECATE + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif // Returns an optional fingerprint if any can be identified from the peer // id. This can be used to automate the identification of clients. It will // not be able to identify peers with non- standard encodings. Only Azureus @@ -67,6 +75,15 @@ namespace libtorrent boost::optional client_fingerprint(peer_id const& p); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TORRENT_NO_DEPRECATE + } #endif // TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 1a5ccbe4c..62cceea94 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -257,6 +257,14 @@ namespace libtorrent } #ifndef TORRENT_NO_DEPRECATE +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif TORRENT_DEPRECATED session(fingerprint const& print , int flags = start_default_features | add_default_plugins @@ -309,6 +317,12 @@ namespace libtorrent } start(flags, std::move(pack), nullptr); } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif #endif // TORRENT_NO_DEPRECATE // The destructor of session will notify all trackers that our torrents diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index 0aa9d5021..5d416cf79 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -207,7 +207,10 @@ namespace libtorrent // this is the fingerprint for the client. It will be used as the // prefix to the peer_id. If this is 20 bytes (or longer) it will be - // used as the peer-id + // truncated at 20 bytes and used as the entire peer-id + // + // There is a utility function, generate_fingerprint() that can be used + // to generate a standard client peer ID fingerprint prefix. peer_fingerprint, // This is a comma-separated list of IP port-pairs. They will be added diff --git a/src/Makefile.am b/src/Makefile.am index d9066a2be..ca013ce97 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -75,6 +75,7 @@ libtorrent_rasterbar_la_SOURCES = \ file.cpp \ file_pool.cpp \ file_storage.cpp \ + fingerprint.cpp \ gzip.cpp \ hasher.cpp \ hex.cpp \ diff --git a/src/bt_peer_connection.cpp b/src/bt_peer_connection.cpp index 34b501ba0..bf99bec5c 100644 --- a/src/bt_peer_connection.cpp +++ b/src/bt_peer_connection.cpp @@ -3381,8 +3381,7 @@ namespace libtorrent } m_client_version = identify_client(pid); - boost::optional f = client_fingerprint(pid); - if (f && std::equal(f->name, f->name + 2, "BC")) + if (pid[0] == '-' && pid[1] == 'B' && pid[2] == 'C' && pid[7] == '-') { // if this is a bitcomet client, lower the request queue size limit if (max_out_request_queue() > 50) max_out_request_queue(50); diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index f60137f6a..b267e4026 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -335,6 +335,8 @@ namespace libtorrent end = int(p->blocks_in_piece); } + TORRENT_ASSERT(end <= p->blocks_in_piece); + // count number of blocks that would be flushed int num_blocks = 0; for (int i = end-1; i >= 0; --i) @@ -2083,6 +2085,12 @@ namespace libtorrent DLOG("kick_hasher: %d - %d (piece: %d offset: %d)\n" , cursor, end, int(pe->piece), ph->offset); + // save a local copy of offset to avoid concurrent access + int offset = ph->offset; +#if TORRENT_USE_ASSERTS + int old_offset = offset; +#endif + l.unlock(); time_point start_time = clock_type::now(); @@ -2090,15 +2098,18 @@ namespace libtorrent for (int i = cursor; i < end; ++i) { cached_block_entry& bl = pe->blocks[i]; - int const size = (std::min)(block_size, piece_size - ph->offset); + int const size = (std::min)(block_size, piece_size - offset); ph->h.update(bl.buf, size); - ph->offset += size; + offset += size; } std::uint64_t hash_time = total_microseconds(clock_type::now() - start_time); l.lock(); + TORRENT_ASSERT(old_offset == ph->offset); + ph->offset = offset; + TORRENT_PIECE_ASSERT(pe->hashing, pe); TORRENT_PIECE_ASSERT(pe->hash, pe); @@ -2318,22 +2329,28 @@ namespace libtorrent // to keep the cache footprint low, try to evict a volatile piece m_disk_cache.try_evict_one_volatile(); + // save a local copy of offset to avoid concurrent access + int offset = ph->offset; +#if TORRENT_USE_ASSERTS + int old_offset = offset; +#endif + l.unlock(); int ret = 0; int next_locked_block = 0; - for (int i = ph->offset / block_size; i < blocks_in_piece; ++i) + for (int i = offset / block_size; i < blocks_in_piece; ++i) { file::iovec_t iov; - iov.iov_len = (std::min)(block_size, piece_size - ph->offset); + iov.iov_len = (std::min)(block_size, piece_size - offset); if (next_locked_block < num_locked_blocks && locked_blocks[next_locked_block] == i) { ++next_locked_block; TORRENT_PIECE_ASSERT(pe->blocks[i].buf, pe); - TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); - ph->offset += int(iov.iov_len); + TORRENT_PIECE_ASSERT(offset == i * block_size, pe); + offset += int(iov.iov_len); ph->h.update({pe->blocks[i].buf, iov.iov_len}); } else @@ -2363,9 +2380,9 @@ namespace libtorrent time_point start_time = clock_type::now(); - TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); + TORRENT_PIECE_ASSERT(offset == i * block_size, pe); ret = j->storage->get_storage_impl()->readv(iov, j->piece - , ph->offset, file_flags, j->error); + , offset, file_flags, j->error); if (ret < 0) { @@ -2398,8 +2415,8 @@ namespace libtorrent m_stats_counters.inc_stats_counter(counters::disk_job_time, read_time); } - TORRENT_PIECE_ASSERT(ph->offset == i * block_size, pe); - ph->offset += int(iov.iov_len); + TORRENT_PIECE_ASSERT(offset == i * block_size, pe); + offset += int(iov.iov_len); ph->h.update({static_cast(iov.iov_base), iov.iov_len}); l.lock(); @@ -2410,6 +2427,9 @@ namespace libtorrent l.lock(); + TORRENT_ASSERT(old_offset == ph->offset); + ph->offset = offset; + // decrement the refcounts of the blocks we just hashed for (int i = 0; i < num_locked_blocks; ++i) m_disk_cache.dec_block_refcount(pe, locked_blocks[i], block_cache::ref_hashing); diff --git a/src/fingerprint.cpp b/src/fingerprint.cpp new file mode 100644 index 000000000..d953dfb86 --- /dev/null +++ b/src/fingerprint.cpp @@ -0,0 +1,100 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/fingerprint.hpp" +#include "libtorrent/assert.hpp" + +namespace libtorrent { + namespace { + + char version_to_char(int const v) + { + if (v >= 0 && v < 10) return char('0' + v); + else if (v >= 10) return char('A' + (v - 10)); + TORRENT_ASSERT_FAIL(); + return '0'; + } + + } // anonymous namespace + + std::string generate_fingerprint(std::string name, int const major + , int const minor + , int const revision + , int const tag) + { + TORRENT_ASSERT_PRECOND(major >= 0); + TORRENT_ASSERT_PRECOND(minor >= 0); + TORRENT_ASSERT_PRECOND(revision >= 0); + TORRENT_ASSERT_PRECOND(tag >= 0); + TORRENT_ASSERT_PRECOND(name.size() == 2); + if (name.size() < 2) name = "--"; + + std::string ret; + ret.resize(8); + ret[0] = '-'; + ret[1] = name[0]; + ret[2] = name[1]; + ret[3] = version_to_char(major); + ret[4] = version_to_char(minor); + ret[5] = version_to_char(revision); + ret[6] = version_to_char(tag); + ret[7] = '-'; + return ret; + } + + fingerprint::fingerprint(const char* id_string, int major, int minor + , int revision, int tag) + : major_version(major) + , minor_version(minor) + , revision_version(revision) + , tag_version(tag) + { + TORRENT_ASSERT(id_string); + TORRENT_ASSERT(major >= 0); + TORRENT_ASSERT(minor >= 0); + TORRENT_ASSERT(revision >= 0); + TORRENT_ASSERT(tag >= 0); + TORRENT_ASSERT(std::strlen(id_string) == 2); + name[0] = id_string[0]; + name[1] = id_string[1]; + } + +#ifndef TORRENT_NO_DEPRECATE + std::string fingerprint::to_string() const + { + return generate_fingerprint(name, major_version, minor_version + , revision_version, tag_version); + } +#endif // TORRENT_NO_DEPRECATE + +} + diff --git a/src/identify_client.cpp b/src/identify_client.cpp index 78e29a2dc..33199b647 100644 --- a/src/identify_client.cpp +++ b/src/identify_client.cpp @@ -35,9 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "libtorrent/aux_/disable_warnings_push.hpp" - #include - #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/identify_client.hpp" @@ -141,7 +139,9 @@ namespace // must be ordered alphabetically map_entry name_map[] = { - {"A", "ABC"} + {"7T", "aTorrent for android"} + , {"A", "ABC"} + , {"AB", "AnyEvent BitTorrent"} , {"AG", "Ares"} , {"AR", "Arctic Torrent"} , {"AT", "Artemis"} @@ -343,6 +343,8 @@ namespace namespace libtorrent { +#ifndef TORRENT_NO_DEPRECATE + boost::optional client_fingerprint(peer_id const& p) { // look for azureus style id @@ -360,6 +362,8 @@ namespace libtorrent return f; } +#endif + std::string identify_client(peer_id const& p) { char const* PID = p.data(); diff --git a/src/storage.cpp b/src/storage.cpp index bf9342c0e..64f2fc895 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -502,9 +502,9 @@ namespace libtorrent // don't do full file allocations on network drives #if TORRENT_USE_WSTRING std::wstring file_name = convert_to_wstring(m_save_path); - int drive_type = GetDriveTypeW(file_name.c_str()); + int const drive_type = GetDriveTypeW(file_name.c_str()); #else - int drive_type = GetDriveTypeA(m_save_path.c_str()); + int const drive_type = GetDriveTypeA(m_save_path.c_str()); #endif if (drive_type == DRIVE_REMOTE) @@ -1246,7 +1246,8 @@ namespace libtorrent if (m_file_created[file] == false) { error_code e; - h->set_size(files().file_size(file), e); + boost::int64_t const size = files().file_size(file); + h->set_size(size, e); m_file_created.set_bit(file); if (e) { @@ -1255,6 +1256,7 @@ namespace libtorrent ec.operation = storage_error::fallocate; return h; } + m_stat_cache.set_dirty(file); } } return h; diff --git a/test/test_primitives.cpp b/test/test_primitives.cpp index 9abb50401..1d6bb4b33 100644 --- a/test/test_primitives.cpp +++ b/test/test_primitives.cpp @@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/socket_io.hpp" // for print_endpoint #include "libtorrent/announce_entry.hpp" #include "libtorrent/hex.hpp" // from_hex +#include "libtorrent/fingerprint.hpp" #include "test.hpp" #include "setup_transfer.hpp" @@ -141,6 +142,13 @@ TORRENT_TEST(primitives) // test endpoint_to_bytes TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("10.11.12.13"), 8080)), "\x0a\x0b\x0c\x0d\x1f\x90"); TEST_EQUAL(endpoint_to_bytes(udp::endpoint(address_v4::from_string("16.5.127.1"), 12345)), "\x10\x05\x7f\x01\x30\x39"); + + // test gen_fingerprint + TEST_EQUAL(generate_fingerprint("AB", 1, 2, 3, 4), "-AB1234-"); + TEST_EQUAL(generate_fingerprint("AB", 1, 2), "-AB1200-"); + TEST_EQUAL(generate_fingerprint("..", 1, 10), "-..1A00-"); + TEST_EQUAL(generate_fingerprint("CZ", 1, 15), "-CZ1F00-"); + TEST_EQUAL(generate_fingerprint("CZ", 1, 15, 16, 17), "-CZ1FGH-"); } TORRENT_TEST(printf_int64) diff --git a/test/test_resume.cpp b/test/test_resume.cpp index 4bb26234e..6414b7245 100644 --- a/test/test_resume.cpp +++ b/test/test_resume.cpp @@ -214,7 +214,9 @@ void test_piece_priorities(bool test_deprecated = false) alert const* a = wait_for_alert(ses, save_resume_data_alert::alert_type); TEST_CHECK(a); - if (save_resume_data_alert const* ra = alert_cast(a)) + save_resume_data_alert const* ra = alert_cast(a); + TEST_CHECK(ra); + if (ra) { std::printf("%s\n", ra->resume_data->to_string().c_str()); entry::string_type prios = (*ra->resume_data)["piece_priority"].string();