Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libtorrent for openSUSE:Factory checked in at 2026-07-01 16:38:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libtorrent (Old) and /work/SRC/openSUSE:Factory/.libtorrent.new.11887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libtorrent" Wed Jul 1 16:38:25 2026 rev:36 rq:1362738 version:0.16.15 Changes: -------- --- /work/SRC/openSUSE:Factory/libtorrent/libtorrent.changes 2026-06-15 19:45:44.313183796 +0200 +++ /work/SRC/openSUSE:Factory/.libtorrent.new.11887/libtorrent.changes 2026-07-01 16:39:06.254211550 +0200 @@ -1,0 +2,10 @@ +Tue Jun 30 21:03:27 UTC 2026 - Jan Engelhardt <[email protected]> + +- Update to release 0.16.15 + * Added socket category option strings + * Fix use-after-free in UdpRouter::disconnect_failure_unsafe + * Validate HTTP proxy url when it is set + * Reset HttpGet objects to ensure there is no unexpected reuse + + +------------------------------------------------------------------- Old: ---- libtorrent-0.16.14.tar.gz New: ---- libtorrent-0.16.15.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libtorrent.spec ++++++ --- /var/tmp/diff_new_pack.xuBkIm/_old 2026-07-01 16:39:07.346249559 +0200 +++ /var/tmp/diff_new_pack.xuBkIm/_new 2026-07-01 16:39:07.350249699 +0200 @@ -16,9 +16,9 @@ # -%define lname libtorrent44 +%define lname libtorrent45 Name: libtorrent -Version: 0.16.14 +Version: 0.16.15 Release: 0 Summary: A BitTorrent library written in C++ License: SUSE-GPL-2.0+-with-openssl-exception ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.xuBkIm/_old 2026-07-01 16:39:07.402251509 +0200 +++ /var/tmp/diff_new_pack.xuBkIm/_new 2026-07-01 16:39:07.406251648 +0200 @@ -1,5 +1,5 @@ -mtime: 1781442878 -commit: aa42603459792425924719a1bae3dc5d2f9ad6b05a33dbd1f2efa7132083f392 +mtime: 1782853557 +commit: dd4ab70bb4ee322cc3b20b7b4ea7b2a4f9fc4ae805688ed34634a14a59d74630 url: https://src.opensuse.org/jengelh/libtorrent revision: master ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-06-30 23:05:57.000000000 +0200 @@ -0,0 +1 @@ +.osc ++++++ libtorrent-0.16.14.tar.gz -> libtorrent-0.16.15.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/README.md new/libtorrent-0.16.15/README.md --- old/libtorrent-0.16.14/README.md 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/README.md 2026-06-22 11:01:48.000000000 +0200 @@ -33,44 +33,20 @@ Help keep rTorrent development going by donating to its creator. -BUILD DEPENDENCIES --------- -Ubuntu, for example -``` -sudo apt install make curl-libcurlpp-dev autoconf -``` -BUILDING +Building -------- -Jump into the github cloned directory +Create configure files: ``` -cd libtorrent -``` - -Create config files -``` autoreconf -ivf ``` -Perform configuration -``` -./configure -``` +Configure, build and install: -Compile, slowest option. ``` +./configure make -``` - -OR Optionally compile with multiple worker threads, optimal number of threads is number of cores -1 for fastest compile time. For example : a 4 core machine should compile with only 3 cores, an 8-core machine 7, etc.) -``` -make -j7 -``` - -Install -``` make install ``` - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/configure new/libtorrent-0.16.15/configure --- old/libtorrent-0.16.14/configure 2026-06-14 10:04:23.000000000 +0200 +++ new/libtorrent-0.16.15/configure 2026-06-22 11:02:01.000000000 +0200 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.72 for libtorrent 0.16.14. +# Generated by GNU Autoconf 2.72 for libtorrent 0.16.15. # # Report bugs to <[email protected]>. # @@ -614,8 +614,8 @@ # Identity of this package. PACKAGE_NAME='libtorrent' PACKAGE_TARNAME='libtorrent' -PACKAGE_VERSION='0.16.14' -PACKAGE_STRING='libtorrent 0.16.14' +PACKAGE_VERSION='0.16.15' +PACKAGE_STRING='libtorrent 0.16.15' PACKAGE_BUGREPORT='[email protected]' PACKAGE_URL='' @@ -1408,7 +1408,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -'configure' configures libtorrent 0.16.14 to adapt to many kinds of systems. +'configure' configures libtorrent 0.16.15 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1479,7 +1479,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of libtorrent 0.16.14:";; + short | recursive ) echo "Configuration of libtorrent 0.16.15:";; esac cat <<\_ACEOF @@ -1634,7 +1634,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -libtorrent configure 0.16.14 +libtorrent configure 0.16.15 generated by GNU Autoconf 2.72 Copyright (C) 2023 Free Software Foundation, Inc. @@ -2352,7 +2352,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by libtorrent $as_me 0.16.14, which was +It was created by libtorrent $as_me 0.16.15, which was generated by GNU Autoconf 2.72. Invocation command line was $ $0$ac_configure_args_raw @@ -4045,7 +4045,7 @@ # Define the identity of the package. PACKAGE='libtorrent' - VERSION='0.16.14' + VERSION='0.16.15' printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h @@ -13691,13 +13691,13 @@ # When releasing the first 1.x.y version, we need to start with 1.1.0 (or higher) as we've already # used 0.16.x. -printf "%s\n" "#define PEER_NAME \"-lt100E-\"" >>confdefs.h +printf "%s\n" "#define PEER_NAME \"-lt100F-\"" >>confdefs.h -printf "%s\n" "#define PEER_VERSION \"lt\\x10\\x0E\"" >>confdefs.h +printf "%s\n" "#define PEER_VERSION \"lt\\x10\\x0F\"" >>confdefs.h -LIBTORRENT_CURRENT=44 +LIBTORRENT_CURRENT=45 LIBTORRENT_REVISION=0 LIBTORRENT_AGE=0 @@ -18708,6 +18708,13 @@ printf "%s\n" "#define LT_SMP_CACHE_BYTES 128" >>confdefs.h ;; + riscv32*|riscv64*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: linux fallback RISC-V 64 bytes" >&5 +printf "%s\n" "linux fallback RISC-V 64 bytes" >&6; } + +printf "%s\n" "#define LT_SMP_CACHE_BYTES 64" >>confdefs.h + + ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unrecognized CPU arch on Linux header fallback" >&5 printf "%s\n" "unrecognized CPU arch on Linux header fallback" >&6; } @@ -23304,7 +23311,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by libtorrent $as_me 0.16.14, which was +This file was extended by libtorrent $as_me 0.16.15, which was generated by GNU Autoconf 2.72. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23372,7 +23379,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\ -libtorrent config.status 0.16.14 +libtorrent config.status 0.16.15 configured by $0, generated by GNU Autoconf 2.72, with options \\"\$ac_cs_config\\" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/configure.ac new/libtorrent-0.16.15/configure.ac --- old/libtorrent-0.16.14/configure.ac 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/configure.ac 2026-06-22 11:01:47.000000000 +0200 @@ -1,4 +1,4 @@ -AC_INIT([[libtorrent]],[[0.16.14]],[[[email protected]]]) +AC_INIT([[libtorrent]],[[0.16.15]],[[[email protected]]]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIRS([scripts]) @@ -8,10 +8,10 @@ # When releasing the first 1.x.y version, we need to start with 1.1.0 (or higher) as we've already # used 0.16.x. -AC_DEFINE([[PEER_NAME]], [["-lt100E-"]], [[Identifier that is part of the default peer id.]]) -AC_DEFINE([[PEER_VERSION]], [["lt\x10\x0E"]], [[4 byte client and version identifier for DHT.]]) +AC_DEFINE([[PEER_NAME]], [["-lt100F-"]], [[Identifier that is part of the default peer id.]]) +AC_DEFINE([[PEER_VERSION]], [["lt\x10\x0F"]], [[4 byte client and version identifier for DHT.]]) -LIBTORRENT_CURRENT=44 +LIBTORRENT_CURRENT=45 LIBTORRENT_REVISION=0 LIBTORRENT_AGE=0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/scripts/common.m4 new/libtorrent-0.16.15/scripts/common.m4 --- old/libtorrent-0.16.14/scripts/common.m4 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/scripts/common.m4 2026-06-22 11:01:47.000000000 +0200 @@ -118,6 +118,10 @@ AC_MSG_RESULT([linux fallback enterprise 128 bytes]) AC_DEFINE([LT_SMP_CACHE_BYTES], 128, [Fallback 128-byte alignment for Linux enterprise hardware.]) ;; + riscv32*|riscv64*) + AC_MSG_RESULT([linux fallback RISC-V 64 bytes]) + AC_DEFINE([LT_SMP_CACHE_BYTES], 64, [Fallback 64-byte alignment for Linux RISC-V hardware.]) + ;; *) AC_MSG_RESULT([unrecognized CPU arch on Linux header fallback]) AC_MSG_FAILURE([Unrecognized CPU architecture ($host_cpu) on Linux fallback path. Aborting build.]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/data/chunk_list.cc new/libtorrent-0.16.15/src/data/chunk_list.cc --- old/libtorrent-0.16.14/src/data/chunk_list.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/data/chunk_list.cc 2026-06-22 11:01:47.000000000 +0200 @@ -247,7 +247,7 @@ } uint32_t -ChunkList::sync_chunks(sync_flags flags) { +ChunkList::sync_chunks(cache_list& cache, sync_flags flags) { LT_LOG_THIS(DEBUG, "Sync chunks: flags:%#x.", flags); Queue::iterator split; @@ -270,7 +270,7 @@ // If we got enough diskspace and have not requested safe syncing, // then sync all chunks with MS_ASYNC. if (!(flags & (sync_safe | sync_sloppy))) { - if (m_manager->safe_sync() || m_slot_free_diskspace() <= m_manager->safe_free_diskspace()) + if (m_manager->safe_sync() || m_slot_free_diskspace(cache) <= m_manager->safe_free_diskspace()) flags = flags | sync_safe; else flags = flags | sync_force; @@ -323,6 +323,12 @@ return failed; } +uint32_t +ChunkList::sync_chunks_no_cache(sync_flags flags) { + cache_list cache; + return sync_chunks(cache, flags); +} + std::pair<int, bool> ChunkList::sync_options(ChunkListNode* node, sync_flags flags) { if ((flags & sync_force)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/data/chunk_list.h new/libtorrent-0.16.15/src/data/chunk_list.h --- old/libtorrent-0.16.14/src/data/chunk_list.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/data/chunk_list.h 2026-06-22 11:01:47.000000000 +0200 @@ -19,13 +19,15 @@ class ChunkList : private std::vector<ChunkListNode> { public: - using size_type = uint32_t; - using base_type = std::vector<ChunkListNode>; - using Queue = std::vector<ChunkListNode*>; + using size_type = uint32_t; + using base_type = std::vector<ChunkListNode>; + using Queue = std::vector<ChunkListNode*>; + using cache_list = std::vector<std::pair<std::string, uint64_t>>; using slot_chunk_index = std::function<Chunk*(uint32_t, int)>; using slot_value = std::function<uint64_t()>; using slot_string = std::function<void(const std::string&)>; + using slot_free_space = std::function<uint64_t(cache_list&)>; using base_type::value_type; using base_type::reference; @@ -97,12 +99,13 @@ // non-continious regions. // Returns the number of failed syncs. - uint32_t sync_chunks(sync_flags flags); + uint32_t sync_chunks(cache_list& cache, sync_flags flags); + uint32_t sync_chunks_no_cache(sync_flags flags); slot_string& slot_storage_error() { return m_slot_storage_error; } slot_chunk_index& slot_create_chunk() { return m_slot_create_chunk; } slot_chunk_index& slot_create_hashing_chunk() { return m_slot_create_hashing_chunk; } - slot_value& slot_free_diskspace() { return m_slot_free_diskspace; } + slot_free_space& slot_free_diskspace() { return m_slot_free_diskspace; } using chunk_address_result = std::pair<iterator, Chunk::iterator>; @@ -134,7 +137,7 @@ slot_string m_slot_storage_error; slot_chunk_index m_slot_create_chunk; slot_chunk_index m_slot_create_hashing_chunk; - slot_value m_slot_free_diskspace; + slot_free_space m_slot_free_diskspace; }; inline ChunkList::sync_flags diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/download/download_main.cc new/libtorrent-0.16.15/src/download/download_main.cc --- old/libtorrent-0.16.14/src/download/download_main.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/download/download_main.cc 2026-06-22 11:01:47.000000000 +0200 @@ -71,8 +71,8 @@ m_chunkList->slot_create_hashing_chunk() = [this](uint32_t index, int prot) { return file_list()->create_hashing_chunk_index(index, prot); }; - m_chunkList->slot_free_diskspace() = [this]() { - return file_list()->free_diskspace(); + m_chunkList->slot_free_diskspace() = [this](FileList::cache_list& cache) { + return file_list()->free_diskspace(cache); }; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/download/download_wrapper.cc new/libtorrent-0.16.15/src/download/download_wrapper.cc --- old/libtorrent-0.16.14/src/download/download_wrapper.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/download/download_wrapper.cc 2026-06-22 11:01:47.000000000 +0200 @@ -106,7 +106,7 @@ // This could/should be async as we do not care that much if it // succeeds or not, any chunks not included in that last // hash_resume_save get ignored anyway. - m_main->chunk_list()->sync_chunks(ChunkList::sync_all | ChunkList::sync_force | ChunkList::sync_sloppy | ChunkList::sync_ignore_error); + m_main->chunk_list()->sync_chunks_no_cache(ChunkList::sync_all | ChunkList::sync_force | ChunkList::sync_sloppy | ChunkList::sync_ignore_error); m_main->close(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/net/curl_get.cc new/libtorrent-0.16.15/src/net/curl_get.cc --- old/libtorrent-0.16.14/src/net/curl_get.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/net/curl_get.cc 2026-06-22 11:01:48.000000000 +0200 @@ -201,7 +201,7 @@ if (!m_was_started) throw torrent::internal_error("CurlGet::prepare_start(...) called on an object that was not started."); - if (m_was_closed) { + if (m_was_closed || m_was_cleaned_up) { m_prepare_canceled = true; return false; } @@ -328,6 +328,12 @@ m_was_closed = false; m_prepare_canceled = false; m_retrying_resolve = false; + + // Use a separate cleanup flag so the state handling when retrying different resolves still works + // properly. + // + // TODO: Review if this causes any issues. + m_was_cleaned_up = true; } // Slots can be added at any time, however once trigger_* is called, it will make a copy of the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/net/curl_get.h new/libtorrent-0.16.15/src/net/curl_get.h --- old/libtorrent-0.16.14/src/net/curl_get.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/net/curl_get.h 2026-06-22 11:01:48.000000000 +0200 @@ -123,6 +123,7 @@ bool m_prepare_canceled{}; bool m_was_started{}; bool m_was_closed{}; + bool m_was_cleaned_up{}; resolve_type m_initial_resolve{RESOLVE_WHATEVER}; resolve_type m_retry_resolve{RESOLVE_NONE}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/bitfield.h new/libtorrent-0.16.15/src/torrent/bitfield.h --- old/libtorrent-0.16.14/src/torrent/bitfield.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/bitfield.h 2026-06-22 11:01:48.000000000 +0200 @@ -63,8 +63,8 @@ iterator begin() { return m_data.get(); } const_iterator begin() const { return m_data.get(); } - iterator end() { return m_data.get() + size_bytes(); } - const_iterator end() const { return m_data.get() + size_bytes(); } + iterator end() { return m_data != nullptr ? m_data.get() + size_bytes() : m_data.get(); } + const_iterator end() const { return m_data != nullptr ? m_data.get() + size_bytes() : m_data.get(); } size_type position(const_iterator itr) const { return (itr - m_data.get()) * 8; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/chunk_manager.cc new/libtorrent-0.16.15/src/torrent/chunk_manager.cc --- old/libtorrent-0.16.14/src/torrent/chunk_manager.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/chunk_manager.cc 2026-06-22 11:01:48.000000000 +0200 @@ -158,11 +158,13 @@ auto itr = base_type::begin() + m_lastFreed; + ChunkList::cache_list cache; + do { if (itr == base_type::end()) itr = base_type::begin(); - (*itr)->sync_chunks(static_cast<ChunkList::sync_flags>(flags)); + (*itr)->sync_chunks(cache, static_cast<ChunkList::sync_flags>(flags)); } while (++itr != base_type::begin() + m_lastFreed && m_memoryUsage >= target); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/data/file_list.cc new/libtorrent-0.16.15/src/torrent/data/file_list.cc --- old/libtorrent-0.16.14/src/torrent/data/file_list.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/data/file_list.cc 2026-06-22 11:01:48.000000000 +0200 @@ -147,23 +147,39 @@ // This function should really ensure that we arn't dealing files // spread over multiple mount-points. uint64_t -FileList::free_diskspace() const { +FileList::free_diskspace(cache_list& cache) const { uint64_t free_diskspace = std::numeric_limits<uint64_t>::max(); for (const auto& link : m_indirect_links) { - struct statvfs stat{}; + auto cache_itr = std::find_if(cache.begin(), cache.end(), [&link](const auto& cacheEntry) { + return cacheEntry.first == link; + }); - auto fn = link.c_str(); + if (cache_itr == cache.end()) { + struct statvfs stat{}; - if (statvfs(fn, &stat) == -1) - continue; + auto fn = link.c_str(); - free_diskspace = std::min<uint64_t>(free_diskspace, static_cast<int64_t>(stat.f_frsize) * stat.f_bavail); + if (statvfs(fn, &stat) == -1) + continue; + + cache.push_back(std::make_pair(link, static_cast<uint64_t>(stat.f_frsize) * stat.f_bavail)); + cache_itr = std::prev(cache.end()); + } + + if (cache_itr->second < free_diskspace) + free_diskspace = cache_itr->second; } return free_diskspace != std::numeric_limits<uint64_t>::max() ? free_diskspace : 0; } +uint64_t +FileList::free_diskspace_no_cache() const { + cache_list cache; + return free_diskspace(cache); +} + FileList::iterator_range FileList::split(iterator position, split_type* first, split_type* last) { if (is_open()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/data/file_list.h new/libtorrent-0.16.15/src/torrent/data/file_list.h --- old/libtorrent-0.16.14/src/torrent/data/file_list.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/data/file_list.h 2026-06-22 11:01:48.000000000 +0200 @@ -32,6 +32,8 @@ using path_list = std::vector<std::string>; using split_type = std::tuple<uint64_t, Path, int>; + using cache_list = std::vector<std::pair<std::string, uint64_t>>; + // The below are using-directives that make visible functions and // typedefs in the parent std::vector, only those listed below are // accessible. If you don't understand how this works, use google, @@ -96,7 +98,8 @@ // If the files span multiple disks, the one with the least amount // of free diskspace will be returned. - uint64_t free_diskspace() const; + uint64_t free_diskspace(cache_list& cache) const; + uint64_t free_diskspace_no_cache() const; // List of directories in the torrent that might be on different // volumes as they are links, including the root directory. Used by diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/download.cc new/libtorrent-0.16.15/src/torrent/download.cc --- old/libtorrent-0.16.14/src/torrent/download.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/download.cc 2026-06-22 11:01:48.000000000 +0200 @@ -350,7 +350,7 @@ void Download::sync_chunks() { - m_ptr->main()->chunk_list()->sync_chunks(ChunkList::sync_all | ChunkList::sync_force); + m_ptr->main()->chunk_list()->sync_chunks_no_cache(ChunkList::sync_all | ChunkList::sync_force); } uint32_t diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/net/http_get.cc new/libtorrent-0.16.15/src/torrent/net/http_get.cc --- old/libtorrent-0.16.14/src/torrent/net/http_get.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/net/http_get.cc 2026-06-22 11:01:48.000000000 +0200 @@ -57,9 +57,7 @@ void HttpGet::reset(const std::string& url, std::shared_ptr<std::ostream> stream) { - if (m_curl_get == nullptr) - m_curl_get = std::make_shared<CurlGet>(); - + m_curl_get = std::make_shared<CurlGet>(); m_curl_get->reset(url, std::move(stream)); } @@ -101,6 +99,16 @@ } void +HttpGet::use_family(int family) { + if (family == AF_INET) + use_ipv4(); + else if (family == AF_INET6) + use_ipv6(); + else + throw torrent::internal_error("HttpGet::use_family() called with an invalid address family."); +} + +void HttpGet::prefer_ipv4() { m_curl_get->set_initial_resolve(CurlGet::RESOLVE_IPV4); m_curl_get->set_retry_resolve(CurlGet::RESOLVE_IPV6); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/net/http_get.h new/libtorrent-0.16.15/src/torrent/net/http_get.h --- old/libtorrent-0.16.14/src/torrent/net/http_get.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/net/http_get.h 2026-06-22 11:01:48.000000000 +0200 @@ -48,6 +48,8 @@ void use_ipv4(); void use_ipv6(); + void use_family(int family); + void prefer_ipv4(); void prefer_ipv6(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/net/http_stack.cc new/libtorrent-0.16.15/src/torrent/net/http_stack.cc --- old/libtorrent-0.16.14/src/torrent/net/http_stack.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/net/http_stack.cc 2026-06-22 11:01:48.000000000 +0200 @@ -18,7 +18,17 @@ // TODO: This should be in a net/utils file. // TODO: Require scheme to also be returned / checked? -std::tuple<std::string, uint16_t> +bool +verify_url_guess_scheme(const std::string& url) { + CURLU* curlu = curl_url(); + + bool result = curl_url_set(curlu, CURLUPART_URL, url.c_str(), CURLU_GUESS_SCHEME) == CURLUE_OK; + + curl_url_cleanup(curlu); + return result; +} + +std::pair<std::string, uint16_t> parse_uri_host_port(const std::string& uri) { char* host_ptr{}; char* port_ptr{}; @@ -136,6 +146,9 @@ void HttpStack::set_http_proxy(const std::string& s) { + if (!s.empty() && !verify_url_guess_scheme(s)) + throw torrent::input_error("Invalid HTTP proxy url: " + s); + m_stack->set_http_proxy(s); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/net/types.h new/libtorrent-0.16.15/src/torrent/net/types.h --- old/libtorrent-0.16.14/src/torrent/net/types.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/net/types.h 2026-06-22 11:01:48.000000000 +0200 @@ -70,15 +70,17 @@ c_sa_shared_ptr sa_lookup_address(const std::string& address_str, int family) LIBTORRENT_EXPORT; std::tuple<sa_unique_ptr,bool> sa_lookup_numeric(const std::string& address_str, int family) LIBTORRENT_EXPORT; -sin46_shared_pair try_lookup_numeric(const std::string& hostname, int family) LIBTORRENT_EXPORT; +sin46_shared_pair try_lookup_numeric(const std::string& hostname, int family) LIBTORRENT_EXPORT; // TODO: Rename to family_enum and add family_enum_str. -const char* family_str(int family) LIBTORRENT_EXPORT; -inline std::string family_enum_str(int family) { return family_str(family); } +const char* family_str(int family) LIBTORRENT_EXPORT; +inline std::string family_enum_str(int family) { return family_str(family); } namespace net { -std::tuple<std::string, uint16_t> parse_uri_host_port(const std::string& uri) LIBTORRENT_EXPORT; +bool verify_url_guess_scheme(const std::string& url) LIBTORRENT_EXPORT; + +std::pair<std::string, uint16_t> parse_uri_host_port(const std::string& uri) LIBTORRENT_EXPORT; } // namespace net diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/object.cc new/libtorrent-0.16.15/src/torrent/object.cc --- old/libtorrent-0.16.14/src/torrent/object.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/object.cc 2026-06-22 11:01:48.000000000 +0200 @@ -1,39 +1,3 @@ -// libTorrent - BitTorrent library -// Copyright (C) 2005-2011, Jari Sundell -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -// -// In addition, as a special exception, the copyright holders give -// permission to link the code of portions of this program with the -// OpenSSL library under certain conditions as described in each -// individual source file, and distribute linked combinations -// including the two. -// -// You must obey the GNU General Public License in all respects for -// all of the code used other than OpenSSL. If you modify file(s) -// with this exception, you may extend this exception to your version -// of the file(s), but you are not obligated to do so. If you do not -// wish to do so, delete this exception statement from your version. -// If you delete this exception statement from all source files in the -// program, then also delete it here. -// -// Contact: Jari Sundell <[email protected]> -// -// Skomakerveien 33 -// 3185 Skoppum, NORWAY - #include "config.h" #include <algorithm> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/runtime/socket_manager.cc new/libtorrent-0.16.15/src/torrent/runtime/socket_manager.cc --- old/libtorrent-0.16.14/src/torrent/runtime/socket_manager.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/runtime/socket_manager.cc 2026-06-22 11:01:48.000000000 +0200 @@ -17,6 +17,18 @@ namespace { uint32_t +calculate_min_generic(uint32_t open_max) { + if (open_max >= 16384) + return 12288; + else if (open_max >= 8096) + return 6144; + else if (open_max >= 1024) + return 512; + else + return 256; +} + +uint32_t calculate_reserved(uint32_t open_max) { if (open_max >= 16384) return 512; @@ -24,12 +36,8 @@ return 256; else if (open_max >= 1024) return 128; - else if (open_max >= 512) - return 64; - else if (open_max >= 128) - return 32; else - return 16; + return 64; } uint32_t @@ -40,33 +48,27 @@ return 64; else if (open_max >= 1024) return 32; - else if (open_max >= 512) + else return 16; - else if (open_max >= 128) - return 8; - else // Assumes we don't try less than 64. - return 4; } uint32_t calculate_internal(uint32_t open_max) { if (open_max >= 16384) - return 32; + return 64; else if (open_max >= 1024) - return 16; + return 32; else - return 8; + return 16; } uint32_t -calculate_scgi(uint32_t open_max) { +calculate_rpc(uint32_t open_max) { if (open_max >= 16384) - return 256; + return 64; else if (open_max >= 8096) - return 128; + return 48; else if (open_max >= 1024) - return 64; - else if (open_max >= 512) return 32; else return 16; @@ -74,18 +76,16 @@ uint32_t calculate_files(uint32_t open_max) { - if (open_max >= 16384) - return 512; + if (open_max >= 32768) + return 4096; + else if (open_max >= 16384) + return 2048; else if (open_max >= 8096) - return 256; + return 1024; else if (open_max >= 1024) return 128; - else if (open_max >= 512) - return 64; - else if (open_max >= 128) - return 16; else - return 4; + return 64; } } // namespace @@ -94,6 +94,9 @@ SocketManager::SocketManager() { // TODO: Set load factor a bit higher than default to account for peak usage during startup / etc. + + for (auto& alloc : m_category_max_alloc) + alloc = category_max_alloc; } SocketManager::~SocketManager() { @@ -120,6 +123,40 @@ return max_size_unsafe(category); } +uint32_t +SocketManager::category_min_allocation(category_t category) { + if (category == category_generic || static_cast<uint32_t>(category) >= category_count) + throw internal_error("SocketManager::category_min_allocation(): invalid category"); + + return m_category_min_alloc[static_cast<uint32_t>(category)]; +} + +uint32_t +SocketManager::category_max_allocation(category_t category) { + if (category == category_generic || static_cast<uint32_t>(category) >= category_count) + throw internal_error("SocketManager::category_max_allocation(): invalid category"); + + return m_category_max_alloc[static_cast<uint32_t>(category)]; +} + +void +SocketManager::set_category_min_allocation(category_t category, uint32_t min_alloc) { + if (category == category_generic || static_cast<uint32_t>(category) >= category_count) + throw internal_error("SocketManager::set_category_min_allocation(): invalid category"); + + auto guard = lock_guard(); + m_category_min_alloc[static_cast<uint32_t>(category)] = min_alloc; +} + +void +SocketManager::set_category_max_allocation(category_t category, uint32_t max_alloc) { + if (category == category_generic || static_cast<uint32_t>(category) >= category_count) + throw internal_error("SocketManager::set_category_max_allocation(): invalid category"); + + auto guard = lock_guard(); + m_category_max_alloc[static_cast<uint32_t>(category)] = max_alloc; +} + void SocketManager::set_max_size_and_adjust(uint32_t max_open) { if (max_open < 512) @@ -128,23 +165,42 @@ auto guard = lock_guard(); m_max_size = max_open; + adjust_allocation_unsafe(); +} + +void +SocketManager::adjust_allocation() { + auto guard = lock_guard(); + + adjust_allocation_unsafe(); +} + +void +SocketManager::adjust_allocation_unsafe() { + auto max_open = m_max_size.load(); uint32_t total_allocated{}; auto set_category = [this, &total_allocated](category_t category, uint32_t allocation) { - total_allocated += allocation; - max_size_unsafe(category) = allocation; - }; + allocation = std::max(allocation, m_category_min_alloc[static_cast<uint32_t>(category)].load()); + allocation = std::min(allocation, m_category_max_alloc[static_cast<uint32_t>(category)].load()); + + total_allocated += allocation; + max_size_unsafe(category) = allocation; + }; set_category(category_internal, calculate_internal(max_open)); set_category(category_http, calculate_http(max_open)); - set_category(category_scgi, calculate_scgi(max_open)); + set_category(category_rpc, calculate_rpc(max_open)); set_category(category_files, calculate_files(max_open)); total_allocated += calculate_reserved(max_open); - if (total_allocated + 8 > max_open) - throw internal_error("set_max_size_and_adjust: total allocated plus reserved exceeds max_open"); + auto min_generic = calculate_min_generic(max_open); + + if (total_allocated + min_generic + 8 > max_open) + throw internal_error("set_max_size_and_adjust: total + min_generic + 8 reserve exceeds max_open : " + + std::to_string(total_allocated) + " + " + std::to_string(min_generic) + " + 8 > " + std::to_string(max_open)); max_size_unsafe(category_generic) = max_open - total_allocated; @@ -152,12 +208,6 @@ } void -SocketManager::set_category_max_size(category_t category, uint32_t max_size) { - auto guard = lock_guard(); - max_size_unsafe(category) = max_size; -} - -void SocketManager::subscribe_to_changes(void* target, const std::function<void()>& callback) { auto guard = lock_guard(); m_change_subscribers.push_back(std::make_pair(target, callback)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/runtime/socket_manager.h new/libtorrent-0.16.15/src/torrent/runtime/socket_manager.h --- old/libtorrent-0.16.14/src/torrent/runtime/socket_manager.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/runtime/socket_manager.h 2026-06-22 11:01:48.000000000 +0200 @@ -30,7 +30,7 @@ category_generic, // peer connections, uncategorized category_http, // HTTP/curl category_internal, // DHT, UDP tracker, thread interrupt, BT listen - category_scgi, // SCGI/RPC + category_rpc, // SCGI/RPC category_files // open files (mmap, etc.) }; @@ -51,7 +51,8 @@ class LIBTORRENT_EXPORT SocketManager { public: - static constexpr uint32_t category_count = 5; + static constexpr uint32_t category_count = 5; + static constexpr uint32_t category_max_alloc = 1000000; static constexpr int flag_inactive = (1 << 0); using category_t = socket_manager_category_t; @@ -65,8 +66,15 @@ uint32_t category_managed_size(category_t category); uint32_t category_max_size(category_t category); + uint32_t category_min_allocation(category_t category); + uint32_t category_max_allocation(category_t category); + + void set_category_min_allocation(category_t category, uint32_t min_alloc); + void set_category_max_allocation(category_t category, uint32_t max_alloc); + void set_max_size_and_adjust(uint32_t max_open); - void set_category_max_size(category_t category, uint32_t max_size); + + void adjust_allocation(); void add_unmanaged_socket(); void remove_unmanaged_socket(); @@ -120,6 +128,8 @@ auto& managed_size_unsafe(category_t category); auto& max_size_unsafe(category_t category); + void adjust_allocation_unsafe(); + void notify_changes_unsafe() const; void account_new_socket_unsafe(socket_map::iterator itr, category_t category); @@ -136,6 +146,9 @@ category_list m_category_managed_size{}; category_list m_category_max_size{}; + category_list m_category_min_alloc{}; + category_list m_category_max_alloc{}; + align_cacheline std::mutex m_mutex; subscriber_list m_change_subscribers; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/system/poll_epoll.cc new/libtorrent-0.16.15/src/torrent/system/poll_epoll.cc --- old/libtorrent-0.16.14/src/torrent/system/poll_epoll.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/system/poll_epoll.cc 2026-06-22 11:01:48.000000000 +0200 @@ -54,6 +54,8 @@ unsigned int m_max_events{}; unsigned int m_waiting_events{}; + unsigned int m_callback_interrupt_backoff{}; + Table m_table; std::unique_ptr<struct epoll_event[]> m_events; @@ -215,8 +217,18 @@ Poll::poll(std::chrono::microseconds timeout) { auto previous_state = m_polling_state.fetch_or(flag_polling, std::memory_order_acquire); - if (previous_state & flag_interrupted || system::Thread::self()->has_any_callbacks()) - timeout = {}; + bool callback_interrupting{}; + + if (previous_state & flag_interrupted || system::Thread::self()->has_any_callbacks()) { + auto backoff_shift = std::min(m_internal->m_callback_interrupt_backoff, 3u); + + if (backoff_shift > 0) + timeout = std::min(1000us * (1 << (backoff_shift - 1)), timeout); + else + timeout = 0us; + + callback_interrupting = true; + } int nfds = ::epoll_wait(m_internal->m_fd, m_internal->m_events.get(), @@ -228,6 +240,11 @@ if (nfds == -1) return -1; + if (nfds == 1 && callback_interrupting && m_internal->m_events[0].data.fd == m_internal->m_wake_event.file_descriptor()) + m_internal->m_callback_interrupt_backoff++; + else + m_internal->m_callback_interrupt_backoff = 0; + m_internal->m_waiting_events = nfds; return nfds; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/system/poll_kqueue.cc new/libtorrent-0.16.15/src/torrent/system/poll_kqueue.cc --- old/libtorrent-0.16.14/src/torrent/system/poll_kqueue.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/system/poll_kqueue.cc 2026-06-22 11:01:48.000000000 +0200 @@ -74,6 +74,8 @@ unsigned int m_waiting_events{}; unsigned int m_changed_events{}; + unsigned int m_callback_interrupt_backoff{}; + Table m_table; std::unique_ptr<struct kevent[]> m_events; std::unique_ptr<struct kevent[]> m_changes; @@ -210,16 +212,26 @@ int Poll::poll(std::chrono::microseconds timeout) { + auto previous_state = m_polling_state.fetch_or(flag_polling, std::memory_order_acquire); + + bool callback_interrupting{}; + + if (previous_state & flag_interrupted || system::Thread::self()->has_any_callbacks()) { + auto backoff_shift = std::min(m_internal->m_callback_interrupt_backoff, 3u); + + if (backoff_shift > 0) + timeout = std::min(1000us * (1 << (backoff_shift - 1)), timeout); + else + timeout = 0us; + + callback_interrupting = true; + } + timespec timeout_spec = { static_cast<time_t>(timeout.count() / 1000000), static_cast<long>(timeout.count() % 1000000) * 1000 }; - auto previous_state = m_polling_state.fetch_or(flag_polling, std::memory_order_acquire); - - if (previous_state & flag_interrupted || system::Thread::self()->has_any_callbacks()) - timeout_spec = timespec{0, 0}; - int nfds = ::kevent(m_internal->m_fd, m_internal->m_changes.get(), m_internal->m_changed_events, @@ -240,6 +252,11 @@ if (nfds == -1) return -1; + if (nfds == 1 && callback_interrupting && m_internal->m_events[0].filter == EVFILT_USER) + m_internal->m_callback_interrupt_backoff++; + else + m_internal->m_callback_interrupt_backoff = 0; + m_internal->m_waiting_events = nfds; return nfds; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/utils/option_strings.cc new/libtorrent-0.16.15/src/torrent/utils/option_strings.cc --- old/libtorrent-0.16.14/src/torrent/utils/option_strings.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/utils/option_strings.cc 2026-06-22 11:01:48.000000000 +0200 @@ -197,6 +197,16 @@ NULL }; +constexpr const char* option_list_socket_category[] = { + "generic", + "http", + "internal", + "rpc", + "files", + + NULL +}; + constexpr const char* option_list_tracker_event[] = { "updated", "completed", @@ -225,6 +235,7 @@ constexpr std::array option_single_lists{ OPTION_SINGLE_ENTRY(option_list_handshake_connection), OPTION_SINGLE_ENTRY(option_list_log_group), + OPTION_SINGLE_ENTRY(option_list_socket_category), OPTION_SINGLE_ENTRY(option_list_tracker_event), }; static_assert(option_single_lists.size() == OPTION_SINGLE_SIZE); @@ -252,14 +263,15 @@ } const char* -option_to_string(option_enum opt_enum, unsigned int value, const char* not_found) { +option_to_c_str(option_enum opt_enum, unsigned int value, const char* not_found) { if (opt_enum < OPTION_START_COMPACT) { auto itr = option_pair_lists[opt_enum]; do { if (itr->value == value) return itr->name; - } while ((++itr)->name != NULL); + + } while ((++itr)->name != nullptr); } else if (opt_enum < OPTION_MAX_SIZE) { if (value < option_single_lists[opt_enum - OPTION_START_COMPACT].size) @@ -270,21 +282,11 @@ } const char* -option_to_string_or_throw(option_enum opt_enum, unsigned int value, const char* not_found) { - const char* result = option_to_string(opt_enum, value, NULL); - - if (result == NULL) - throw input_error(not_found); - - return result; -} - -const char* -option_as_string(option_enum opt_enum, unsigned int value) { - const char* result = option_to_string(opt_enum, value, NULL); +option_to_c_str_or_throw(option_enum opt_enum, unsigned int value, const char* not_found) { + const char* result = option_to_c_str(opt_enum, value, nullptr); - if (result == NULL) - throw input_error("invalid option value : enum:" + std::to_string(opt_enum) + " value:" + std::to_string(value)); + if (result == nullptr) + throw input_error(std::string(not_found) + " : enum:" + std::to_string(opt_enum) + " value:" + std::to_string(value)); return result; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/torrent/utils/option_strings.h new/libtorrent-0.16.15/src/torrent/utils/option_strings.h --- old/libtorrent-0.16.14/src/torrent/utils/option_strings.h 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/torrent/utils/option_strings.h 2026-06-22 11:01:48.000000000 +0200 @@ -20,6 +20,7 @@ OPTION_HANDSHAKE_CONNECTION, OPTION_LOG_GROUP, + OPTION_SOCKET_CATEGORY, OPTION_TRACKER_EVENT, OPTION_MAX_SIZE, @@ -30,14 +31,40 @@ int option_find_string(option_enum opt_enum, const char* name) LIBTORRENT_EXPORT; inline int option_find_string_str(option_enum opt_enum, const std::string& name) { return option_find_string(opt_enum, name.c_str()); } -const char* option_to_string(option_enum opt_enum, unsigned int value, const char* not_found = "invalid") LIBTORRENT_EXPORT; -const char* option_to_string_or_throw(option_enum opt_enum, unsigned int value, const char* not_found = "Invalid option value") LIBTORRENT_EXPORT; +const char* option_to_c_str(option_enum opt_enum, unsigned int value, const char* not_found = "invalid") LIBTORRENT_EXPORT; +const char* option_to_c_str_or_throw(option_enum opt_enum, unsigned int value, const char* not_found = "Invalid option value") LIBTORRENT_EXPORT; -// TODO: Deprecated. -const char* option_as_string(option_enum opt_enum, unsigned int value) LIBTORRENT_EXPORT; +std::string option_to_str(option_enum opt_enum, unsigned int value); +std::string option_to_str(option_enum opt_enum, unsigned int value, const char* not_found); +std::string option_to_str_or_throw(option_enum opt_enum, unsigned int value); +std::string option_to_str_or_throw(option_enum opt_enum, unsigned int value, const char* not_found); torrent::Object option_list_strings(option_enum opt_enum) LIBTORRENT_EXPORT; +// +// Implementation: +// + +inline std::string +option_to_str(option_enum opt_enum, unsigned int value) { + return option_to_c_str(opt_enum, value); +} + +inline std::string +option_to_str(option_enum opt_enum, unsigned int value, const char* not_found) { + return option_to_c_str(opt_enum, value, not_found); +} + +inline std::string +option_to_str_or_throw(option_enum opt_enum, unsigned int value) { + return option_to_c_str_or_throw(opt_enum, value); +} + +inline std::string +option_to_str_or_throw(option_enum opt_enum, unsigned int value, const char* not_found) { + return option_to_c_str_or_throw(opt_enum, value, not_found); +} + } // namespace torrent #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/tracker/tracker_dht.cc new/libtorrent-0.16.15/src/tracker/tracker_dht.cc --- old/libtorrent-0.16.14/src/tracker/tracker_dht.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/tracker/tracker_dht.cc 2026-06-22 11:01:48.000000000 +0200 @@ -40,7 +40,7 @@ assert(!m_weak_tracker.expired()); LT_LOG("sending event : state:%s dht_state:%s replied:%d contacted:%d", - option_as_string(OPTION_TRACKER_EVENT, new_state), states[m_dht_state], m_replied.load(), m_contacted.load()); + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, new_state), states[m_dht_state], m_replied.load(), m_contacted.load()); close(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/tracker/tracker_http.cc new/libtorrent-0.16.15/src/tracker/tracker_http.cc --- old/libtorrent-0.16.14/src/tracker/tracker_http.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/tracker/tracker_http.cc 2026-06-22 11:01:48.000000000 +0200 @@ -34,9 +34,6 @@ m_get.reset(raw_info.url, nullptr); - m_get.add_done_slot(tracker_thread::thread(), [this] { receive_done(); }); - m_get.add_failed_slot(tracker_thread::thread(), [this](const auto& str) { receive_signal_failed(str); }); - m_delay_scrape.slot() = [this] { delayed_send_scrape(); }; auto [hostname, port] = net::parse_uri_host_port(raw_info.url); @@ -77,7 +74,7 @@ m_last_error_message = ""; if (m_current_family == AF_UNSPEC) { - LT_LOG("send event : no valid address family available : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, new_state), info().url.c_str()); + LT_LOG("send event : no valid address family available : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, new_state), info().url.c_str()); return receive_failed("No valid address family available."); } @@ -104,7 +101,7 @@ void TrackerHttp::close() { - LT_LOG("closing event : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + LT_LOG("closing event : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); this_thread::scheduler()->erase(&m_delay_scrape); m_requested_scrape = false; @@ -117,7 +114,7 @@ TrackerHttp::close_directly() { if (m_data == nullptr) { // LT_LOG("closing directly (already closed) : state:%s url:%s", - // option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + // option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); remove_events(); return; @@ -131,7 +128,7 @@ void TrackerHttp::cleanup() { - LT_LOG("cleaning up : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + LT_LOG("cleaning up : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); close_directly(); @@ -167,20 +164,18 @@ update_requesting_state(); m_get.try_wait_for_close(); + m_get.reset(request_url, m_data); + m_get.use_family(m_current_family); - if (m_current_family == AF_INET) - m_get.use_ipv4(); - else if (m_current_family == AF_INET6) - m_get.use_ipv6(); - else - throw torrent::internal_error("TrackerHttp::send_event_unsafe() cannot send event, no valid address family to use."); + m_get.add_done_slot(tracker_thread::thread(), [this] { receive_done(); }); + m_get.add_failed_slot(tracker_thread::thread(), [this](const auto& str) { receive_signal_failed(str); }); - LT_LOG("sending event : state:%s family:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state), family_str(m_current_family), info().url.c_str()); + LT_LOG("sending event : state:%s family:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state), family_str(m_current_family), info().url.c_str()); LT_LOG_DUMP(request_url.c_str(), request_url.size(), "sending event : state:%s family:%s up_adj:%" PRIu64 " completed_adj:%" PRIu64 " left_adj:%" PRIu64, - option_as_string(OPTION_TRACKER_EVENT, state), family_str(m_current_family), + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state), family_str(m_current_family), m_params.uploaded_adjusted, m_params.completed_adjusted, m_params.download_left); torrent::net_thread::http_stack()->start_get(m_get); @@ -195,14 +190,12 @@ update_requesting_state(); m_get.try_wait_for_close(); + m_get.reset(request_url, m_data); + m_get.use_family(m_current_family); - if (m_current_family == AF_INET) - m_get.use_ipv4(); - else if (m_current_family == AF_INET6) - m_get.use_ipv6(); - else - throw torrent::internal_error("TrackerHttp::send_scrape_unsafe() cannot send event, no valid address family to use."); + m_get.add_done_slot(tracker_thread::thread(), [this] { receive_done(); }); + m_get.add_failed_slot(tracker_thread::thread(), [this](const auto& str) { receive_signal_failed(str); }); LT_LOG("sending scrape : family:%s url:%s", family_str(m_current_family), info().url.c_str()); LT_LOG_DUMP(request_url.c_str(), request_url.size(), "tracker scrape", 0); @@ -384,7 +377,7 @@ if (m_data == nullptr) throw internal_error("TrackerHttp::receive_done() called on an invalid object"); - LT_LOG("received reply : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + LT_LOG("received reply : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); if (lt_log_is_valid(LOG_TRACKER_DUMP)) { std::string dump = m_data->str(); @@ -417,7 +410,19 @@ + "\""); } - // If no failures, set intervals to defaults prior to processing + if (b.has_key_string("warning message")) { + auto& msg = b.get_key("warning message").as_string(); + + if (msg.find("unregistered") != std::string::npos || + msg.find("not registered") != std::string::npos || + msg.find("torrent cannot be found") != std::string::npos) { + + LT_LOG("tracker warning treated as failure : url:%s : %s", info().url.c_str(), msg.c_str()); + return receive_failed("Tracker warning: " + msg); + } + + LT_LOG("tracker warning : url:%s : %s", info().url.c_str(), msg.c_str()); + } if (state().latest_event() == tracker::TrackerState::EVENT_SCRAPE) { m_requested_scrape = false; @@ -440,7 +445,7 @@ TrackerHttp::receive_failed(const std::string& msg) { if (m_data == nullptr) { LT_LOG("received failure with no data : state:%s url:%s : %s", - option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str(), msg.c_str()); + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str(), msg.c_str()); update_requesting_state(); m_slot_failure(msg); @@ -546,7 +551,7 @@ state().m_scrape_downloaded = std::max<int64_t>(object.get_key_value("downloaded"), 0); LT_LOG("tracker reply : state:%s url:%s : interval:%" PRId64 " min_interval:%" PRId64 " complete:%u incomplete:%u downloaded:%u", - option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str(), + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str(), (int64_t)state().normal_interval().count(), (int64_t)state().min_interval().count(), state().scrape_complete(), state().scrape_incomplete(), state().scrape_downloaded()); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/tracker/tracker_list.cc new/libtorrent-0.16.15/src/tracker/tracker_list.cc --- old/libtorrent-0.16.14/src/tracker/tracker_list.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/tracker/tracker_list.cc 2026-06-22 11:01:48.000000000 +0200 @@ -112,7 +112,7 @@ } LT_LOG("sending %s : requester:%p url:%s", - option_as_string(OPTION_TRACKER_EVENT, event), tracker.get_worker(), tracker.url().c_str()); + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, event), tracker.get_worker(), tracker.url().c_str()); tracker::TrackerParams params; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/tracker/tracker_udp.cc new/libtorrent-0.16.15/src/tracker/tracker_udp.cc --- old/libtorrent-0.16.14/src/tracker/tracker_udp.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/tracker/tracker_udp.cc 2026-06-22 11:01:48.000000000 +0200 @@ -40,7 +40,7 @@ void TrackerUdp::send_event(tracker::TrackerParams params, tracker::TrackerState::event_enum new_state) { - LT_LOG("sending event : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, new_state), info().url.c_str()); + LT_LOG("sending event : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, new_state), info().url.c_str()); close_directly(); @@ -59,7 +59,7 @@ connect_family(AF_INET6); LT_LOG("started announce : state:%s url:%s inet_tx:%u inet6_tx:%u", - option_as_string(OPTION_TRACKER_EVENT, new_state), info().url.c_str(), + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, new_state), info().url.c_str(), m_inet_state.transaction_id, m_inet6_state.transaction_id); if (m_inet_state.transaction_id == 0 && m_inet6_state.transaction_id == 0) @@ -75,7 +75,7 @@ void TrackerUdp::close() { - LT_LOG("closing event : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + LT_LOG("closing event : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); close_directly(); update_requesting_state(); @@ -95,7 +95,7 @@ void TrackerUdp::cleanup() { - LT_LOG("cleaning up : state:%s url:%s", option_as_string(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); + LT_LOG("cleaning up : state:%s url:%s", option_to_c_str_or_throw(OPTION_TRACKER_EVENT, state().latest_event()), info().url.c_str()); close_directly(); remove_events(); @@ -305,14 +305,14 @@ LT_LOG_DUMP(buffer.begin(), buffer.size_end(), "prepare announce : state:%s family:%s id:%" PRIx32 " up_adj:%" PRIu64 " completed_adj:%" PRIu64 " left_adj:%" PRIu64, - option_as_string(OPTION_TRACKER_EVENT, m_send_state), family_str(family), state_for_family(family).transaction_id, + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, m_send_state), family_str(family), state_for_family(family).transaction_id, m_params.uploaded_adjusted, m_params.completed_adjusted, m_params.download_left); } bool TrackerUdp::process_announce(int family, uint32_t id, buffer_type& buffer) { LT_LOG_DUMP(buffer.begin(), buffer.size_end(), "process announce : state:%s family:%s id:%" PRIx32, - option_as_string(OPTION_TRACKER_EVENT, m_send_state), family_str(family), id); + option_to_c_str_or_throw(OPTION_TRACKER_EVENT, m_send_state), family_str(family), id); switch (process_header(family, 1, buffer)) { case -1: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/src/tracker/udp_router.cc new/libtorrent-0.16.15/src/tracker/udp_router.cc --- old/libtorrent-0.16.14/src/tracker/udp_router.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/src/tracker/udp_router.cc 2026-06-22 11:01:48.000000000 +0200 @@ -284,10 +284,11 @@ UdpRouter::disconnect_failure_unsafe(connection_map::iterator itr, int errno_err, int gai_err) { assert(itr != m_connections.end()); + auto id = itr->first; auto failure_fn = std::move(itr->second.failure); disconnect_unsafe(itr); - failure_fn(itr->first, errno_err, gai_err); + failure_fn(id, errno_err, gai_err); } void diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/libtorrent-0.16.14/test/torrent/utils/test_option_strings.cc new/libtorrent-0.16.15/test/torrent/utils/test_option_strings.cc --- old/libtorrent-0.16.14/test/torrent/utils/test_option_strings.cc 2026-06-14 10:04:09.000000000 +0200 +++ new/libtorrent-0.16.15/test/torrent/utils/test_option_strings.cc 2026-06-22 11:01:48.000000000 +0200 @@ -10,7 +10,7 @@ #define TEST_ENTRY(group, name, value) \ { lt_log_print(torrent::LOG_MOCK_CALLS, "option_string: %s", name); \ - std::string result(torrent::option_as_string(torrent::group, value)); \ + std::string result(torrent::option_to_c_str_or_throw(torrent::group, value)); \ CPPUNIT_ASSERT_MESSAGE("Not found '" + result + "'", result == name); \ CPPUNIT_ASSERT(torrent::option_find_string(torrent::group, name) == value); \ }
