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); \
   }

Reply via email to