diff --git a/.cirrus.yml b/.cirrus.yml index 2f00a72b3c6..ed19e81691e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,5 +1,5 @@ freebsd_instance: - image_family: freebsd-12-1 + image_family: freebsd-14-0 task: install_script: | @@ -28,7 +28,7 @@ build_and_store_wheels: &BUILD_AND_STORE_WHEELS delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel} install_cibuildwheel_script: - - $PYTHON -m pip install cibuildwheel==2.12.0 + - $PYTHON -m pip install cibuildwheel==2.16.3 run_cibuildwheel_script: - cibuildwheel @@ -40,12 +40,12 @@ build_macos_arm64_task: name: Build macOS arm64 wheels. macos_instance: - image: ghcr.io/cirruslabs/macos-monterey-xcode + image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest env: CIRRUS_CLONE_SUBMODULES: true CIBW_SKIP: pp* cp38-* # cp38-* has problem with x86_64 / arm64 confusion - CIBW_BUILD: cp39-* cp310-* cp311-* + CIBW_BUILD: cp39-* cp310-* cp311-* cp312-* CIBW_ARCH: arm64 PATH: /opt/homebrew/bin:$PATH PYTHON: python3.9 @@ -64,19 +64,19 @@ test_macos_arm64_task: name: Test macOS arm64. macos_instance: - image: ghcr.io/cirruslabs/macos-monterey-xcode + image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest env: CIRRUS_CLONE_SUBMODULES: true PATH: /opt/homebrew/bin:$PATH PYTHON: python3.9 OPENSSL_OPTS: > - openssl-lib=/opt/homebrew/opt/openssl@1.1/lib - openssl-include=/opt/homebrew/opt/openssl@1.1/include + openssl-lib=/opt/homebrew/opt/openssl@3.0/lib + openssl-include=/opt/homebrew/opt/openssl@3.0/include install_pre_requirements_script: - brew install python@3.9 - - brew install boost-build boost openssl@1.1 + - brew install boost-build boost openssl@3.0 - echo "using darwin ;" >>~/user-config.jam debug_script: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c998e41a12a..1aa07219b49 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -181,7 +181,7 @@ jobs: - name: install boost run: | sudo apt install libboost-tools-dev libboost-dev - echo "using clang_tidy : : clang-tidy \"-checks=-clang-analyzer-core.*,-clang-analyzer-unix.*\" : -I/usr/local/clang-7.0.0/include/c++/v1 -stdlib=libc++ -stdlib=libc++ ;" >> ~/user-config.jam; + echo "using clang_tidy : : clang-tidy \"-checks=-clang-analyzer-core.*,-clang-analyzer-unix.*\" : -I/usr/local/clang-7.0.0/include/c++/v1 -stdlib=libc++ -Wno-unknown-warning-option -stdlib=libc++ ;" >> ~/user-config.jam; - name: analyze run: | diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 597a3ccd707..00000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,20 +0,0 @@ -path_classifiers: - test: - - "test/*.py" - - exclude: "test/*.cpp" - - exclude: "test/*.hpp" - -extraction: - cpp: - prepare: - packages: - - libboost-all-dev - - libssl-dev - - ninja-build - after_prepare: | - wget -q "https://github.com/Kitware/CMake/releases/download/v3.21.2/cmake-3.21.2-linux-x86_64.tar.gz" - tar xzf cmake-3.21.2-linux-x86_64.tar.gz - index: - build_command: | - ./cmake-3.21.2-linux-x86_64/bin/cmake -Dbuild_examples=ON -Dbuild_tests=ON -Dbuild_tools=ON -GNinja . - ./cmake-3.21.2-linux-x86_64/bin/cmake --build . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c76262e0b0c..480a621335a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ default_language_version: python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace #- id: end-of-file-fixer @@ -37,7 +37,7 @@ repos: - id: rst-directive-colons - id: rst-inline-touching-normal - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + rev: 5.13.2 hooks: - id: isort exclude: | @@ -62,12 +62,12 @@ repos: tools/update_copyright.py )$ - repo: https://github.com/PyCQA/autoflake - rev: v2.1.1 + rev: v2.2.1 hooks: - id: autoflake args: [--in-place, --remove-unused-variables, --remove-all-unused-imports, --remove-duplicate-keys] - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 24.2.0 hooks: - id: black # Avoiding PR bloat @@ -107,7 +107,7 @@ repos: name: black (pyi) types: [pyi] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.3.0 + rev: v1.8.0 hooks: - id: mypy # Avoiding PR bloat @@ -140,7 +140,7 @@ repos: tools/update_copyright.py )$ - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + rev: 7.0.0 hooks: - id: flake8 exclude: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b014b2a5e5..dc4827ff2fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -589,6 +589,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES Clang) -Wno-exit-time-destructors -Wno-weak-vtables -Wno-return-std-move-in-c++11 + -Wno-unsafe-buffer-usage -Wno-unknown-warning-option ) elseif(CMAKE_CXX_COMPILER_ID MATCHES GNU) diff --git a/ChangeLog b/ChangeLog index 947f5f8295b..70b3ee76a82 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ * libtorrent now requires C++17 to build * added support for WebTorrent + * allow on_unknown_torrent method in the absence of active torrents (new plugin feature added) * fix missing python converter for dht::announce_flags_t 2.0.9 released diff --git a/Jamfile b/Jamfile index 49126369fc5..fa7999817fc 100644 --- a/Jamfile +++ b/Jamfile @@ -304,6 +304,10 @@ rule warnings ( properties * ) # libtorrent uses alloca() carefully result += -Wno-alloca ; + +# these warnings should all be addressed. Either by transitioning to span and +# array, or by suppressing the warning for specific code + result += -Wno-unsafe-buffer-usage ; } if gcc in $(properties) diff --git a/Makefile b/Makefile index 1fc5ba8b5b8..a8fb40f10d6 100644 --- a/Makefile +++ b/Makefile @@ -1083,6 +1083,7 @@ TEST_TORRENTS = \ url_seed_multi_space_nolist.torrent \ whitespace_url.torrent \ v2.torrent \ + v2_empty_file.torrent \ v2_multipiece_file.torrent \ v2_only.torrent \ v2_invalid_filename.torrent \ diff --git a/include/libtorrent/aux_/aligned_storage.hpp b/include/libtorrent/aux_/aligned_storage.hpp index 2b542b0ced5..76607a50ef2 100644 --- a/include/libtorrent/aux_/aligned_storage.hpp +++ b/include/libtorrent/aux_/aligned_storage.hpp @@ -15,9 +15,10 @@ see LICENSE file. namespace libtorrent { namespace aux { -#if defined __GNUC__ && __GNUC__ < 5 && !defined(_LIBCPP_VERSION) +#if __cplusplus >= 202302L || defined __GNUC__ && __GNUC__ < 5 && !defined(_LIBCPP_VERSION) // this is for backwards compatibility with not-quite C++11 compilers +// and for C++23 which deprecated std::aligned_storage template struct aligned_storage { diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index d41ad3119e8..0153184c96f 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -6,6 +6,7 @@ Copyright (c) 2015-2022, Alden Torres Copyright (c) 2015, Thomas Copyright (c) 2016-2017, Pavel Pimenov Copyright (c) 2020, Paul-Louis Ageneau +Copyright (c) 2023, Joris Carrier All rights reserved. You may use, distribute and modify this code under the terms of the BSD license, @@ -323,7 +324,8 @@ namespace aux { plugins_all_idx = 0, // to store all plugins plugins_optimistic_unchoke_idx = 1, // optimistic_unchoke_feature plugins_tick_idx = 2, // tick_feature - plugins_dht_request_idx = 3 // dht_request_feature + plugins_dht_request_idx = 3, // dht_request_feature + plugins_unknown_torrent_idx = 4 // unknown_torrent_feature }; template @@ -1307,7 +1309,7 @@ namespace aux { #ifndef TORRENT_DISABLE_EXTENSIONS // this is a list to allow extensions to potentially remove themselves. - std::array>, 4> m_ses_extensions; + std::array>, 5> m_ses_extensions; #endif #if TORRENT_ABI_VERSION == 1 diff --git a/include/libtorrent/extensions.hpp b/include/libtorrent/extensions.hpp index 91d7c900c79..337913d584f 100644 --- a/include/libtorrent/extensions.hpp +++ b/include/libtorrent/extensions.hpp @@ -188,6 +188,10 @@ TORRENT_VERSION_NAMESPACE_3 // called static inline constexpr feature_flags_t alert_feature = 4_bit; + // include this bit if your plugin needs to have on_unknown_torrent() + // called even if there is no active torrent in the session + static inline constexpr feature_flags_t unknown_torrent_feature = 5_bit; + // This function is expected to return a bitmask indicating which features // this plugin implements. Some callbacks on this object may not be called // unless the corresponding feature flag is returned here. Note that diff --git a/include/libtorrent/flags.hpp b/include/libtorrent/flags.hpp index 0ff19aa918a..3ba35c93f4b 100644 --- a/include/libtorrent/flags.hpp +++ b/include/libtorrent/flags.hpp @@ -23,7 +23,7 @@ struct bit_t int m_bit_idx; }; -constexpr bit_t operator "" _bit(unsigned long long int b) { return bit_t{static_cast(b)}; } +constexpr bit_t operator ""_bit(unsigned long long int b) { return bit_t{static_cast(b)}; } namespace flags { diff --git a/include/libtorrent/i2p_stream.hpp b/include/libtorrent/i2p_stream.hpp index 1839a839022..22bb0b14260 100644 --- a/include/libtorrent/i2p_stream.hpp +++ b/include/libtorrent/i2p_stream.hpp @@ -423,7 +423,8 @@ struct i2p_stream : aux::proxy_base m_state = read_session_create_response; char cmd[400]; int size = std::snprintf(cmd, sizeof(cmd), - "SESSION CREATE STYLE=STREAM ID=%s DESTINATION=TRANSIENT SIGNATURE_TYPE=7 " + "SESSION CREATE STYLE=STREAM ID=%s " + "DESTINATION=TRANSIENT SIGNATURE_TYPE=7 i2cp.leaseSetEncType=4,0 " "inbound.quantity=%d outbound.quantity=%d inbound.length=%d outbound.length=%d\n", m_id, m_session_options.m_inbound_quantity, m_session_options.m_outbound_quantity, m_session_options.m_inbound_length, m_session_options.m_outbound_length); diff --git a/src/ip_notifier.cpp b/src/ip_notifier.cpp index 036fa480d22..a70b5dfa762 100644 --- a/src/ip_notifier.cpp +++ b/src/ip_notifier.cpp @@ -101,8 +101,8 @@ struct ip_change_notifier_impl final : ip_change_notifier void async_wait(std::function cb) override { - post(m_ios, [cb]() - { cb(make_error_code(boost::system::errc::not_supported)); }); + post(m_ios, [cb1=std::move(cb)]() + { cb1(make_error_code(boost::system::errc::not_supported)); }); } void cancel() override {} @@ -130,10 +130,10 @@ struct ip_change_notifier_impl final : ip_change_notifier void async_wait(std::function cb) override { m_socket.async_receive(boost::asio::buffer(m_buf) - , [cb=std::move(cb), this] (error_code const& ec, std::size_t const bytes_transferred) + , [cb1=std::move(cb), this] (error_code const& ec, std::size_t const bytes_transferred) { - if (ec) cb(ec); - else this->on_notify(int(bytes_transferred), std::move(cb)); + if (ec) cb1(ec); + else this->on_notify(int(bytes_transferred), std::move(cb1)); }); } @@ -193,10 +193,10 @@ struct ip_change_notifier_impl final : ip_change_notifier if (!pertinent) { m_socket.async_receive(boost::asio::buffer(m_buf) - , [cb=std::move(cb), this] (error_code const& ec, std::size_t const bytes_transferred) + , [cb1=std::move(cb), this] (error_code const& ec, std::size_t const bytes_transferred) { - if (ec) cb(ec); - else this->on_notify(int(bytes_transferred), std::move(cb)); + if (ec) cb1(ec); + else this->on_notify(int(bytes_transferred), std::move(cb1)); }); } else diff --git a/src/posix_storage.cpp b/src/posix_storage.cpp index 4074b2ba73a..450b579b9ae 100644 --- a/src/posix_storage.cpp +++ b/src/posix_storage.cpp @@ -36,6 +36,7 @@ namespace aux { posix_storage::posix_storage(storage_params const& p) : m_files(p.files) , m_save_path(p.path) + , m_file_priority(p.priorities) , m_part_file_name("." + to_hex(p.info_hash) + ".parts") { if (p.mapped_files) m_mapped_files = std::make_unique(*p.mapped_files); diff --git a/src/read_resume_data.cpp b/src/read_resume_data.cpp index 1751589d9bb..f0d34ef1a05 100644 --- a/src/read_resume_data.cpp +++ b/src/read_resume_data.cpp @@ -124,6 +124,10 @@ namespace { } ret.ti = std::move(ti); } + else + { + ec = errors::mismatching_info_hash; + } } #if TORRENT_ABI_VERSION < 3 diff --git a/src/session_impl.cpp b/src/session_impl.cpp index f26054d86c8..0f8fb37b21d 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -19,6 +19,7 @@ Copyright (c) 2020, Paul-Louis Ageneau Copyright (c) 2020, Rosen Penev Copyright (c) 2022, Vladimir Golovnev (glassez) Copyright (c) 2022, thrnz +Copyright (c) 2023, Joris Carrier All rights reserved. You may use, distribute and modify this code under the terms of the BSD license, @@ -952,6 +953,8 @@ bool ssl_server_name_callback(ssl::stream_handle_type stream_handle, std::string m_ses_extensions[plugins_tick_idx].push_back(ext); if (features & plugin::dht_request_feature) m_ses_extensions[plugins_dht_request_idx].push_back(ext); + if (features & plugin::unknown_torrent_feature) + m_ses_extensions[plugins_unknown_torrent_idx].push_back(ext); if (features & plugin::alert_feature) m_alerts.add_extension(ext); session_handle h(shared_from_this()); @@ -3049,9 +3052,15 @@ namespace { return; } + bool want_on_unknown_torrent = false; +#ifndef TORRENT_DISABLE_EXTENSIONS + want_on_unknown_torrent = !m_ses_extensions[plugins_unknown_torrent_idx].empty(); +#endif + // check if we have any active torrents + // or if there is an extension that wants on_unknown_torrent // if we don't reject the connection - if (m_torrents.empty()) + if (m_torrents.empty() && !want_on_unknown_torrent) { #ifndef TORRENT_DISABLE_LOGGING session_log("<== INCOMING CONNECTION [ rejected, there are no torrents ]"); @@ -3103,9 +3112,10 @@ namespace { // if we don't have any active torrents, there's no // point in accepting this connection. If, however, // the setting to start up queued torrents when they - // get an incoming connection is enabled, we cannot + // get an incoming connection is enabled or if there is + // an extension that wants on_unknown_torrent, we cannot // perform this check. - if (!m_settings.get_bool(settings_pack::incoming_starts_queued_torrents)) + if (!m_settings.get_bool(settings_pack::incoming_starts_queued_torrents) && !want_on_unknown_torrent) { bool has_active_torrent = std::any_of(m_torrents.begin(), m_torrents.end() , [](std::shared_ptr const& i) @@ -5662,8 +5672,11 @@ namespace { listen_socket->external_address.cast_vote(external_ip, source_router, address()); } - if (proto == portmap_protocol::tcp) listen_socket->tcp_port_mapping[transport].port = port; - else if (proto == portmap_protocol::udp) listen_socket->udp_port_mapping[transport].port = port; + // need to check whether this mapping is for one of session ports (it could also be a user mapping) + if ((proto == portmap_protocol::tcp) && (listen_socket->tcp_port_mapping[transport].mapping == mapping)) + listen_socket->tcp_port_mapping[transport].port = port; + else if ((proto == portmap_protocol::udp) && (listen_socket->udp_port_mapping[transport].mapping == mapping)) + listen_socket->udp_port_mapping[transport].port = port; if (!ec && m_alerts.should_post()) { diff --git a/src/ssl.cpp b/src/ssl.cpp index b42260e2143..7c5e5855ea9 100644 --- a/src/ssl.cpp +++ b/src/ssl.cpp @@ -77,6 +77,8 @@ void set_server_name_callback(context_handle_type c, server_name_callback_type c #if defined __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wcast-function-type-strict" +#pragma clang diagnostic ignored "-Wunknown-warning-option" #endif SSL_CTX_set_tlsext_servername_callback(c, cb); SSL_CTX_set_tlsext_servername_arg(c, arg); diff --git a/src/torrent.cpp b/src/torrent.cpp index b0273392b31..5367b2ec448 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -2450,9 +2450,9 @@ bool is_downloading_state(int const st) span v2_span(hashes); m_ses.disk_thread().async_hash(m_storage, m_checking_piece, v2_span, flags - , [self = shared_from_this(), hashes = std::move(hashes)] + , [self = shared_from_this(), hashes1 = std::move(hashes)] (piece_index_t p, sha1_hash const& h, storage_error const& error) mutable - { self->on_piece_hashed(std::move(hashes), p, h, error); }); + { self->on_piece_hashed(std::move(hashes1), p, h, error); }); ++m_checking_piece; if (m_checking_piece >= m_torrent_file->end_piece()) break; } @@ -11397,9 +11397,9 @@ namespace { span v2_span(hashes); m_ses.disk_thread().async_hash(m_storage, piece, v2_span, flags - , [self = shared_from_this(), hashes = std::move(hashes)] + , [self = shared_from_this(), hashes1 = std::move(hashes)] (piece_index_t p, sha1_hash const& h, storage_error const& error) mutable - { self->on_piece_verified(std::move(hashes), p, h, error); }); + { self->on_piece_verified(std::move(hashes1), p, h, error); }); m_picker->started_hash_job(piece); m_ses.deferred_submit_jobs(); } diff --git a/src/torrent_peer.cpp b/src/torrent_peer.cpp index 75789238d7f..2261c6697d7 100644 --- a/src/torrent_peer.cpp +++ b/src/torrent_peer.cpp @@ -63,11 +63,13 @@ namespace libtorrent::aux { { if (e1.port() > e2.port()) swap(e1, e2); - std::uint32_t p; - auto* ptr = reinterpret_cast(&p); + std::array buf; + auto ptr = buf.data(); aux::write_uint16(e1.port(), ptr); aux::write_uint16(e2.port(), ptr); - ret = aux::crc32c_32(p); + std::uint32_t p; + std::memcpy(&p, buf.data(), 4); + ret = crc32c_32(p); } else if (aux::is_v6(e1)) { diff --git a/test/test_copy_file.cpp b/test/test_copy_file.cpp index 2fd34485251..789fce4eb16 100644 --- a/test/test_copy_file.cpp +++ b/test/test_copy_file.cpp @@ -98,7 +98,7 @@ bool fs_supports_sparse_files() static fsword_t const ufs = 0x00011954; static const std::set sparse_filesystems{ EXT4_SUPER_MAGIC, EXT3_SUPER_MAGIC, XFS_SUPER_MAGIC, fsword_t(BTRFS_SUPER_MAGIC) - , ufs, REISERFS_SUPER_MAGIC + , ufs, REISERFS_SUPER_MAGIC, TMPFS_MAGIC }; printf("filesystem: %ld\n", long(st.f_type)); return sparse_filesystems.count(st.f_type); diff --git a/test/test_http_connection.cpp b/test/test_http_connection.cpp index b5ee57423d7..d39f6cb00c1 100644 --- a/test/test_http_connection.cpp +++ b/test/test_http_connection.cpp @@ -217,7 +217,7 @@ TORRENT_TEST(socks5_pw_proxy_ssl) { run_suite("https", settings_pack::socks5_pw) #endif // USE_SSL TORRENT_TEST(http_proxy) { run_suite("http", settings_pack::http); } -TORRENT_TEST(http__pwproxy) { run_suite("http", settings_pack::http_pw); } +TORRENT_TEST(http_pwproxy) { run_suite("http", settings_pack::http_pw); } TORRENT_TEST(socks5_proxy) { run_suite("http", settings_pack::socks5); } TORRENT_TEST(socks5_pw_proxy) { run_suite("http", settings_pack::socks5_pw); } diff --git a/test/test_read_resume.cpp b/test/test_read_resume.cpp index 0106ebad9ce..8b9c3a9b465 100644 --- a/test/test_read_resume.cpp +++ b/test/test_read_resume.cpp @@ -134,23 +134,6 @@ TORRENT_TEST(read_resume_missing_file_format) TEST_EQUAL(ec, error_code(errors::invalid_file_tag)); } -TORRENT_TEST(read_resume_mismatching_torrent) -{ - entry rd; - - rd["file-format"] = "libtorrent resume file"; - rd["file-version"] = 1; - rd["info-hash"] = "abcdefghijklmnopqrst"; - entry& info = rd["info"]; - info["piece length"] = 16384 * 16; - info["name"] = "test"; - - // the info-hash field does not match the torrent in the "info" field, so it - // will be ignored - add_torrent_params atp = read_resume_data(bencode(rd)); - TEST_CHECK(!atp.ti); -} - namespace { std::shared_ptr generate_torrent() { @@ -196,6 +179,48 @@ TORRENT_TEST(read_resume_torrent) TEST_EQUAL(atp.ti->name(), ti->name()); } +TORRENT_TEST(mismatching_v1_hash) +{ + std::shared_ptr ti = generate_torrent(); + + entry rd; + rd["file-format"] = "libtorrent resume file"; + rd["file-version"] = 1; + rd["info-hash"] = "abababababababababab"; + rd["info-hash2"] = ti->info_hashes().v2; + rd["info"] = bdecode(ti->info_section()); + + std::vector resume_data; + bencode(std::back_inserter(resume_data), rd); + + // the info-hash field does not match the torrent in the "info" field, so it + // will be ignored + error_code ec; + add_torrent_params atp = read_resume_data(resume_data, ec); + TEST_CHECK(ec == errors::mismatching_info_hash); +} + +TORRENT_TEST(mismatching_v2_hash) +{ + std::shared_ptr ti = generate_torrent(); + + entry rd; + rd["file-format"] = "libtorrent resume file"; + rd["file-version"] = 1; + rd["info-hash"] = ti->info_hashes().v1; + rd["info-hash2"] = "abababababababababababababababab"; + rd["info"] = bdecode(ti->info_section()); + + std::vector resume_data; + bencode(std::back_inserter(resume_data), rd); + + // the info-hash field does not match the torrent in the "info" field, so it + // will be ignored + error_code ec; + add_torrent_params atp = read_resume_data(resume_data, ec); + TEST_CHECK(ec == errors::mismatching_info_hash); +} + namespace { void test_roundtrip(add_torrent_params input)