new file mode 100644
@@ -0,0 +1,44 @@
+From c2b119de0e66fb047bb20e445ac8e25824448858 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Sun, 30 Jul 2017 20:34:47 +0100
+Subject: [PATCH] Fix compilation issue with gcc v6.x and empty CXXFLAGS (See
+ #10)
+
+---
+ configure.ac | 1 +
+ scripts/rak_compiler.m4 | 13 +++++++++++++
+ 2 files changed, 14 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 65e34872..2f29e3f9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -28,6 +28,7 @@ AC_C_BIGENDIAN(
+ AC_MSG_ERROR([Could not determine endianness])
+ )
+
++RAK_CHECK_CXXFLAGS
+ RAK_ENABLE_DEBUG
+ RAK_ENABLE_EXTRA_DEBUG
+ RAK_ENABLE_WERROR
+diff --git a/scripts/rak_compiler.m4 b/scripts/rak_compiler.m4
+index 39bd19a7..87871abf 100644
+--- a/scripts/rak_compiler.m4
++++ b/scripts/rak_compiler.m4
+@@ -1,3 +1,16 @@
++AC_DEFUN([RAK_CHECK_CXXFLAGS], [
++
++ AC_MSG_CHECKING([for user-defined CXXFLAGS])
++
++ if test -n "$CXXFLAGS"; then
++ AC_MSG_RESULT([user-defined "$CXXFLAGS"])
++ else
++ CXXFLAGS="-O2"
++ AC_MSG_RESULT([default "$CXXFLAGS"])
++ fi
++])
++
++
+ AC_DEFUN([RAK_ENABLE_DEBUG], [
+ AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug], [enable debug information [[default=yes]]]),
new file mode 100644
@@ -0,0 +1,56 @@
+From 8229218dff1105e9fb2bb2c7510910a0db98f3ef Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Wed, 2 Aug 2017 12:48:27 +0100
+Subject: [PATCH] Modfiy gcc v6.x fix for empty CXXFLAGS (See #10)
+
+---
+ configure.ac | 1 +
+ scripts/rak_compiler.m4 | 21 +++++++++++++++++----
+ 2 files changed, 18 insertions(+), 4 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 2f29e3f9..a6df6b80 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -28,6 +28,7 @@ AC_C_BIGENDIAN(
+ AC_MSG_ERROR([Could not determine endianness])
+ )
+
++RAK_CHECK_CFLAGS
+ RAK_CHECK_CXXFLAGS
+ RAK_ENABLE_DEBUG
+ RAK_ENABLE_EXTRA_DEBUG
+diff --git a/scripts/rak_compiler.m4 b/scripts/rak_compiler.m4
+index 87871abf..9a361bed 100644
+--- a/scripts/rak_compiler.m4
++++ b/scripts/rak_compiler.m4
+@@ -1,12 +1,25 @@
++AC_DEFUN([RAK_CHECK_CFLAGS], [
++
++ AC_MSG_CHECKING([for user-defined CFLAGS])
++
++ if test "$CFLAGS" = ""; then
++ unset CFLAGS
++ AC_MSG_RESULT([undefined])
++ else
++ AC_MSG_RESULT([user-defined "$CFLAGS"])
++ fi
++])
++
++
+ AC_DEFUN([RAK_CHECK_CXXFLAGS], [
+
+ AC_MSG_CHECKING([for user-defined CXXFLAGS])
+
+- if test -n "$CXXFLAGS"; then
+- AC_MSG_RESULT([user-defined "$CXXFLAGS"])
++ if test "$CXXFLAGS" = ""; then
++ unset CXXFLAGS
++ AC_MSG_RESULT([undefined])
+ else
+- CXXFLAGS="-O2"
+- AC_MSG_RESULT([default "$CXXFLAGS"])
++ AC_MSG_RESULT([user-defined "$CXXFLAGS"])
+ fi
+ ])
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 48635a0fdb06b8572809dc54c630109db1e6e85c Mon Sep 17 00:00:00 2001
+From: Matt Traudt <sirmatt@ksu.edu>
+Date: Mon, 20 Nov 2017 15:22:51 -0500
+Subject: [PATCH] Add space to fmt str in log_gz_file_write
+
+Without this space, the log level identifier ('D', 'I', etc.)
+would be right next to actual content.
+
+Before: <timestamp> Depoll->pcb(17): Open event.
+After: <timestamp> D epoll->pcb(17): Open event.
+---
+ src/torrent/utils/log.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index 646d36e3..58b563a6 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -381,7 +381,7 @@ log_gz_file_write(std::shared_ptr<log_gz_output>& outfile, const char* data, siz
+
+ // Normal groups are nul-terminated strings.
+ if (group >= 0) {
+- const char* fmt = (group >= LOG_NON_CASCADING) ? ("%" PRIi32 " ") : ("%" PRIi32 " %c");
++ const char* fmt = (group >= LOG_NON_CASCADING) ? ("%" PRIi32 " ") : ("%" PRIi32 " %c ");
+
+ int buffer_length = snprintf(buffer, 64, fmt,
+ cachedTime.seconds(),
new file mode 100644
@@ -0,0 +1,379 @@
+From facd1b78071e315a2e5ee4992d3545dae4290e07 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Sun, 10 Dec 2017 18:51:32 +0000
+Subject: [PATCH] IPv4 filter enhancement #11IPv4 filter enhancement (Closes
+ #11)
+
+---
+ src/torrent/peer/peer_list.cc | 23 ++-
+ src/torrent/peer/peer_list.h | 2 +-
+ src/torrent/utils/extents.h | 267 ++++++++++++++--------------------
+ 3 files changed, 134 insertions(+), 158 deletions(-)
+
+diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc
+index 23ca651a..aa60939a 100644
+--- a/src/torrent/peer/peer_list.cc
++++ b/src/torrent/peer/peer_list.cc
+@@ -146,7 +146,11 @@ PeerList::insert_address(const sockaddr* sa, int flags) {
+
+ PeerInfo* peerInfo = new PeerInfo(sa);
+ peerInfo->set_listen_port(address->port());
+- peerInfo->set_flags(m_ipv4_table.at(address->sa_inet()->address_h()) & PeerInfo::mask_ip_table);
++ uint32_t host_byte_order_ipv4_addr = address->sa_inet()->address_h();
++
++ // IPv4 addresses stored in host byte order in ipv4_table so they are comparable. ntohl has been called
++ if(m_ipv4_table.defined(host_byte_order_ipv4_addr))
++ peerInfo->set_flags(m_ipv4_table.at(host_byte_order_ipv4_addr) & PeerInfo::mask_ip_table);
+
+ manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info());
+
+@@ -264,12 +268,25 @@ PeerList::connected(const sockaddr* sa, int flags) {
+ !socket_address_key::is_comparable_sockaddr(sa))
+ return NULL;
+
+- int filter_value = m_ipv4_table.at(address->sa_inet()->address_h());
++ uint32_t host_byte_order_ipv4_addr = address->sa_inet()->address_h();
++ int filter_value = 0;
++
++ // IPv4 addresses stored in host byte order in ipv4_table so they are comparable. ntohl has been called
++ if(m_ipv4_table.defined(host_byte_order_ipv4_addr))
++ filter_value = m_ipv4_table.at(host_byte_order_ipv4_addr);
+
+ // We should also remove any PeerInfo objects already for this
+ // address.
+- if ((filter_value & PeerInfo::flag_unwanted))
++ if ((filter_value & PeerInfo::flag_unwanted)) {
++ char ipv4_str[INET_ADDRSTRLEN];
++ uint32_t net_order_addr = htonl(host_byte_order_ipv4_addr);
++
++ inet_ntop(AF_INET, &net_order_addr, ipv4_str, INET_ADDRSTRLEN);
++
++ lt_log_print(LOG_PEER_INFO, "Peer %s is unwanted: preventing connection", ipv4_str);
++
+ return NULL;
++ }
+
+ PeerInfo* peerInfo;
+ range_type range = base_type::equal_range(sock_key);
+diff --git a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h
+index a3b409cb..4c2f707d 100644
+--- a/src/torrent/peer/peer_list.h
++++ b/src/torrent/peer/peer_list.h
+@@ -46,7 +46,7 @@ namespace torrent {
+
+ class DownloadInfo;
+
+-typedef extents<uint32_t, int, 32, 256, 8> ipv4_table;
++typedef extents<uint32_t, int> ipv4_table;
+
+ class LIBTORRENT_EXPORT PeerList : private std::multimap<socket_address_key, PeerInfo*> {
+ public:
+diff --git a/src/torrent/utils/extents.h b/src/torrent/utils/extents.h
+index 8ec1e600..c2b887b1 100644
+--- a/src/torrent/utils/extents.h
++++ b/src/torrent/utils/extents.h
+@@ -37,191 +37,150 @@
+ #ifndef LIBTORRENT_UTILS_EXTENTS_H
+ #define LIBTORRENT_UTILS_EXTENTS_H
+
+-#include lt_tr1_array
+
+-#include <algorithm>
++#include <map>
++#include <stdexcept>
+
+ namespace torrent {
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-struct extents_base {
+- typedef Key key_type;
+- typedef std::pair<Key, Key> range_type;
+- typedef std::pair<extents_base*, Tp> mapped_type;
+- typedef Tp mapped_value_type;
+-
+- typedef std::array<mapped_type, TableSize> table_type;
+-
+- extents_base(key_type pos, unsigned int mb, mapped_value_type val);
+- extents_base(extents_base* parent, typename table_type::const_iterator itr);
+- ~extents_base();
+-
+- bool is_divisible(key_type key) const { return key % mask_bits == 0; }
+- bool is_leaf_branch() const { return mask_bits == 0; }
+- bool is_equal_range(key_type first, key_type last, const mapped_value_type& val) const;
+-
+- unsigned int sizeof_data() const;
+-
+- typename table_type::iterator partition_at(key_type key) { return table.begin() + ((key >> mask_bits) & (TableSize - 1)); }
+- typename table_type::const_iterator partition_at(key_type key) const { return table.begin() + ((key >> mask_bits) & (TableSize - 1)); }
+-
+- unsigned int mask_distance(unsigned int mb) { return (~(~key_type() << mb) >> mask_bits); }
+-
+- key_type partition_pos(typename table_type::const_iterator part) const { return position + (std::distance(table.begin(), part) << mask_bits); }
+-
+- void insert(key_type pos, unsigned int mb, const mapped_value_type& val);
+-
+- const mapped_value_type& at(key_type key) const;
+-
+- unsigned int mask_bits;
+- key_type position;
+- table_type table;
+-};
+-
+-template <typename Key, typename Tp, unsigned int MaskBits, unsigned int TableSize, unsigned int TableBits>
+-class extents : private extents_base<Key, Tp, TableSize, TableBits> {
++template <class Address, class Value, class Compare=std::less<Address> >
++class extents {
+ public:
+- typedef extents_base<Key, Tp, TableSize, TableBits> base_type;
+-
+- typedef typename base_type::key_type key_type;
+- typedef base_type value_type;
+- typedef typename base_type::range_type range_type;
+- typedef typename base_type::mapped_type mapped_type;
+- typedef typename base_type::mapped_value_type mapped_value_type;
+- typedef typename base_type::table_type table_type;
+-
+- static const key_type mask_bits = MaskBits;
+- static const key_type table_bits = TableBits;
+- static const key_type table_size = TableSize;
+-
+- using base_type::at;
+- using base_type::sizeof_data;
++ typedef Address key_type; // start address
++ typedef Value mapped_value_type; // The value mapped to the ip range
++ typedef std::pair<Address, Value> mapped_type; // End address, value mapped to ip range
++ typedef std::map<key_type, mapped_type, Compare> range_map_type; // The map itself
+
+ extents();
++ ~extents();
+
+- bool is_equal_range(key_type first, key_type last, const mapped_value_type& val) const;
+-
+- void insert(key_type pos, unsigned int mb, const mapped_value_type& val);
++ void insert(key_type address_start, key_type address_end, mapped_value_type value);
++ bool defined(key_type address_start, key_type address_end);
++ bool defined(key_type address);
++ key_type get_matching_key(key_type address_start, key_type address_end); // throws error on not defined. test with defined()
++ mapped_value_type at(key_type address_start, key_type address_end); // throws error on not defined. test with defined()
++ mapped_value_type at(key_type address); // throws error on not defined. test with defined()
++ unsigned int sizeof_data() const;
+
+- base_type* data() { return this; }
++ range_map_type range_map;
+ };
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-extents_base<Key, Tp, TableSize, TableBits>::extents_base(key_type pos, unsigned int mb, mapped_value_type val) :
+- mask_bits(mb), position(pos) {
+- std::fill(table.begin(), table.end(), mapped_type(NULL, mapped_value_type()));
++///////////////////////////////////////
++// CONSTRUCTOR [PLACEHOLDER]
++///////////////////////////////////////
++template <class Address, class Value, class Compare >
++extents<Address, Value, Compare>::extents() {
++ //nothing to do
++ return;
+ }
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-extents_base<Key, Tp, TableSize, TableBits>::extents_base(extents_base* parent, typename table_type::const_iterator itr) :
+- mask_bits(parent->mask_bits - TableBits), position(parent->partition_pos(itr)) {
+- std::fill(table.begin(), table.end(), mapped_type(NULL, itr->second));
++///////////////////////////////////////
++// DESTRUCTOR [PLACEHOLDER]
++///////////////////////////////////////
++template <class Address, class Value, class Compare >
++extents<Address, Value, Compare>::~extents() {
++ //nothing to do. map destructor can handle cleanup.
++ return;
+ }
+
+-template <typename Key, typename Tp, unsigned int MaskBits, unsigned int TableSize, unsigned int TableBits>
+-extents<Key, Tp, MaskBits, TableSize, TableBits>::extents() :
+- base_type(key_type(), mask_bits - table_bits, mapped_value_type())
+-{
+-}
++//////////////////////////////////////////////////////////////////////////////////
++// INSERT O(log N) assuming no overlapping ranges
++/////////////////////////////////////////////////////////////////////////////////
++template <class Address, class Value, class Compare >
++void extents<Address, Value, Compare>::insert(key_type address_start, key_type address_end, mapped_value_type value) {
++ //we allow overlap ranges though not 100% overlap but only if mapped values are the same. first remove any overlap range that has a different value.
++ typename range_map_type::iterator iter = range_map.upper_bound(address_start);
++ if( iter != range_map.begin() ) { iter--; }
++ bool ignore_due_to_total_overlap = false;
++ while( iter->first <= address_end && iter != range_map.end() ) {
++ key_type delete_key = iter->first;
++ bool do_delete_due_to_overlap = iter->first <= address_end && (iter->second).first >= address_start && (iter->second).second != value;
++ bool do_delete_due_to_total_overlap = address_start <= iter->first && address_end >= (iter->second).first;
++ iter++;
++ if(do_delete_due_to_overlap || do_delete_due_to_total_overlap) {
++ range_map.erase (delete_key);
++ }
++ else {
++ ignore_due_to_total_overlap = ignore_due_to_total_overlap || ( iter->first <= address_start && (iter->second).first >= address_end );
++ }
++ }
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-extents_base<Key, Tp, TableSize, TableBits>::~extents_base() {
+- for (typename table_type::const_iterator itr = table.begin(), last = table.end(); itr != last; itr++)
+- delete itr->first;
++ if(!ignore_due_to_total_overlap) {
++ mapped_type entry;
++ entry.first = address_end;
++ entry.second = value;
++ range_map.insert( std::pair<key_type,mapped_type>(address_start, entry) );
++ }
+ }
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-unsigned int
+-extents_base<Key, Tp, TableSize, TableBits>::sizeof_data() const {
+- unsigned int sum = sizeof(*this);
+-
+- for (typename table_type::const_iterator itr = table.begin(), last = table.end(); itr != last; itr++)
+- if (itr->first != NULL)
+- sum += itr->first->sizeof_data();
+-
+- return sum;
++//////////////////////////////////////////////////////////////////////
++// DEFINED O(log N) assuming no overlapping ranges
++//////////////////////////////////////////////////////////////////////
++template <class Address, class Value, class Compare >
++bool extents<Address, Value, Compare>::defined(key_type address_start, key_type address_end) {
++ bool defined = false;
++ typename range_map_type::iterator iter = range_map.upper_bound(address_start);
++ if( iter != range_map.begin() ) { iter--; }
++ while( iter->first <= address_end && !defined && iter != range_map.end() ) {
++ defined = iter->first <= address_end && (iter->second).first >= address_start;
++ iter++;
++ }
++ return defined;
+ }
+-
+-template <typename Key, typename Tp, unsigned int MaskBits, unsigned int TableSize, unsigned int TableBits>
+-void
+-extents<Key, Tp, MaskBits, TableSize, TableBits>::insert(key_type pos, unsigned int mb, const mapped_value_type& val) {
+- key_type mask = ~key_type() << mb;
+-
+- base_type::insert(pos & mask, mb, val);
++template <class Address, class Value, class Compare >
++bool extents<Address, Value, Compare>::defined(key_type address) {
++ return defined(address, address);
+ }
+
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-void
+-extents_base<Key, Tp, TableSize, TableBits>::insert(key_type pos, unsigned int mb, const mapped_value_type& val) {
+- // RESTRICTED
+- typename table_type::iterator first = partition_at(pos);
+- typename table_type::iterator last = partition_at(pos) + mask_distance(mb) + 1;
+-
+- if (mb < mask_bits) {
+- if (first->first == NULL)
+- first->first = new extents_base(this, first);
++//////////////////////////////////////////////////////////////////////
++// GET_MATCHING_KEY O(log N) assuming no overlapping ranges
++//////////////////////////////////////////////////////////////////////
++template <class Address, class Value, class Compare >
++typename extents<Address, Value, Compare>::key_type extents<Address, Value, Compare>::get_matching_key(key_type address_start, key_type address_end) {
++ key_type key;
++ bool defined = false;
++ typename range_map_type::iterator iter = range_map.upper_bound(address_start);
++ if( iter != range_map.begin() ) { iter--; }
++ while( iter->first <= address_end && !defined && iter != range_map.end() ) {
++ defined = iter->first <= address_end && (iter->second).first >= address_start;
++ if(defined)
++ key = iter->first;
+
+- first->first->insert(pos, mb, val);
+- return;
++ iter++;
+ }
+-
+- while (first != last) {
+- if (first->first != NULL) {
+- delete first->first;
+- first->first = NULL;
+- }
+-
+- (first++)->second = val;
++ // this will cause exception to be thrown
++ if(!defined) {
++ std::out_of_range e("nothing defined for specified key");
++ throw e;
+ }
++ return key;
+ }
+
+-template <typename Key, typename Tp, unsigned int MaskBits, unsigned int TableSize, unsigned int TableBits>
+-bool
+-extents<Key, Tp, MaskBits, TableSize, TableBits>::is_equal_range(key_type first, key_type last, const mapped_value_type& val) const {
+- // RESTRICTED
+- first = std::max(first, key_type());
+- last = std::min(last, key_type() + (~key_type() >> (sizeof(key_type) * 8 - MaskBits)));
+-
+- if (first <= last)
+- return base_type::is_equal_range(first, last, val);
+- else
+- return true;
++//////////////////////////////////////////////////////////////////////
++// AT O(log N) assuming no overlapping ranges
++//////////////////////////////////////////////////////////////////////
++template <class Address, class Value, class Compare >
++typename extents<Address, Value, Compare>::mapped_value_type extents<Address, Value, Compare>::at(key_type address_start, key_type address_end) {
++ key_type key = get_matching_key(address_start, address_end);
++ mapped_type entry = range_map.at(key);
++ return entry.second;
+ }
+-
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-bool
+-extents_base<Key, Tp, TableSize, TableBits>::is_equal_range(key_type key_first, key_type key_last, const mapped_value_type& val) const {
+- // RESTRICTED
+- typename table_type::const_iterator first = partition_at(key_first);
+- typename table_type::const_iterator last = partition_at(key_last) + 1;
+-
+- do {
+- // std::cout << "shift_amount " << key_first << ' ' << key_last << std::endl;
+-
+- if (first->first == NULL && val != first->second)
+- return false;
+-
+- if (first->first != NULL && !first->first->is_equal_range(std::max(key_first, partition_pos(first)),
+- std::min(key_last, partition_pos(first + 1) - 1), val))
+- return false;
+-
+- } while (++first != last);
+-
+- return true;
++template <class Address, class Value, class Compare >
++typename extents<Address, Value, Compare>::mapped_value_type extents<Address, Value, Compare>::at(key_type address) {
++ return at(address, address);
+ }
+
+-// Assumes 'key' is within the range of the range.
+-template <typename Key, typename Tp, unsigned int TableSize, unsigned int TableBits>
+-const typename extents_base<Key, Tp, TableSize, TableBits>::mapped_value_type&
+-extents_base<Key, Tp, TableSize, TableBits>::at(key_type key) const {
+- typename table_type::const_iterator itr = partition_at(key);
+-
+- while (itr->first != NULL)
+- itr = itr->first->partition_at(key);
+-
+- return itr->second;
++//////////////////////////////////////////////////////////////////////
++// SIZEOF_DATA O(1)
++//////////////////////////////////////////////////////////////////////
++template <class Address, class Value, class Compare >
++unsigned int extents<Address, Value, Compare>::sizeof_data() const {
++ // we don't know overhead on map, so this won't be accurate. just estimate.
++ unsigned int entry_size = sizeof(key_type) + sizeof(mapped_type);
++ return entry_size * range_map.size();
+ }
+
++
+ }
+
+ #endif
new file mode 100644
@@ -0,0 +1,47 @@
+From 4a5ed3897e772c75929b376b08985d844898b619 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Tue, 12 Dec 2017 15:19:51 +0000
+Subject: [PATCH] Disable extents test to pass TravisCI (See #11)
+
+---
+ test/torrent/utils/test_extents.cc | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/test/torrent/utils/test_extents.cc b/test/torrent/utils/test_extents.cc
+index 6ac5a57d..d6b8d11d 100644
+--- a/test/torrent/utils/test_extents.cc
++++ b/test/torrent/utils/test_extents.cc
+@@ -16,10 +16,11 @@ void
+ ExtentsTest::tearDown() {
+ }
+
+-typedef torrent::extents<uint32_t, int, 8, 16, 4> extent_type_1;
++//typedef torrent::extents<uint32_t, int, 8, 16, 4> extent_type_1;
++typedef torrent::extents<uint32_t, int> extent_type_1;
+
+ // typedef torrent::extents<uint32_t, int, 0, 256, 16> extent_type_3;
+-
++/*
+ template <typename Extent>
+ bool
+ verify_extent_data(Extent& extent, const uint32_t* idx, const int* val) {
+@@ -46,11 +47,11 @@ static const int val_basic_1[] = {1, 0, 1};
+
+ // static const uint32_t idx_basic_2[] = {0, 1, 16, 255, 256, 256};
+ // static const int val_basic_2[] = {1, 0, 2, 1};
+-
++*/
+ void
+ ExtentsTest::test_basic() {
+ extent_type_1 extent_1;
+-
++/*
+ // Test empty.
+ CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_empty, val_empty));
+
+@@ -68,4 +69,5 @@ ExtentsTest::test_basic() {
+ // extent_1.insert(38, 3, 2);
+
+ // CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_2, val_basic_2));
++*/
+ }
new file mode 100644
@@ -0,0 +1,30 @@
+From da7db7db29a8488e29d3b0f02906c5db379d4b6d Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Thu, 7 Jun 2018 13:18:03 +0900
+Subject: [PATCH] Bumped version to 0.13.7.
+
+---
+ configure.ac | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index a6df6b80..5b1ea237 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1,12 +1,12 @@
+-AC_INIT(libtorrent, 0.13.6, sundell.software@gmail.com)
++AC_INIT(libtorrent, 0.13.7, sundell.software@gmail.com)
+
+ LT_INIT([disable-static])
+
+ dnl Find a better way to do this
+-AC_DEFINE(PEER_NAME, "-lt0D60-", Identifier that is part of the default peer id)
+-AC_DEFINE(PEER_VERSION, "lt\x0D\x60", 4 byte client and version identifier for DHT)
++AC_DEFINE(PEER_NAME, "-lt0D70-", Identifier that is part of the default peer id)
++AC_DEFINE(PEER_VERSION, "lt\x0D\x70", 4 byte client and version identifier for DHT)
+
+-LIBTORRENT_CURRENT=19
++LIBTORRENT_CURRENT=20
+ LIBTORRENT_REVISION=0
+ LIBTORRENT_AGE=0
+
new file mode 100644
@@ -0,0 +1,105 @@
+From dbf6abfd6f905b9218465d15eebec7eedaaed6b0 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Tue, 20 Dec 2016 19:51:02 +0900
+Subject: [PATCH] Added support for openssl 1.1.
+
+---
+ configure.ac | 4 ++++
+ src/utils/diffie_hellman.cc | 36 ++++++++++++++++++++++++++++++++++--
+ 2 files changed, 38 insertions(+), 2 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 5b1ea237..b885714d 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -71,12 +71,15 @@ AC_ARG_ENABLE(openssl,
+ [ --disable-openssl Don't use OpenSSL's SHA1 implementation.],
+ [
+ if test "$enableval" = "yes"; then
++dnl move to scripts.
+ PKG_CHECK_MODULES(OPENSSL, libcrypto,
+ CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS";
+ LIBS="$LIBS $OPENSSL_LIBS")
+
+ AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
+ AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
++ AC_CHECK_LIB([crypto], [DH_set0_pqg], [AC_DEFINE(USE_OPENSSL_1_1, 1, Using OpenSSL 1.1.)])
++
+ else
+ AC_DEFINE(USE_NSS_SHA, 1, Using Mozilla's SHA1 implementation.)
+ fi
+@@ -87,6 +90,7 @@ AC_ARG_ENABLE(openssl,
+
+ AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
+ AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
++ AC_CHECK_LIB([crypto], [DH_set0_pqg], [AC_DEFINE(USE_OPENSSL_1_1, 1, Using OpenSSL 1.1.)])
+ ]
+ )
+
+diff --git a/src/utils/diffie_hellman.cc b/src/utils/diffie_hellman.cc
+index aa653d45..7ec13165 100644
+--- a/src/utils/diffie_hellman.cc
++++ b/src/utils/diffie_hellman.cc
+@@ -54,11 +54,23 @@ DiffieHellman::DiffieHellman(const unsigned char *prime, int primeLength,
+ m_secret(NULL), m_size(0) {
+
+ #ifdef USE_OPENSSL
++
+ m_dh = DH_new();
++
++#ifdef USE_OPENSSL_1_1
++ BIGNUM * const dh_p = BN_bin2bn(prime, primeLength, NULL);
++ BIGNUM * const dh_g = BN_bin2bn(generator, generatorLength, NULL);
++
++ if (dh_p == NULL || dh_g == NULL ||
++ !DH_set0_pqg(m_dh, dh_p, NULL, dh_g))
++ throw internal_error("Could not generate Diffie-Hellman parameters");
++#else
+ m_dh->p = BN_bin2bn(prime, primeLength, NULL);
+ m_dh->g = BN_bin2bn(generator, generatorLength, NULL);
++#endif
+
+ DH_generate_key(m_dh);
++
+ #else
+ throw internal_error("Compiled without encryption support.");
+ #endif
+@@ -74,7 +86,19 @@ DiffieHellman::~DiffieHellman() {
+ bool
+ DiffieHellman::is_valid() const {
+ #ifdef USE_OPENSSL
++ if (m_dh == NULL)
++ return false;
++
++#ifdef USE_OPENSSL_1_1
++ const BIGNUM *pub_key;
++
++ DH_get0_key(m_dh, &pub_key, NULL);
++
++ return pub_key != NULL;
++#else
+ return m_dh != NULL && m_dh->pub_key != NULL;
++#endif
++
+ #else
+ return false;
+ #endif
+@@ -103,8 +127,16 @@ DiffieHellman::store_pub_key(unsigned char* dest, unsigned int length) {
+ #ifdef USE_OPENSSL
+ std::memset(dest, 0, length);
+
+- if ((int)length >= BN_num_bytes(m_dh->pub_key))
+- BN_bn2bin(m_dh->pub_key, dest + length - BN_num_bytes(m_dh->pub_key));
++ const BIGNUM *pub_key;
++
++#ifdef USE_OPENSSL_1_1
++ DH_get0_key(m_dh, &pub_key, NULL);
++#else
++ pub_key = m_dh->pub_key;
++#endif
++
++ if ((int)length >= BN_num_bytes(pub_key))
++ BN_bn2bin(pub_key, dest + length - BN_num_bytes(pub_key));
+ #endif
+ }
+
new file mode 100644
@@ -0,0 +1,24 @@
+From c2ec5e0fb8ce7a0df513b5f4086e23d92049ef0e Mon Sep 17 00:00:00 2001
+From: Stephen Shkardoon <ss23@ss23.geek.nz>
+Date: Mon, 25 Jun 2018 20:05:18 +1200
+Subject: [PATCH] Use AC_COMPILE instead of AC_RUN to check for execinfo.h
+
+This way enables cross compiling, since we don't need to run anything
+during the configure script.
+---
+ scripts/common.m4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/scripts/common.m4 b/scripts/common.m4
+index ff023928..b6d051f5 100644
+--- a/scripts/common.m4
++++ b/scripts/common.m4
+@@ -153,7 +153,7 @@ dnl Need to fix this so that it uses the stuff defined by the system.
+ AC_DEFUN([TORRENT_CHECK_EXECINFO], [
+ AC_MSG_CHECKING(for execinfo.h)
+
+- AC_RUN_IFELSE([AC_LANG_SOURCE([
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+ #include <execinfo.h>
+ int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;}
+ ])],
new file mode 100644
@@ -0,0 +1,46 @@
+From b0fb874a8921fa4ba2ea0923d779fae8f70c82b1 Mon Sep 17 00:00:00 2001
+From: Stephen Shkardoon <ss23@ss23.geek.nz>
+Date: Thu, 21 Jun 2018 14:38:30 +1200
+Subject: [PATCH] Modify configure to prevent unnecessary kqueue checks
+
+By only running the TORRENT_CHECK_KQUEUE_SOCKET_ONLY check if kqueue support
+is already detected, we increase the number of platforms that we can
+cross compile on.
+Otherwise, the cross compilation fails due to TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+using AC_RUN_IFELSE, which fails during cross compilation.
+---
+ scripts/checks.m4 | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/scripts/checks.m4 b/scripts/checks.m4
+index 8d77fc5e..c9333561 100644
+--- a/scripts/checks.m4
++++ b/scripts/checks.m4
+@@ -88,6 +88,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
+ [
+ AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
+ AC_MSG_RESULT(yes)
++ TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ], [
+ AC_MSG_RESULT(no)
+ ])
+@@ -137,7 +138,6 @@ AC_DEFUN([TORRENT_WITH_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ])
+ ])
+@@ -149,11 +149,9 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ], [
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ])
+ ])
+
new file mode 100644
@@ -0,0 +1,58 @@
+From 4ff83fc53b2c7462b02f804ee20414d85944e3c9 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Sun, 14 May 2017 19:36:09 +0100
+Subject: [PATCH] Display info on failed tracker bencode parsing (See #9)
+
+---
+ rak/string_manip.h | 20 ++++++++++++++++++++
+ src/tracker/tracker_http.cc | 6 ++++--
+ 2 files changed, 24 insertions(+), 2 deletions(-)
+
+diff --git a/rak/string_manip.h b/rak/string_manip.h
+index f8d3f590..68614d2a 100644
+--- a/rak/string_manip.h
++++ b/rak/string_manip.h
+@@ -371,6 +371,26 @@ is_all_name(const Sequence& src) {
+ return is_all_name(src.begin(), src.end());
+ }
+
++template <typename Iterator>
++std::string
++sanitize(Iterator first, Iterator last) {
++ std::string dest;
++ for (; first != last; ++first) {
++ if (std::isprint(*first) && *first != '\r' && *first != '\n' && *first != '\t')
++ dest += *first;
++ else
++ dest += " ";
++ }
++
++ return dest;
++}
++
++template <typename Sequence>
++std::string
++sanitize(const Sequence& src) {
++ return trim(sanitize(src.begin(), src.end()));
++}
++
+ }
+
+ #endif
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index 553f922c..b6d9e0ac 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -288,8 +288,10 @@ TrackerHttp::receive_done() {
+ Object b;
+ *m_data >> b;
+
+- if (m_data->fail())
+- return receive_failed("Could not parse bencoded data");
++ if (m_data->fail()) {
++ std::string dump = m_data->str();
++ return receive_failed("Could not parse bencoded data: " + rak::sanitize(dump).substr(0,99));
++ }
+
+ if (!b.is_map())
+ return receive_failed("Root not a bencoded map");
new file mode 100644
@@ -0,0 +1,60 @@
+From 2f197be69057f793d29272b90300f74d0b588d51 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Mon, 15 May 2017 19:24:33 +0100
+Subject: [PATCH] Strip tags also when displaying info on failed tracker
+ bencode parsing (See #9)
+
+---
+ rak/string_manip.h | 25 +++++++++++++++++++++++++
+ src/tracker/tracker_http.cc | 2 +-
+ 2 files changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/rak/string_manip.h b/rak/string_manip.h
+index 68614d2a..ae867c98 100644
+--- a/rak/string_manip.h
++++ b/rak/string_manip.h
+@@ -391,6 +391,31 @@ sanitize(const Sequence& src) {
+ return trim(sanitize(src.begin(), src.end()));
+ }
+
++template <typename Iterator>
++std::string striptags(Iterator first, Iterator last) {
++ bool copychar = true;
++ std::string dest;
++
++ for (; first != last; ++first) {
++ if (std::isprint(*first) && *first == '<') {
++ copychar = false;
++ } else if (std::isprint(*first) && *first == '>') {
++ copychar = true;
++ continue;
++ }
++
++ if (copychar)
++ dest += *first;
++ }
++
++ return dest;
++}
++
++template <typename Sequence>
++std::string striptags(const Sequence& src) {
++ return striptags(src.begin(), src.end());
++}
++
+ }
+
+ #endif
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index b6d9e0ac..eefe5a17 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -290,7 +290,7 @@ TrackerHttp::receive_done() {
+
+ if (m_data->fail()) {
+ std::string dump = m_data->str();
+- return receive_failed("Could not parse bencoded data: " + rak::sanitize(dump).substr(0,99));
++ return receive_failed("Could not parse bencoded data: " + rak::sanitize(rak::striptags(dump)).substr(0,99));
+ }
+
+ if (!b.is_map())
new file mode 100644
@@ -0,0 +1,45 @@
+From 0e86289a8bb5672781e508683bb28aebf9995127 Mon Sep 17 00:00:00 2001
+From: lps-rocks <admin@lps.rocks>
+Date: Mon, 4 Mar 2019 05:03:47 -0500
+Subject: [PATCH] Switch to C++11 MRT RNG for random bytes
+
+Switching to a better RNG for generating strings will prevent the common peerID collisions the rTorrent client has been seeing for YEARS in #440 and #318.
+---
+ rak/string_manip.h | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/rak/string_manip.h b/rak/string_manip.h
+index ae867c98..1a09c377 100644
+--- a/rak/string_manip.h
++++ b/rak/string_manip.h
+@@ -39,9 +39,13 @@
+
+ #include <algorithm>
+ #include <cctype>
++#include <climits>
+ #include <cstdlib>
++#include <functional>
+ #include <iterator>
+ #include <locale>
++#include <random>
++
+
+ namespace rak {
+
+@@ -312,11 +316,13 @@ transform_hex_str(const Sequence& seq) {
+ template <typename Sequence>
+ Sequence
+ generate_random(size_t length) {
++ std::random_device rd;
++ std::mt19937 mt(rd());
++ using bytes_randomizer = std::independent_bits_engine<std::mt19937, CHAR_BIT, uint8_t>;
++ bytes_randomizer bytes(mt);
+ Sequence s;
+ s.reserve(length);
+-
+- std::generate_n(std::back_inserter(s), length, &::random);
+-
++ std::generate_n(std::back_inserter(s), length, std::ref(bytes));
+ return s;
+ }
+
new file mode 100644
@@ -0,0 +1,45 @@
+From bf35c5f3d4e458a671fdc3c382f4fa06ecaeb119 Mon Sep 17 00:00:00 2001
+From: Vladyslav Movchan <vladislav.movchan@gmail.com>
+Date: Sat, 3 Nov 2018 19:52:56 +0200
+Subject: [PATCH] Prevent loss of 'm_ipv6_socket' attribute which led to
+ execution of setsockopt(..., IPPROTO_IP, IP_TOS, ...) on IPv6 socket
+
+---
+ src/net/socket_fd.cc | 4 ++--
+ src/net/socket_fd.h | 1 +
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/net/socket_fd.cc b/src/net/socket_fd.cc
+index 54cb6ded..f04059f6 100644
+--- a/src/net/socket_fd.cc
++++ b/src/net/socket_fd.cc
+@@ -251,7 +251,7 @@ SocketFd::accept(rak::socket_address* sa) {
+ socklen_t len = sizeof(rak::socket_address);
+
+ if (sa == NULL) {
+- return SocketFd(::accept(m_fd, NULL, &len));
++ return SocketFd(::accept(m_fd, NULL, &len), m_ipv6_socket);
+ }
+
+ int fd = ::accept(m_fd, sa->c_sockaddr(), &len);
+@@ -260,7 +260,7 @@ SocketFd::accept(rak::socket_address* sa) {
+ *sa = sa->sa_inet6()->normalize_address();
+ }
+
+- return SocketFd(fd);
++ return SocketFd(fd, m_ipv6_socket);
+ }
+
+ // unsigned int
+diff --git a/src/net/socket_fd.h b/src/net/socket_fd.h
+index ca765e88..2329b4e9 100644
+--- a/src/net/socket_fd.h
++++ b/src/net/socket_fd.h
+@@ -51,6 +51,7 @@ public:
+
+ SocketFd() : m_fd(-1) {}
+ explicit SocketFd(int fd) : m_fd(fd) {}
++ SocketFd(int fd, bool ipv6_socket) : m_fd(fd), m_ipv6_socket(ipv6_socket) {}
+
+ bool is_valid() const { return m_fd >= 0; }
+
new file mode 100644
@@ -0,0 +1,27 @@
+From 1890bde5c051d932d1b2940e815d0c82964c474c Mon Sep 17 00:00:00 2001
+From: Gleb Smirnoff <glebius@FreeBSD.org>
+Date: Tue, 2 Oct 2018 18:57:43 -0700
+Subject: [PATCH] If during socket creation AF_INET6 failes initialize sockaddr
+ as AF_INET. Otherwise any bind(2) would fail due to sockaddr address family
+ not matching socket address family.
+
+---
+ src/net/listen.cc | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/src/net/listen.cc b/src/net/listen.cc
+index da1c2e84..d424e94c 100644
+--- a/src/net/listen.cc
++++ b/src/net/listen.cc
+@@ -75,7 +75,10 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre
+
+ // TODO: Temporary until we refactor:
+ if (bindAddress->family() == 0) {
+- sa.sa_inet6()->clear();
++ if (m_ipv6_socket)
++ sa.sa_inet6()->clear();
++ else
++ sa.sa_inet()->clear();
+ } else {
+ sa.copy(*bindAddress, bindAddress->length());
+ }
new file mode 100644
@@ -0,0 +1,29 @@
+From cbd946b6cad8c93b3d39ab4f338b3640f684cbfc Mon Sep 17 00:00:00 2001
+From: Adam Fontenot <adam.m.fontenot@gmail.com>
+Date: Sat, 7 Jul 2018 16:52:07 -0700
+Subject: [PATCH] Fixes https://github.com/rakshasa/rtorrent/issues/731
+
+---
+ src/tracker/tracker_http.cc | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index eefe5a17..1bf94107 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -145,13 +145,13 @@ TrackerHttp::send_state(int state) {
+ if (!localAddress->is_address_any())
+ s << "&ip=" << localAddress->address_str();
+
+- if (localAddress->is_address_any() || localAddress->family() != rak::socket_address::pf_inet6) {
++ if (localAddress->is_address_any() && localAddress->family() == rak::socket_address::pf_inet) {
+ rak::socket_address local_v6;
+ if (get_local_address(rak::socket_address::af_inet6, &local_v6))
+ s << "&ipv6=" << rak::copy_escape_html(local_v6.address_str());
+ }
+
+- if (localAddress->is_address_any() || localAddress->family() != rak::socket_address::pf_inet) {
++ if (localAddress->is_address_any() && localAddress->family() == rak::socket_address::pf_inet6) {
+ rak::socket_address local_v4;
+ if (get_local_address(rak::socket_address::af_inet, &local_v4))
+ s << "&ipv4=" << local_v4.address_str();
new file mode 100644
@@ -0,0 +1,40 @@
+From d49316117401cff4045b1d1194cec60909e45555 Mon Sep 17 00:00:00 2001
+From: chros <chros@chrosGX620>
+Date: Sun, 13 May 2018 11:34:33 +0100
+Subject: [PATCH] Fix honoring throttle.min_peers* settings in rtorrent (See
+ #13)
+
+---
+ src/download/download_main.cc | 17 ++++++-----------
+ 1 file changed, 6 insertions(+), 11 deletions(-)
+
+diff --git a/src/download/download_main.cc b/src/download/download_main.cc
+index 48222c38..efe91d66 100644
+--- a/src/download/download_main.cc
++++ b/src/download/download_main.cc
+@@ -355,19 +355,14 @@ DownloadMain::receive_tracker_success() {
+
+ void
+ DownloadMain::receive_tracker_request() {
+- bool should_stop = false;
+- bool should_start = false;
++ if (info()->is_pex_enabled() && info()->size_pex() > 0
++ || connection_list()->size() + peer_list()->available_list()->size() / 2 >= connection_list()->min_size()) {
+
+- if (info()->is_pex_enabled() && info()->size_pex() > 0)
+- should_stop = true;
+-
+- if (connection_list()->size() + peer_list()->available_list()->size() / 2 < connection_list()->min_size())
+- should_start = true;
+-
+- if (should_stop)
+ m_tracker_controller->stop_requesting();
+- else if (should_start)
+- m_tracker_controller->start_requesting();
++ return;
++ }
++
++ m_tracker_controller->start_requesting();
+ }
+
+ struct SocketAddressCompact_less {
new file mode 100644
@@ -0,0 +1,22 @@
+From 9fd2f35f86397b9259e0c9b7c54444c0ec3c8965 Mon Sep 17 00:00:00 2001
+From: adam <adam@shinka.sh>
+Date: Mon, 26 Feb 2018 16:04:34 +0200
+Subject: [PATCH] increase piece length max
+
+---
+ src/download/download_constructor.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/download/download_constructor.cc b/src/download/download_constructor.cc
+index 1bb261bd..67ef4276 100644
+--- a/src/download/download_constructor.cc
++++ b/src/download/download_constructor.cc
+@@ -157,7 +157,7 @@ DownloadConstructor::parse_info(const Object& b) {
+ } else {
+ chunkSize = b.get_key_value("piece length");
+
+- if (chunkSize <= (1 << 10) || chunkSize > (128 << 20))
++ if (chunkSize <= (1 << 10) || chunkSize > (128 << 22))
+ throw input_error("Torrent has an invalid \"piece length\".");
+ }
+
new file mode 100644
@@ -0,0 +1,22 @@
+From b263b347f14b9d0ca54d04ca92367a98d791dc17 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Sun, 9 Jun 2019 14:53:32 +0900
+Subject: [PATCH] Set max piece size 512mb.
+
+---
+ src/download/download_constructor.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/download/download_constructor.cc b/src/download/download_constructor.cc
+index 67ef4276..1bf362fa 100644
+--- a/src/download/download_constructor.cc
++++ b/src/download/download_constructor.cc
+@@ -157,7 +157,7 @@ DownloadConstructor::parse_info(const Object& b) {
+ } else {
+ chunkSize = b.get_key_value("piece length");
+
+- if (chunkSize <= (1 << 10) || chunkSize > (128 << 22))
++ if (chunkSize <= (1 << 10) || chunkSize > (512 << 20))
+ throw input_error("Torrent has an invalid \"piece length\".");
+ }
+
new file mode 100644
@@ -0,0 +1,22 @@
+From cdfb4381bedb278dabb8ca75c10858d16b895355 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Sun, 9 Jun 2019 14:55:44 +0900
+Subject: [PATCH] Fixed compiler warning.
+
+---
+ src/download/download_main.cc | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/download/download_main.cc b/src/download/download_main.cc
+index efe91d66..9a3f9df2 100644
+--- a/src/download/download_main.cc
++++ b/src/download/download_main.cc
+@@ -355,7 +355,7 @@ DownloadMain::receive_tracker_success() {
+
+ void
+ DownloadMain::receive_tracker_request() {
+- if (info()->is_pex_enabled() && info()->size_pex() > 0
++ if ((info()->is_pex_enabled() && info()->size_pex()) > 0
+ || connection_list()->size() + peer_list()->available_list()->size() / 2 >= connection_list()->min_size()) {
+
+ m_tracker_controller->stop_requesting();
new file mode 100644
@@ -0,0 +1,74 @@
+From 8934703edb5982661483eb8a29d76e6a726b5fe2 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Mon, 8 Jul 2019 19:37:06 +0200
+Subject: [PATCH] Added _GNU_SOURCE to fallocate test. (neheb)
+
+---
+ scripts/checks.m4 | 10 ++++++----
+ src/data/socket_file.cc | 1 +
+ 2 files changed, 7 insertions(+), 4 deletions(-)
+
+diff --git a/scripts/checks.m4 b/scripts/checks.m4
+index c9333561..83be8461 100644
+--- a/scripts/checks.m4
++++ b/scripts/checks.m4
+@@ -88,7 +88,6 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
+ [
+ AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
+ AC_MSG_RESULT(yes)
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ], [
+ AC_MSG_RESULT(no)
+ ])
+@@ -97,7 +96,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
+ AC_DEFUN([TORRENT_CHECK_KQUEUE_SOCKET_ONLY], [
+ AC_MSG_CHECKING(whether kqueue supports pipes and ptys)
+
+- AC_RUN_IFELSE([AC_LANG_SOURCE([
++ AC_LINK_IFELSE([AC_LANG_SOURCE([
+ #include <fcntl.h>
+ #include <stdlib.h>
+ #include <unistd.h>
+@@ -138,6 +137,7 @@ AC_DEFUN([TORRENT_WITH_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
++ TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ])
+ ])
+@@ -149,9 +149,11 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
++ TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ], [
+ TORRENT_CHECK_KQUEUE
++ TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ])
+ ])
+
+@@ -172,8 +174,8 @@ AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
+ AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
+ AC_MSG_CHECKING(for fallocate)
+
+- AC_TRY_LINK([#include <fcntl.h>
+- #include <linux/falloc.h>
++ AC_TRY_LINK([#define _GNU_SOURCE
++ #include <fcntl.h>
+ ],[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
+ ],
+ [
+diff --git a/src/data/socket_file.cc b/src/data/socket_file.cc
+index 4b4519ed..b359ef8e 100644
+--- a/src/data/socket_file.cc
++++ b/src/data/socket_file.cc
+@@ -48,6 +48,7 @@
+ #include <sys/types.h>
+
+ #ifdef HAVE_FALLOCATE
++#define _GNU_SOURCE
+ #include <linux/falloc.h>
+ #endif
+
new file mode 100644
@@ -0,0 +1,293 @@
+From 856d42e2ca3c9810bf84a8bce219000e822540fa Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Fri, 12 Jul 2019 00:29:35 +0200
+Subject: [PATCH] Fixed diffie hellman implementation.
+
+---
+ src/utils/diffie_hellman.cc | 137 ++++++++++++------------------------
+ src/utils/diffie_hellman.h | 67 ++++--------------
+ 2 files changed, 59 insertions(+), 145 deletions(-)
+
+diff --git a/src/utils/diffie_hellman.cc b/src/utils/diffie_hellman.cc
+index 7ec13165..d53a857b 100644
+--- a/src/utils/diffie_hellman.cc
++++ b/src/utils/diffie_hellman.cc
+@@ -1,118 +1,79 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
++#include "diffie_hellman.h"
++
++#include "torrent/exceptions.h"
++
+ #include <cstring>
+-#include <string>
+
+ #ifdef USE_OPENSSL
++#include <openssl/dh.h>
+ #include <openssl/bn.h>
+ #endif
+
+-#include "diffie_hellman.h"
+-#include "torrent/exceptions.h"
+-
+ namespace torrent {
+
+-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+-DiffieHellman::DiffieHellman(const unsigned char *prime, int primeLength,
+- const unsigned char *generator, int generatorLength) :
+- m_secret(NULL), m_size(0) {
+-
+ #ifdef USE_OPENSSL
+
+- m_dh = DH_new();
+-
+-#ifdef USE_OPENSSL_1_1
+- BIGNUM * const dh_p = BN_bin2bn(prime, primeLength, NULL);
+- BIGNUM * const dh_g = BN_bin2bn(generator, generatorLength, NULL);
++static void dh_free(void* dh) { DH_free(reinterpret_cast<DH*>(dh)); }
++static DiffieHellman::dh_ptr dh_new() { return DiffieHellman::dh_ptr(reinterpret_cast<void*>(DH_new()), &dh_free); }
++static DH* dh_get(DiffieHellman::dh_ptr& dh) { return reinterpret_cast<DH*>(dh.get()); }
+
+- if (dh_p == NULL || dh_g == NULL ||
+- !DH_set0_pqg(m_dh, dh_p, NULL, dh_g))
+- throw internal_error("Could not generate Diffie-Hellman parameters");
++static bool
++dh_set_pg(DiffieHellman::dh_ptr& dh, BIGNUM* dh_p, BIGNUM* dh_g) {
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ return DH_set0_pqg(reinterpret_cast<DH*>(dh.get()), dh_p, nullptr, dh_g);
+ #else
+- m_dh->p = BN_bin2bn(prime, primeLength, NULL);
+- m_dh->g = BN_bin2bn(generator, generatorLength, NULL);
++ reinterpret_cast<DH*>(dh.get())->p = dh_p;
++ reinterpret_cast<DH*>(dh.get())->g = dh_g;
++ return true;
+ #endif
++}
+
+- DH_generate_key(m_dh);
+-
++static const BIGNUM* dh_get_pub_key(const DiffieHellman::dh_ptr& dh) {
++#if OPENSSL_VERSION_NUMBER >= 0x10100000L
++ const BIGNUM *pub_key;
++ DH_get0_key(reinterpret_cast<DH*>(dh.get()), &pub_key, nullptr);
++ return pub_key;
+ #else
+- throw internal_error("Compiled without encryption support.");
++ return dh != nullptr ? reinterpret_cast<DH*>(dh.get())->pub_key : nullptr;
+ #endif
+-};
++}
+
+-DiffieHellman::~DiffieHellman() {
+- delete [] m_secret;
+-#ifdef USE_OPENSSL
+- DH_free(m_dh);
++#else
++static DiffieHellman::dh_ptr dh_new() { throw internal_error("Compiled without encryption support."); }
++static void dh_free(void* dh) {}
++static void* dh_get_pub_key(const DiffieHellman::dh_ptr& dh) { return nullptr; }
+ #endif
+-};
+
+-bool
+-DiffieHellman::is_valid() const {
+-#ifdef USE_OPENSSL
+- if (m_dh == NULL)
+- return false;
++#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
++DiffieHellman::DiffieHellman(const unsigned char *prime, int primeLength,
++ const unsigned char *generator, int generatorLength) :
++ m_dh(dh_new()), m_size(0) {
+
+-#ifdef USE_OPENSSL_1_1
+- const BIGNUM *pub_key;
++#ifdef USE_OPENSSL
++ BIGNUM* dh_p = BN_bin2bn(prime, primeLength, nullptr);
++ BIGNUM* dh_g = BN_bin2bn(generator, generatorLength, nullptr);
+
+- DH_get0_key(m_dh, &pub_key, NULL);
++ if (dh_p == nullptr || dh_g == nullptr || !dh_set_pg(m_dh, dh_p, dh_g))
++ throw internal_error("Could not generate Diffie-Hellman parameters");
+
+- return pub_key != NULL;
+-#else
+- return m_dh != NULL && m_dh->pub_key != NULL;
++ DH_generate_key(dh_get(m_dh));
+ #endif
++};
+
+-#else
+- return false;
+-#endif
++bool
++DiffieHellman::is_valid() const {
++ return dh_get_pub_key(m_dh) != nullptr;
+ }
+
+ bool
+ DiffieHellman::compute_secret(const unsigned char *pubkey, unsigned int length) {
+ #ifdef USE_OPENSSL
+- BIGNUM* k = BN_bin2bn(pubkey, length, NULL);
++ BIGNUM* k = BN_bin2bn(pubkey, length, nullptr);
+
+- delete [] m_secret;
+- m_secret = new char[DH_size(m_dh)];
+-
+- m_size = DH_compute_key((unsigned char*)m_secret, k, m_dh);
++ m_secret.reset(new char[DH_size(dh_get(m_dh))]);
++ m_size = DH_compute_key(reinterpret_cast<unsigned char*>(m_secret.get()), k, dh_get(m_dh));
+
+ BN_free(k);
+
+@@ -124,16 +85,10 @@ DiffieHellman::compute_secret(const unsigned char *pubkey, unsigned int length)
+
+ void
+ DiffieHellman::store_pub_key(unsigned char* dest, unsigned int length) {
+-#ifdef USE_OPENSSL
+ std::memset(dest, 0, length);
+
+- const BIGNUM *pub_key;
+-
+-#ifdef USE_OPENSSL_1_1
+- DH_get0_key(m_dh, &pub_key, NULL);
+-#else
+- pub_key = m_dh->pub_key;
+-#endif
++#ifdef USE_OPENSSL
++ const BIGNUM *pub_key = dh_get_pub_key(m_dh);
+
+ if ((int)length >= BN_num_bytes(pub_key))
+ BN_bn2bin(pub_key, dest + length - BN_num_bytes(pub_key));
+diff --git a/src/utils/diffie_hellman.h b/src/utils/diffie_hellman.h
+index 432542be..2cec5bee 100644
+--- a/src/utils/diffie_hellman.h
++++ b/src/utils/diffie_hellman.h
+@@ -1,79 +1,38 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_DIFFIE_HELLMAN_H
+ #define LIBTORRENT_DIFFIE_HELLMAN_H
+
+ #include "config.h"
+
++#include <memory>
+ #include <string>
+
+-#ifdef USE_OPENSSL
+-#include <openssl/dh.h>
+-#endif
+-
+ namespace torrent {
+
+ class DiffieHellman {
+ public:
++ typedef std::unique_ptr<char[]> secret_ptr;
++ typedef std::unique_ptr<void, void (*)(void*)> dh_ptr;
++
+ DiffieHellman(const unsigned char prime[], int primeLength,
+ const unsigned char generator[], int generatorLength);
+- ~DiffieHellman();
+
+- bool compute_secret(const unsigned char pubkey[], unsigned int length);
+- void store_pub_key(unsigned char* dest, unsigned int length);
++ bool is_valid() const;
+
+- bool is_valid() const;
++ bool compute_secret(const unsigned char pubkey[], unsigned int length);
++ void store_pub_key(unsigned char* dest, unsigned int length);
+
+- unsigned int size() const { return m_size; }
++ unsigned int size() const { return m_size; }
+
+- const char* c_str() const { return m_secret; }
+- std::string secret_str() const { return std::string(m_secret, m_size); }
++ const char* c_str() const { return m_secret.get(); }
++ std::string secret_str() const { return std::string(m_secret.get(), m_size); }
+
+ private:
+ DiffieHellman(const DiffieHellman& dh);
+ DiffieHellman& operator = (const DiffieHellman& dh);
+
+-#ifdef USE_OPENSSL
+- DH* m_dh;
+-#else
+- void* m_void;
+-#endif
+- char* m_secret;
+- unsigned int m_size;
++ dh_ptr m_dh;
++ secret_ptr m_secret;
++ int m_size;
+ };
+
+ };
new file mode 100644
@@ -0,0 +1,64 @@
+From 660bbc17e32c518c2727607ee5b73039c7109207 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Sat, 13 Jul 2019 00:07:43 +0200
+Subject: [PATCH] Increased max timeout for tracker requests.
+
+---
+ src/torrent/tracker.h | 40 ++--------------------------------------
+ 1 file changed, 2 insertions(+), 38 deletions(-)
+
+diff --git a/src/torrent/tracker.h b/src/torrent/tracker.h
+index 2b00ad47..a528ef6a 100644
+--- a/src/torrent/tracker.h
++++ b/src/torrent/tracker.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_TRACKER_H
+ #define LIBTORRENT_TRACKER_H
+
+@@ -148,8 +112,8 @@ protected:
+
+ void set_group(uint32_t v) { m_group = v; }
+
+- void set_normal_interval(int v) { m_normal_interval = std::min(std::max(600, v), 3600); }
+- void set_min_interval(int v) { m_min_interval = std::min(std::max(300, v), 1800); }
++ void set_normal_interval(int v) { m_normal_interval = std::min(std::max(600, v), 8 * 3600); }
++ void set_min_interval(int v) { m_min_interval = std::min(std::max(300, v), 4 * 3600); }
+
+ int m_flags;
+
new file mode 100644
@@ -0,0 +1,123 @@
+From efc75948253c1a8db482daabf45d9eabaaf4b099 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Wed, 17 Jul 2019 19:41:04 +0200
+Subject: [PATCH] Close log files when reusing a name.
+
+---
+ src/torrent/utils/log.cc | 20 ++++++++++++++++----
+ src/torrent/utils/log.h | 7 +++++++
+ test/torrent/tracker_timeout_test.cc | 8 ++++----
+ test/torrent/utils/log_test.cc | 12 ++++++------
+ 4 files changed, 33 insertions(+), 14 deletions(-)
+
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index 58b563a6..5169a730 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -294,12 +294,16 @@ log_open_output(const char* name, log_slot slot) {
+ throw input_error("Cannot open more than 64 log output handlers.");
+ }
+
+- if (log_find_output_name(name) != log_outputs.end()) {
+- pthread_mutex_unlock(&log_mutex);
+- throw input_error("Log name already used.");
++ log_output_list::iterator itr = log_find_output_name(name);
++
++ if (itr == log_outputs.end()) {
++ log_outputs.push_back(std::make_pair(name, slot));
++ } else {
++ // by replacing the "write" slot binding, the old file gets closed
++ // (handles are shared pointers)
++ itr->second = slot;
+ }
+
+- log_outputs.push_back(std::make_pair(name, slot));
+ log_rebuild_cache();
+
+ pthread_mutex_unlock(&log_mutex);
+@@ -307,6 +311,14 @@ log_open_output(const char* name, log_slot slot) {
+
+ void
+ log_close_output(const char* name) {
++ pthread_mutex_lock(&log_mutex);
++
++ log_output_list::iterator itr = log_find_output_name(name);
++
++ if (itr != log_outputs.end())
++ log_outputs.erase(itr);
++
++ pthread_mutex_unlock(&log_mutex);
+ }
+
+ void
+diff --git a/src/torrent/utils/log.h b/src/torrent/utils/log.h
+index a053d6ec..430bda5e 100644
+--- a/src/torrent/utils/log.h
++++ b/src/torrent/utils/log.h
+@@ -229,6 +229,7 @@ void log_cleanup() LIBTORRENT_EXPORT;
+
+ void log_open_output(const char* name, log_slot slot) LIBTORRENT_EXPORT;
+ void log_close_output(const char* name) LIBTORRENT_EXPORT;
++void log_close_output_str(const std::string name) LIBTORRENT_EXPORT;
+
+ void log_add_group_output(int group, const char* name) LIBTORRENT_EXPORT;
+ void log_remove_group_output(int group, const char* name) LIBTORRENT_EXPORT;
+@@ -240,6 +241,12 @@ void log_open_file_output(const char* name, const char* filename) LIBTORR
+ void log_open_gz_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT;
+ log_buffer* log_open_log_buffer(const char* name) LIBTORRENT_EXPORT;
+
++//
++// Implementation:
++//
++
++inline void log_close_output_str(const std::string name) { log_close_output(name.c_str()); }
++
+ }
+
+ #endif
+diff --git a/test/torrent/tracker_timeout_test.cc b/test/torrent/tracker_timeout_test.cc
+index 081b9301..cd060006 100644
+--- a/test/torrent/tracker_timeout_test.cc
++++ b/test/torrent/tracker_timeout_test.cc
+@@ -29,13 +29,13 @@ tracker_timeout_test::test_set_timeout() {
+
+ tracker.set_new_normal_interval(100);
+ CPPUNIT_ASSERT(tracker.normal_interval() == 600);
+- tracker.set_new_normal_interval(4000);
+- CPPUNIT_ASSERT(tracker.normal_interval() == 3600);
++ tracker.set_new_normal_interval(8 * 4000);
++ CPPUNIT_ASSERT(tracker.normal_interval() == 8 * 3600);
+
+ tracker.set_new_min_interval(100);
+ CPPUNIT_ASSERT(tracker.min_interval() == 300);
+- tracker.set_new_min_interval(4000);
+- CPPUNIT_ASSERT(tracker.min_interval() == 1800);
++ tracker.set_new_min_interval(4 * 4000);
++ CPPUNIT_ASSERT(tracker.min_interval() == 4 * 3600);
+ }
+
+ void
+diff --git a/test/torrent/utils/log_test.cc b/test/torrent/utils/log_test.cc
+index 9b99c245..24c22b59 100644
+--- a/test/torrent/utils/log_test.cc
++++ b/test/torrent/utils/log_test.cc
+@@ -75,13 +75,13 @@ utils_log_test::test_output_open() {
+ // Test inserting duplicate names, should catch.
+ // CPPUNIT_ASSERT_THROW(torrent::log_open_output("test_output_1", torrent::log_slot());, torrent::input_error);
+
+- try {
+- torrent::log_open_output("test_output_1", torrent::log_slot());
+- } catch (torrent::input_error& e) {
+- return;
+- }
++ // try {
++ // torrent::log_open_output("test_output_1", torrent::log_slot());
++ // } catch (torrent::input_error& e) {
++ // return;
++ // }
+
+- CPPUNIT_ASSERT(false);
++ // CPPUNIT_ASSERT(false);
+
+ // Test more than 64 entries.
+ }
new file mode 100644
@@ -0,0 +1,30 @@
+From 7faa9c58ce098bbdeff83b6add72f3075b47881d Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Fri, 19 Jul 2019 13:38:12 +0200
+Subject: [PATCH] Bumped to version 0.13.8.
+
+---
+ configure.ac | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index b885714d..4ed08124 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1,12 +1,12 @@
+-AC_INIT(libtorrent, 0.13.7, sundell.software@gmail.com)
++AC_INIT(libtorrent, 0.13.8, sundell.software@gmail.com)
+
+ LT_INIT([disable-static])
+
+ dnl Find a better way to do this
+-AC_DEFINE(PEER_NAME, "-lt0D70-", Identifier that is part of the default peer id)
+-AC_DEFINE(PEER_VERSION, "lt\x0D\x70", 4 byte client and version identifier for DHT)
++AC_DEFINE(PEER_NAME, "-lt0D80-", Identifier that is part of the default peer id)
++AC_DEFINE(PEER_VERSION, "lt\x0D\x80", 4 byte client and version identifier for DHT)
+
+-LIBTORRENT_CURRENT=20
++LIBTORRENT_CURRENT=21
+ LIBTORRENT_REVISION=0
+ LIBTORRENT_AGE=0
+
new file mode 100644
@@ -0,0 +1,109 @@
+From 7667094274879fe158e718bf2765d35f82d924bd Mon Sep 17 00:00:00 2001
+From: nicholi <nschell@gmail.com>
+Date: Tue, 23 Jul 2019 23:59:16 -0700
+Subject: [PATCH] Allow logs to be appended rather than overwritten.
+
+---
+ src/torrent/utils/log.cc | 13 +++++++++++++
+ src/torrent/utils/log.h | 1 +
+ test/torrent/utils/log_test.cc | 35 ++++++++++++++++++++++++++++++++++
+ test/torrent/utils/log_test.h | 2 ++
+ 4 files changed, 51 insertions(+)
+
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index 5169a730..a900c109 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -428,6 +428,19 @@ log_open_file_output(const char* name, const char* filename) {
+ std::placeholders::_3));
+ }
+
++void
++log_open_file_output_append(const char* name, const char* filename) {
++ std::shared_ptr<std::ofstream> outfile(new std::ofstream(filename, std::ofstream::out | std::ofstream::app));
++
++ if (!outfile->good())
++ throw input_error("Could not open log file '" + std::string(filename) + "'.");
++
++ log_open_output(name, std::bind(&log_file_write, outfile,
++ std::placeholders::_1,
++ std::placeholders::_2,
++ std::placeholders::_3));
++}
++
+ void
+ log_open_gz_file_output(const char* name, const char* filename) {
+ std::shared_ptr<log_gz_output> outfile(new log_gz_output(filename));
+diff --git a/src/torrent/utils/log.h b/src/torrent/utils/log.h
+index 430bda5e..531c8565 100644
+--- a/src/torrent/utils/log.h
++++ b/src/torrent/utils/log.h
+@@ -238,6 +238,7 @@ void log_add_child(int group, int child) LIBTORRENT_EXPORT;
+ void log_remove_child(int group, int child) LIBTORRENT_EXPORT;
+
+ void log_open_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT;
++void log_open_file_output_append(const char* name, const char* filename) LIBTORRENT_EXPORT;
+ void log_open_gz_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT;
+ log_buffer* log_open_log_buffer(const char* name) LIBTORRENT_EXPORT;
+
+diff --git a/test/torrent/utils/log_test.cc b/test/torrent/utils/log_test.cc
+index 24c22b59..aa13fff8 100644
+--- a/test/torrent/utils/log_test.cc
++++ b/test/torrent/utils/log_test.cc
+@@ -155,3 +155,38 @@ utils_log_test::test_file_output() {
+
+ CPPUNIT_ASSERT_MESSAGE(buffer, std::string(buffer).find("test_file") != std::string::npos);
+ }
++
++void
++utils_log_test::test_file_output_append() {
++ std::string filename = "utils_log_test.XXXXXX";
++
++ mktemp(&*filename.begin());
++
++ torrent::log_open_file_output_append("test_file", filename.c_str());
++ torrent::log_add_group_output(GROUP_PARENT_1, "test_file");
++
++ lt_log_print(GROUP_PARENT_1, "test_line_1");
++
++ torrent::log_cleanup(); // To ensure we flush the buffers.
++
++ // re-open and write 2nd line
++ torrent::log_open_file_output_append("test_file", filename.c_str());
++ torrent::log_add_group_output(GROUP_PARENT_1, "test_file");
++
++ lt_log_print(GROUP_PARENT_1, "test_line_2");
++
++ torrent::log_cleanup(); // To ensure we flush the buffers.
++
++ std::ifstream temp_file(filename.c_str());
++
++ CPPUNIT_ASSERT(temp_file.good());
++
++ char buffer_line1[256];
++ temp_file.getline(buffer_line1, 256);
++
++ char buffer_line2[256];
++ temp_file.getline(buffer_line2, 256);
++
++ CPPUNIT_ASSERT_MESSAGE(buffer_line1, std::string(buffer_line1).find("test_line_1") != std::string::npos);
++ CPPUNIT_ASSERT_MESSAGE(buffer_line2, std::string(buffer_line2).find("test_line_2") != std::string::npos);
++}
+diff --git a/test/torrent/utils/log_test.h b/test/torrent/utils/log_test.h
+index 3a66cc24..d4cb3bc6 100644
+--- a/test/torrent/utils/log_test.h
++++ b/test/torrent/utils/log_test.h
+@@ -10,6 +10,7 @@ class utils_log_test : public CppUnit::TestFixture {
+ CPPUNIT_TEST(test_print);
+ CPPUNIT_TEST(test_children);
+ CPPUNIT_TEST(test_file_output);
++ CPPUNIT_TEST(test_file_output_append);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+@@ -22,4 +23,5 @@ public:
+ void test_print();
+ void test_children();
+ void test_file_output();
++ void test_file_output_append();
+ };
new file mode 100644
@@ -0,0 +1,101 @@
+From df54913c34c8b584d6d2072a65ad1590766780c5 Mon Sep 17 00:00:00 2001
+From: nicholi <nschell@gmail.com>
+Date: Fri, 26 Jul 2019 00:50:52 -0700
+Subject: [PATCH] Removed log append function. Added append parameter with
+ default value (false) to log_open_file functions.
+
+---
+ src/torrent/utils/log.cc | 26 ++++++++------------------
+ src/torrent/utils/log.h | 5 ++---
+ test/torrent/utils/log_test.cc | 4 ++--
+ 3 files changed, 12 insertions(+), 23 deletions(-)
+
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index a900c109..6c605474 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -73,7 +73,7 @@ struct log_cache_entry {
+ };
+
+ struct log_gz_output {
+- log_gz_output(const char* filename) { gz_file = gzopen(filename, "w"); }
++ log_gz_output(const char* filename, bool append) { gz_file = gzopen(filename, append ? "a" : "w"); }
+ ~log_gz_output() { if (gz_file != NULL) gzclose(gz_file); }
+
+ bool is_valid() { return gz_file != Z_NULL; }
+@@ -416,8 +416,11 @@ log_gz_file_write(std::shared_ptr<log_gz_output>& outfile, const char* data, siz
+ }
+
+ void
+-log_open_file_output(const char* name, const char* filename) {
+- std::shared_ptr<std::ofstream> outfile(new std::ofstream(filename));
++log_open_file_output(const char* name, const char* filename, bool append) {
++ std::ios_base::openmode mode = std::ofstream::out;
++ if (append)
++ mode |= std::ofstream::app;
++ std::shared_ptr<std::ofstream> outfile(new std::ofstream(filename, mode));
+
+ if (!outfile->good())
+ throw input_error("Could not open log file '" + std::string(filename) + "'.");
+@@ -429,21 +432,8 @@ log_open_file_output(const char* name, const char* filename) {
+ }
+
+ void
+-log_open_file_output_append(const char* name, const char* filename) {
+- std::shared_ptr<std::ofstream> outfile(new std::ofstream(filename, std::ofstream::out | std::ofstream::app));
+-
+- if (!outfile->good())
+- throw input_error("Could not open log file '" + std::string(filename) + "'.");
+-
+- log_open_output(name, std::bind(&log_file_write, outfile,
+- std::placeholders::_1,
+- std::placeholders::_2,
+- std::placeholders::_3));
+-}
+-
+-void
+-log_open_gz_file_output(const char* name, const char* filename) {
+- std::shared_ptr<log_gz_output> outfile(new log_gz_output(filename));
++log_open_gz_file_output(const char* name, const char* filename, bool append) {
++ std::shared_ptr<log_gz_output> outfile(new log_gz_output(filename, append));
+
+ if (!outfile->is_valid())
+ throw input_error("Could not open log gzip file '" + std::string(filename) + "'.");
+diff --git a/src/torrent/utils/log.h b/src/torrent/utils/log.h
+index 531c8565..0dfdc86b 100644
+--- a/src/torrent/utils/log.h
++++ b/src/torrent/utils/log.h
+@@ -237,9 +237,8 @@ void log_remove_group_output(int group, const char* name) LIBTORRENT_EXPORT;
+ void log_add_child(int group, int child) LIBTORRENT_EXPORT;
+ void log_remove_child(int group, int child) LIBTORRENT_EXPORT;
+
+-void log_open_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT;
+-void log_open_file_output_append(const char* name, const char* filename) LIBTORRENT_EXPORT;
+-void log_open_gz_file_output(const char* name, const char* filename) LIBTORRENT_EXPORT;
++void log_open_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
++void log_open_gz_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
+ log_buffer* log_open_log_buffer(const char* name) LIBTORRENT_EXPORT;
+
+ //
+diff --git a/test/torrent/utils/log_test.cc b/test/torrent/utils/log_test.cc
+index aa13fff8..9f975636 100644
+--- a/test/torrent/utils/log_test.cc
++++ b/test/torrent/utils/log_test.cc
+@@ -162,7 +162,7 @@ utils_log_test::test_file_output_append() {
+
+ mktemp(&*filename.begin());
+
+- torrent::log_open_file_output_append("test_file", filename.c_str());
++ torrent::log_open_file_output("test_file", filename.c_str(), false);
+ torrent::log_add_group_output(GROUP_PARENT_1, "test_file");
+
+ lt_log_print(GROUP_PARENT_1, "test_line_1");
+@@ -170,7 +170,7 @@ utils_log_test::test_file_output_append() {
+ torrent::log_cleanup(); // To ensure we flush the buffers.
+
+ // re-open and write 2nd line
+- torrent::log_open_file_output_append("test_file", filename.c_str());
++ torrent::log_open_file_output("test_file", filename.c_str(), true);
+ torrent::log_add_group_output(GROUP_PARENT_1, "test_file");
+
+ lt_log_print(GROUP_PARENT_1, "test_line_2");
new file mode 100644
@@ -0,0 +1,7365 @@
+From b0f945e11d6afe43c917b58291c6fbcf5468a908 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Fri, 23 Aug 2019 23:23:48 +0900
+Subject: [PATCH] Backport changes from feature-bind. (#200)
+
+---
+ .dir-locals.el | 7 +
+ Makefile.am | 1 +
+ configure.ac | 40 +-
+ extra/corrupt_file.cc | 2 +-
+ rak/file_stat.h | 2 +-
+ rak/fs_stat.h | 2 +-
+ rak/partial_queue.h | 2 +-
+ rak/path.h | 2 +-
+ rak/priority_queue_default.h | 2 +-
+ rak/socket_address.h | 49 +-
+ rak/timer.h | 2 +-
+ scripts/checks.m4 | 4 +-
+ scripts/rak_cxx.m4 | 47 --
+ scripts/ssl.m4 | 38 ++
+ src/data/chunk_list.cc | 2 -
+ src/data/chunk_list.h | 2 +-
+ src/data/chunk_list_node.h | 2 +-
+ src/data/hash_check_queue.h | 2 +-
+ src/data/hash_queue.cc | 2 -
+ src/data/hash_queue.h | 2 +-
+ src/data/hash_queue_node.h | 4 +-
+ src/data/hash_torrent.cc | 2 -
+ src/data/hash_torrent.h | 4 +-
+ src/data/memory_chunk.h | 2 +-
+ src/data/socket_file.h | 2 +-
+ src/dht/dht_hash_map.h | 57 +-
+ src/download/chunk_selector.h | 2 +-
+ src/download/chunk_statistics.h | 2 +-
+ src/download/delegator.cc | 2 +-
+ src/download/delegator.h | 2 +-
+ src/download/download_constructor.h | 2 +-
+ src/globals.cc | 15 +-
+ src/globals.h | 36 --
+ src/manager.cc | 36 --
+ src/net/Makefile.am | 2 +
+ src/net/data_buffer.h | 2 +-
+ src/net/listen.cc | 40 +-
+ src/net/listen.h | 4 +-
+ src/net/protocol_buffer.h | 2 +-
+ src/net/socket_base.h | 2 +-
+ src/net/socket_fd.cc | 10 +
+ src/net/socket_fd.h | 6 +
+ src/net/socket_listen.cc | 137 +++++
+ src/net/socket_listen.h | 46 ++
+ src/net/socket_set.h | 2 +-
+ src/net/throttle_node.h | 2 +-
+ src/protocol/handshake.cc | 13 +-
+ src/protocol/handshake_manager.cc | 56 +-
+ src/protocol/handshake_manager.h | 40 +-
+ src/protocol/peer_connection_base.cc | 2 -
+ src/protocol/request_list.cc | 38 +-
+ src/torrent/Makefile.am | 1 +
+ src/torrent/common.h | 5 +-
+ src/torrent/connection_manager.h | 2 +-
+ src/torrent/data/download_data.h | 2 +-
+ src/torrent/data/file_list.cc | 2 -
+ src/torrent/data/transfer_list.h | 3 +-
+ src/torrent/download.cc | 4 +-
+ src/torrent/download/choke_group.cc | 2 +-
+ src/torrent/download/choke_group.h | 2 +-
+ src/torrent/download/choke_queue.cc | 3 +-
+ src/torrent/download/choke_queue.h | 5 +-
+ src/torrent/download/group_entry.h | 3 +-
+ src/torrent/download/resource_manager.cc | 1 -
+ src/torrent/download/resource_manager.h | 2 +-
+ src/torrent/download_info.h | 4 +-
+ src/torrent/error.cc | 6 +-
+ src/torrent/error.h | 7 +-
+ src/torrent/event.cc | 19 +
+ src/torrent/event.h | 82 ++-
+ src/torrent/http.h | 2 +-
+ src/torrent/net/Makefile.am | 18 +-
+ src/torrent/net/address_info.cc | 43 ++
+ src/torrent/net/address_info.h | 69 +++
+ src/torrent/net/fd.cc | 209 +++++++
+ src/torrent/net/fd.h | 63 ++
+ src/torrent/net/socket_address.cc | 559 ++++++++++++++++++
+ src/torrent/net/socket_address.h | 229 +++++++
+ src/torrent/net/socket_address_key.h | 2 +-
+ src/torrent/net/socket_event.cc | 29 +
+ src/torrent/net/socket_event.h | 31 +
+ src/torrent/net/types.h | 33 ++
+ src/torrent/object.h | 21 +-
+ src/torrent/peer/client_list.cc | 2 +-
+ src/torrent/peer/connection_list.h | 3 +-
+ src/torrent/peer/peer_list.cc | 7 +-
+ src/torrent/poll.h | 3 +-
+ src/torrent/torrent.cc | 42 +-
+ src/torrent/torrent.h | 11 +-
+ src/torrent/tracker.h | 2 +-
+ src/torrent/tracker_controller.h | 3 +-
+ src/torrent/tracker_list.h | 2 +-
+ src/torrent/utils/Makefile.am | 5 +-
+ src/torrent/utils/directory_events.h | 3 +-
+ src/torrent/utils/log.cc | 66 +--
+ src/torrent/utils/log.h | 98 ++-
+ src/torrent/utils/log_buffer.cc | 55 +-
+ src/torrent/utils/log_buffer.h | 52 +-
+ src/torrent/utils/net.cc | 72 ---
+ src/torrent/utils/net.h | 56 --
+ src/torrent/utils/option_strings.cc | 63 +-
+ src/torrent/utils/option_strings.h | 7 +-
+ src/torrent/utils/random.cc | 29 +
+ src/torrent/utils/random.h | 15 +
+ src/torrent/utils/ranges.h | 1 -
+ src/torrent/utils/resume.cc | 2 -
+ src/torrent/utils/signal_bitfield.h | 3 +-
+ src/torrent/utils/thread_base.h | 3 +-
+ src/utils/instrumentation.cc | 2 -
+ src/utils/instrumentation.h | 3 +-
+ src/utils/queue_buckets.h | 4 +-
+ src/utils/sha_fast.h | 2 +-
+ test/Makefile.am | 29 +-
+ test/data/hash_check_queue_test.cc | 2 +-
+ test/data/hash_queue_test.cc | 2 +-
+ test/helpers/expect_fd.h | 107 ++++
+ test/helpers/expect_utils.h | 13 +
+ test/helpers/mock_compare.h | 96 +++
+ test/helpers/mock_function.cc | 170 ++++++
+ test/helpers/mock_function.h | 133 +++++
+ test/helpers/network.h | 182 ++++++
+ test/helpers/progress_listener.cc | 63 ++
+ test/helpers/progress_listener.h | 47 ++
+ test/helpers/test_fixture.cc | 18 +
+ test/helpers/test_fixture.h | 14 +
+ test/helpers/utils.h | 60 ++
+ test/main.cc | 82 ++-
+ test/net/test_socket_listen.cc | 398 +++++++++++++
+ test/net/test_socket_listen.h | 44 ++
+ test/torrent/net/test_address_info.cc | 62 ++
+ test/torrent/net/test_address_info.h | 19 +
+ test/torrent/net/test_fd.cc | 24 +
+ test/torrent/net/test_fd.h | 12 +
+ test/torrent/net/test_socket_address.cc | 383 ++++++++++++
+ test/torrent/net/test_socket_address.h | 43 ++
+ test/torrent/net/test_socket_address_key.cc | 87 ---
+ test/torrent/object_stream_test.cc | 4 +-
+ test/torrent/tracker_controller_features.cc | 2 +-
+ test/torrent/tracker_controller_requesting.cc | 2 +-
+ test/torrent/tracker_controller_test.cc | 2 +-
+ test/torrent/tracker_list_features_test.cc | 2 +-
+ test/torrent/utils/directory_events_test.cc | 4 +-
+ test/torrent/utils/log_buffer_test.h | 17 -
+ test/torrent/utils/log_test.cc | 4 +-
+ test/torrent/utils/net_test.cc | 32 -
+ test/torrent/utils/net_test.h | 15 -
+ test/torrent/utils/option_strings_test.cc | 3 +-
+ test/torrent/utils/test_extents.cc | 2 +-
+ ...{log_buffer_test.cc => test_log_buffer.cc} | 14 +-
+ test/torrent/utils/test_log_buffer.h | 17 +
+ test/torrent/utils/test_uri_parser.cc | 2 +-
+ test/torrent/utils/thread_base_test.cc | 3 +-
+ 152 files changed, 3968 insertions(+), 1110 deletions(-)
+ create mode 100644 .dir-locals.el
+ create mode 100644 scripts/ssl.m4
+ create mode 100644 src/net/socket_listen.cc
+ create mode 100644 src/net/socket_listen.h
+ create mode 100644 src/torrent/event.cc
+ create mode 100644 src/torrent/net/address_info.cc
+ create mode 100644 src/torrent/net/address_info.h
+ create mode 100644 src/torrent/net/fd.cc
+ create mode 100644 src/torrent/net/fd.h
+ create mode 100644 src/torrent/net/socket_address.cc
+ create mode 100644 src/torrent/net/socket_address.h
+ create mode 100644 src/torrent/net/socket_event.cc
+ create mode 100644 src/torrent/net/socket_event.h
+ create mode 100644 src/torrent/net/types.h
+ delete mode 100644 src/torrent/utils/net.cc
+ delete mode 100644 src/torrent/utils/net.h
+ create mode 100644 src/torrent/utils/random.cc
+ create mode 100644 src/torrent/utils/random.h
+ create mode 100644 test/helpers/expect_fd.h
+ create mode 100644 test/helpers/expect_utils.h
+ create mode 100644 test/helpers/mock_compare.h
+ create mode 100644 test/helpers/mock_function.cc
+ create mode 100644 test/helpers/mock_function.h
+ create mode 100644 test/helpers/network.h
+ create mode 100644 test/helpers/progress_listener.cc
+ create mode 100644 test/helpers/progress_listener.h
+ create mode 100644 test/helpers/test_fixture.cc
+ create mode 100644 test/helpers/test_fixture.h
+ create mode 100644 test/helpers/utils.h
+ create mode 100644 test/net/test_socket_listen.cc
+ create mode 100644 test/net/test_socket_listen.h
+ create mode 100644 test/torrent/net/test_address_info.cc
+ create mode 100644 test/torrent/net/test_address_info.h
+ create mode 100644 test/torrent/net/test_fd.cc
+ create mode 100644 test/torrent/net/test_fd.h
+ create mode 100644 test/torrent/net/test_socket_address.cc
+ create mode 100644 test/torrent/net/test_socket_address.h
+ delete mode 100644 test/torrent/net/test_socket_address_key.cc
+ delete mode 100644 test/torrent/utils/log_buffer_test.h
+ delete mode 100644 test/torrent/utils/net_test.cc
+ delete mode 100644 test/torrent/utils/net_test.h
+ rename test/torrent/utils/{log_buffer_test.cc => test_log_buffer.cc} (86%)
+ create mode 100644 test/torrent/utils/test_log_buffer.h
+
+diff --git a/.dir-locals.el b/.dir-locals.el
+new file mode 100644
+index 00000000..af1189f2
+--- /dev/null
++++ b/.dir-locals.el
+@@ -0,0 +1,7 @@
++;;; Directory Local Variables
++;;; For more information see (info "(emacs) Directory Variables")
++
++((c++-mode
++ (flycheck-clang-language-standard . "c++11")
++ (flycheck-gcc-language-standard . "c++11")))
++
+diff --git a/Makefile.am b/Makefile.am
+index f175e634..9507b9ea 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -8,6 +8,7 @@ EXTRA_DIST= \
+ scripts/checks.m4 \
+ scripts/common.m4 \
+ scripts/attributes.m4 \
++ scripts/ssl.m4 \
+ doc/main.xml \
+ doc/http.xml \
+ doc/torrent.xml \
+diff --git a/configure.ac b/configure.ac
+index 4ed08124..620ca552 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -17,7 +17,7 @@ AC_SUBST(LIBTORRENT_CURRENT)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_INFO)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_NO)
+
+-AM_INIT_AUTOMAKE
++AM_INIT_AUTOMAKE([serial-tests])
+ AC_CONFIG_HEADERS(config.h)
+
+ AC_PROG_CXX
+@@ -35,7 +35,6 @@ RAK_ENABLE_EXTRA_DEBUG
+ RAK_ENABLE_WERROR
+
+ RAK_CHECK_CXX11
+-RAK_CHECK_TR1_LIB
+
+ AC_SYS_LARGEFILE
+
+@@ -67,41 +66,8 @@ CFLAGS="$PTHREAD_CFLAGS $CPPUNIT_CFLAGS $CFLAGS"
+ CXXFLAGS="$PTHREAD_CFLAGS $CPPUNIT_CFLAGS $CXXFLAGS"
+ LIBS="$PTHREAD_LIBS $CPPUNIT_LIBS $LIBS"
+
+-AC_ARG_ENABLE(openssl,
+- [ --disable-openssl Don't use OpenSSL's SHA1 implementation.],
+- [
+- if test "$enableval" = "yes"; then
+-dnl move to scripts.
+- PKG_CHECK_MODULES(OPENSSL, libcrypto,
+- CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS";
+- LIBS="$LIBS $OPENSSL_LIBS")
+-
+- AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
+- AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
+- AC_CHECK_LIB([crypto], [DH_set0_pqg], [AC_DEFINE(USE_OPENSSL_1_1, 1, Using OpenSSL 1.1.)])
+-
+- else
+- AC_DEFINE(USE_NSS_SHA, 1, Using Mozilla's SHA1 implementation.)
+- fi
+- ],[
+- PKG_CHECK_MODULES(OPENSSL, libcrypto,
+- CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS";
+- LIBS="$LIBS $OPENSSL_LIBS")
+-
+- AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
+- AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
+- AC_CHECK_LIB([crypto], [DH_set0_pqg], [AC_DEFINE(USE_OPENSSL_1_1, 1, Using OpenSSL 1.1.)])
+- ]
+-)
+-
+-AC_ARG_ENABLE(cyrus-rc4,
+- [ --enable-cyrus-rc4=PFX Use Cyrus RC4 implementation.],
+- [
+- CXXFLAGS="$CXXFLAGS -I${enableval}/include";
+- LIBS="$LIBS -lrc4 -L${enableval}/lib"
+- AC_DEFINE(USE_CYRUS_RC4, 1, Using Cyrus RC4 implementation.)
+- ]
+-)
++TORRENT_ARG_OPENSSL
++TORRENT_ARG_CYRUS_RC4
+
+ AC_CHECK_FUNCS(posix_memalign)
+
+diff --git a/extra/corrupt_file.cc b/extra/corrupt_file.cc
+index 2a818cc7..7ae906e6 100644
+--- a/extra/corrupt_file.cc
++++ b/extra/corrupt_file.cc
+@@ -1,6 +1,6 @@
+ #include <iostream>
+ #include <stdexcept>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <fcntl.h>
+diff --git a/rak/file_stat.h b/rak/file_stat.h
+index 5ad45e8f..f1ad8c2b 100644
+--- a/rak/file_stat.h
++++ b/rak/file_stat.h
+@@ -38,7 +38,7 @@
+ #define RAK_FILE_STAT_H
+
+ #include <string>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <sys/stat.h>
+
+ namespace rak {
+diff --git a/rak/fs_stat.h b/rak/fs_stat.h
+index 5e844277..2d73ff1b 100644
+--- a/rak/fs_stat.h
++++ b/rak/fs_stat.h
+@@ -38,7 +38,7 @@
+ #define RAK_FS_STAT_H
+
+ #include <string>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ #include <rak/error_number.h>
+
+diff --git a/rak/partial_queue.h b/rak/partial_queue.h
+index 6650a633..1abfdddf 100644
+--- a/rak/partial_queue.h
++++ b/rak/partial_queue.h
+@@ -39,7 +39,7 @@
+
+ #include <cstring>
+ #include <stdexcept>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ namespace rak {
+
+diff --git a/rak/path.h b/rak/path.h
+index bfe8ccc1..64daf355 100644
+--- a/rak/path.h
++++ b/rak/path.h
+@@ -99,7 +99,7 @@ path_expand(const char* src, char* first, char* last) {
+ src++;
+ }
+
+- return std::max(first + strlcpy(first, src, std::distance(first, last)), last);
++ return std::min(first + strlcpy(first, src, std::distance(first, last)), last);
+ }
+
+ }
+diff --git a/rak/priority_queue_default.h b/rak/priority_queue_default.h
+index 01a0070e..a7bba0ce 100644
+--- a/rak/priority_queue_default.h
++++ b/rak/priority_queue_default.h
+@@ -37,7 +37,7 @@
+ #ifndef RAK_PRIORITY_QUEUE_DEFAULT_H
+ #define RAK_PRIORITY_QUEUE_DEFAULT_H
+
+-#include lt_tr1_functional
++#include <functional>
+ #include <rak/allocators.h>
+ #include <rak/priority_queue.h>
+ #include <rak/timer.h>
+diff --git a/rak/socket_address.h b/rak/socket_address.h
+index 961c53b2..8eb60116 100644
+--- a/rak/socket_address.h
++++ b/rak/socket_address.h
+@@ -47,9 +47,12 @@
+ #ifndef RAK_SOCKET_ADDRESS_H
+ #define RAK_SOCKET_ADDRESS_H
+
++#include <cinttypes>
++#include <cstdint>
+ #include <cstring>
+-#include <string>
+ #include <stdexcept>
++#include <string>
++
+ #include <arpa/inet.h>
+ #include <netinet/in.h>
+ #include <sys/types.h>
+@@ -84,7 +87,6 @@ public:
+
+ bool is_valid_inet_class() const { return family() == af_inet || family() == af_inet6; }
+
+- // Should we need to set AF_UNSPEC?
+ void clear() { std::memset(this, 0, sizeof(socket_address)); set_family(); }
+
+ sa_family_t family() const { return m_sockaddr.sa_family; }
+@@ -124,6 +126,7 @@ public:
+ // extranous bytes and ensure it does not go beyond the size of this
+ // struct.
+ void copy(const socket_address& src, size_t length);
++ void copy_sockaddr(const sockaddr* src);
+
+ static socket_address* cast_from(sockaddr* sa) { return reinterpret_cast<socket_address*>(sa); }
+ static const socket_address* cast_from(const sockaddr* sa) { return reinterpret_cast<const socket_address*>(sa); }
+@@ -220,6 +223,8 @@ public:
+
+ void set_address_any() { set_port(0); set_address(in6addr_any); }
+
++ std::string pretty_address_str() const;
++
+ sa_family_t family() const { return m_sockaddr.sin6_family; }
+ void set_family() { m_sockaddr.sin6_family = AF_INET6; }
+
+@@ -340,7 +345,7 @@ socket_address::pretty_address_str() const {
+ case af_inet:
+ return sa_inet()->address_str();
+ case af_inet6:
+- return sa_inet6()->address_str();
++ return sa_inet6()->pretty_address_str();
+ case af_unspec:
+ return std::string("unspec");
+ default:
+@@ -380,13 +385,16 @@ socket_address::length() const {
+ inline void
+ socket_address::copy(const socket_address& src, size_t length) {
+ length = std::min(length, sizeof(socket_address));
+-
+- // Does this get properly optimized?
++
+ std::memset(this, 0, sizeof(socket_address));
+ std::memcpy(this, &src, length);
+ }
+
+-// Should we be able to compare af_unspec?
++inline void
++socket_address::copy_sockaddr(const sockaddr* src) {
++ std::memset(this, 0, sizeof(socket_address));
++ std::memcpy(this, src, socket_address::cast_from(src)->length());
++}
+
+ inline bool
+ socket_address::operator == (const socket_address& rhs) const {
+@@ -488,6 +496,35 @@ socket_address_inet6::set_address_c_str(const char* a) {
+ return inet_pton(AF_INET6, a, &m_sockaddr.sin6_addr);
+ }
+
++inline std::string
++socket_address_inet6::pretty_address_str() const {
++ char buf[INET6_ADDRSTRLEN + 2 + 6];
++
++ if (inet_ntop(family(), &m_sockaddr.sin6_addr, buf + 1, INET6_ADDRSTRLEN) == NULL)
++ return std::string();
++
++ buf[0] = '[';
++
++ char* last_char = (char*)std::memchr(buf + 1, 0, INET6_ADDRSTRLEN);
++
++ // TODO: Throw exception here.
++
++ if (last_char == NULL || last_char >= buf + 1 + INET6_ADDRSTRLEN)
++ throw std::logic_error("inet_ntop for inet6 returned bad buffer");
++
++ *(last_char++) = ']';
++
++ if (!is_port_any()) {
++ if (snprintf(last_char, 7, ":%" PRIu16, port()) == -1)
++ return std::string("error"); // TODO: Throw here.
++
++ } else {
++ *last_char = '\0';
++ }
++
++ return std::string(buf);
++}
++
+ inline socket_address
+ socket_address_inet6::normalize_address() const {
+ const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(m_sockaddr.sin6_addr.s6_addr);
+diff --git a/rak/timer.h b/rak/timer.h
+index e25ad2e6..842a2e53 100644
+--- a/rak/timer.h
++++ b/rak/timer.h
+@@ -38,7 +38,7 @@
+ #define RAK_TIMER_H
+
+ #include <limits>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <sys/time.h>
+
+ namespace rak {
+diff --git a/scripts/checks.m4 b/scripts/checks.m4
+index 83be8461..98ef17f8 100644
+--- a/scripts/checks.m4
++++ b/scripts/checks.m4
+@@ -88,6 +88,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE], [
+ [
+ AC_DEFINE(USE_KQUEUE, 1, Use kqueue.)
+ AC_MSG_RESULT(yes)
++ TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ], [
+ AC_MSG_RESULT(no)
+ ])
+@@ -137,7 +138,6 @@ AC_DEFUN([TORRENT_WITH_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ])
+ ])
+@@ -149,11 +149,9 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ fi
+ ], [
+ TORRENT_CHECK_KQUEUE
+- TORRENT_CHECK_KQUEUE_SOCKET_ONLY
+ ])
+ ])
+
+diff --git a/scripts/rak_cxx.m4 b/scripts/rak_cxx.m4
+index 3660f3a7..0db61b83 100644
+--- a/scripts/rak_cxx.m4
++++ b/scripts/rak_cxx.m4
+@@ -12,50 +12,3 @@ AC_DEFUN([RAK_CHECK_CXX11], [
+ ]
+ )
+ ])
+-
+-AC_DEFUN([RAK_CHECK_TR1_LIB], [
+- AC_LANG_PUSH(C++)
+- AC_MSG_CHECKING(should use TR1 headers)
+-
+- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+- #include <unordered_map>
+- class Foo; typedef std::unordered_map<Foo*, int> Bar;
+- Bar b1;
+- ])
+- ], [
+- AC_MSG_RESULT(no)
+- AC_DEFINE(USE_TR1_LIB, 0, Define to 1 if you need to use TR1 containers.)
+-
+- AC_DEFINE([lt_tr1_array], [<array>], [TR1 array])
+- AC_DEFINE([lt_tr1_functional], [<functional>], [TR1 functional])
+- AC_DEFINE([lt_tr1_memory], [<memory>], [TR1 memory])
+- AC_DEFINE([lt_tr1_unordered_map], [<unordered_map>], [TR1 unordered_map])
+-
+- ], [
+- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+- #include <tr1/unordered_map>
+- class Foo; typedef std::tr1::unordered_map<Foo*, int> Bar;
+- Bar b1;
+- ])
+- ], [
+- AC_MSG_RESULT([yes])
+- AC_DEFINE(USE_TR1_LIB, 1, Define to 1 if you need to use TR1 containers.)
+-
+- AC_DEFINE([lt_tr1_array], [<tr1/array>], [TR1 array])
+- AC_DEFINE([lt_tr1_functional], [<tr1/functional>], [TR1 functional])
+- AC_DEFINE([lt_tr1_memory], [<tr1/memory>], [TR1 memory])
+- AC_DEFINE([lt_tr1_unordered_map], [<tr1/unordered_map>], [TR1 unordered_map])
+-
+- ], [
+- AC_MSG_ERROR([No support for C++11 standard library nor TR1 extensions found.])
+- ])
+- ])
+-
+- AH_VERBATIM(lt_tr1_zzz, [
+-#if USE_TR1_LIB == 1
+-namespace std { namespace tr1 {} using namespace tr1; }
+-#endif
+-])
+-
+- AC_LANG_POP(C++)
+-])
+diff --git a/scripts/ssl.m4 b/scripts/ssl.m4
+new file mode 100644
+index 00000000..f07349a1
+--- /dev/null
++++ b/scripts/ssl.m4
+@@ -0,0 +1,38 @@
++AC_DEFUN([TORRENT_CHECK_OPENSSL],
++ [
++ PKG_CHECK_MODULES(OPENSSL, libcrypto,
++ CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS";
++ LIBS="$LIBS $OPENSSL_LIBS")
++
++ AC_DEFINE(USE_OPENSSL, 1, Using OpenSSL.)
++ AC_DEFINE(USE_OPENSSL_SHA, 1, Using OpenSSL's SHA1 implementation.)
++ ]
++)
++
++AC_DEFUN([TORRENT_ARG_OPENSSL],
++ [
++ AC_ARG_ENABLE(openssl,
++ [ --disable-openssl Don't use OpenSSL's SHA1 implementation.],
++ [
++ if test "$enableval" = "yes"; then
++ TORRENT_CHECK_OPENSSL
++ else
++ AC_DEFINE(USE_NSS_SHA, 1, Using Mozilla's SHA1 implementation.)
++ fi
++ ],[
++ TORRENT_CHECK_OPENSSL
++ ])
++ ]
++)
++
++AC_DEFUN([TORRENT_ARG_CYRUS_RC4],
++ [
++ AC_ARG_ENABLE(cyrus-rc4,
++ [ --enable-cyrus-rc4=PFX Use Cyrus RC4 implementation.],
++ [
++ CXXFLAGS="$CXXFLAGS -I${enableval}/include";
++ LIBS="$LIBS -lrc4 -L${enableval}/lib"
++ AC_DEFINE(USE_CYRUS_RC4, 1, Using Cyrus RC4 implementation.)
++ ])
++ ]
++)
+diff --git a/src/data/chunk_list.cc b/src/data/chunk_list.cc
+index 00abbc2a..7622b825 100644
+--- a/src/data/chunk_list.cc
++++ b/src/data/chunk_list.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include <rak/error_number.h>
+ #include <rak/functional.h>
+
+diff --git a/src/data/chunk_list.h b/src/data/chunk_list.h
+index b6ff4cdb..4b40dc42 100644
+--- a/src/data/chunk_list.h
++++ b/src/data/chunk_list.h
+@@ -37,9 +37,9 @@
+ #ifndef LIBTORRENT_DATA_CHUNK_LIST_H
+ #define LIBTORRENT_DATA_CHUNK_LIST_H
+
++#include <functional>
+ #include <string>
+ #include <vector>
+-#include lt_tr1_functional
+
+ #include "chunk.h"
+ #include "chunk_handle.h"
+diff --git a/src/data/chunk_list_node.h b/src/data/chunk_list_node.h
+index 76de6671..95e4ed4f 100644
+--- a/src/data/chunk_list_node.h
++++ b/src/data/chunk_list_node.h
+@@ -37,7 +37,7 @@
+ #ifndef LIBTORRENT_DATA_CHUNK_LIST_NODE_H
+ #define LIBTORRENT_DATA_CHUNK_LIST_NODE_H
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <rak/timer.h>
+
+ namespace torrent {
+diff --git a/src/data/hash_check_queue.h b/src/data/hash_check_queue.h
+index b933f137..9f28c118 100644
+--- a/src/data/hash_check_queue.h
++++ b/src/data/hash_check_queue.h
+@@ -38,8 +38,8 @@
+ #define LIBTORRENT_DATA_HASH_CHECK_QUEUE_H
+
+ #include <deque>
++#include <functional>
+ #include <pthread.h>
+-#include lt_tr1_functional
+
+ #include "rak/allocators.h"
+
+diff --git a/src/data/hash_queue.cc b/src/data/hash_queue.cc
+index 7dffaee4..3f54892b 100644
+--- a/src/data/hash_queue.cc
++++ b/src/data/hash_queue.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include <functional>
+ #include <rak/functional.h>
+ #include <unistd.h>
+diff --git a/src/data/hash_queue.h b/src/data/hash_queue.h
+index 6885a383..3a841967 100644
+--- a/src/data/hash_queue.h
++++ b/src/data/hash_queue.h
+@@ -38,9 +38,9 @@
+ #define LIBTORRENT_DATA_HASH_QUEUE_H
+
+ #include <deque>
++#include <functional>
+ #include <map>
+ #include <pthread.h>
+-#include lt_tr1_functional
+
+ #include "torrent/hash_string.h"
+ #include "hash_queue_node.h"
+diff --git a/src/data/hash_queue_node.h b/src/data/hash_queue_node.h
+index aa59a062..c8367b7c 100644
+--- a/src/data/hash_queue_node.h
++++ b/src/data/hash_queue_node.h
+@@ -37,9 +37,9 @@
+ #ifndef LIBTORRENT_DATA_HASH_QUEUE_NODE_H
+ #define LIBTORRENT_DATA_HASH_QUEUE_NODE_H
+
++#include <cinttypes>
++#include <functional>
+ #include <string>
+-#include lt_tr1_functional
+-#include <inttypes.h>
+
+ #include "chunk_handle.h"
+ #include "hash_chunk.h"
+diff --git a/src/data/hash_torrent.cc b/src/data/hash_torrent.cc
+index 758a10fa..e803f1cb 100644
+--- a/src/data/hash_torrent.cc
++++ b/src/data/hash_torrent.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include "data/chunk_list.h"
+ #include "torrent/exceptions.h"
+ #include "torrent/data/download_data.h"
+diff --git a/src/data/hash_torrent.h b/src/data/hash_torrent.h
+index 6af643f2..9ce2f100 100644
+--- a/src/data/hash_torrent.h
++++ b/src/data/hash_torrent.h
+@@ -37,9 +37,9 @@
+ #ifndef LIBTORRENT_DATA_HASH_TORRENT_H
+ #define LIBTORRENT_DATA_HASH_TORRENT_H
+
++#include <cinttypes>
++#include <functional>
+ #include <string>
+-#include <inttypes.h>
+-#include lt_tr1_functional
+ #include <rak/priority_queue_default.h>
+
+ #include "data/chunk_handle.h"
+diff --git a/src/data/memory_chunk.h b/src/data/memory_chunk.h
+index cc32eff1..d2b5565d 100644
+--- a/src/data/memory_chunk.h
++++ b/src/data/memory_chunk.h
+@@ -38,7 +38,7 @@
+ #define LIBTORRENT_DATA_MEMORY_CHUNK_H
+
+ #include <algorithm>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <sys/mman.h>
+ #include <cstddef>
+
+diff --git a/src/data/socket_file.h b/src/data/socket_file.h
+index 7b27af8c..d25c4a44 100644
+--- a/src/data/socket_file.h
++++ b/src/data/socket_file.h
+@@ -38,7 +38,7 @@
+ #define LIBTORRENT_SOCKET_FILE_H
+
+ #include <string>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <fcntl.h>
+ #include <sys/types.h>
+
+diff --git a/src/dht/dht_hash_map.h b/src/dht/dht_hash_map.h
+index 140f070b..1506db56 100644
+--- a/src/dht/dht_hash_map.h
++++ b/src/dht/dht_hash_map.h
+@@ -39,20 +39,14 @@
+
+ #include "config.h"
+
+-#if HAVE_TR1
+-#include <lt_tr1_unordered_map>
+-#else
+-#include <map>
+-#endif
+-
+-#include "torrent/hash_string.h"
++#include <unordered_map>
+
+ #include "dht_node.h"
+ #include "dht_tracker.h"
++#include "torrent/hash_string.h"
+
+ namespace torrent {
+
+-#if HAVE_TR1
+ // Hash functions for HashString keys, and dereferencing HashString pointers.
+
+ // Since the first few bits are very similar if not identical (since the IDs
+@@ -142,53 +136,6 @@ public:
+
+ };
+
+-#else
+-
+-// Compare HashString pointers by dereferencing them.
+-struct hashstring_ptr_less : public std::binary_function<const HashString*, const HashString*, bool> {
+- size_t operator () (const HashString* one, const HashString* two) const
+- { return *one < *two; }
+-};
+-
+-class DhtNodeList : public std::map<const HashString*, DhtNode*, hashstring_ptr_less> {
+-public:
+- typedef std::map<const HashString*, DhtNode*, hashstring_ptr_less> base_type;
+-
+- // Define accessor iterator with more convenient access to the key and
+- // element values. Allows changing the map definition more easily if needed.
+- template<typename T>
+- struct accessor_wrapper : public T {
+- accessor_wrapper(const T& itr) : T(itr) { }
+-
+- const HashString& id() const { return *(**this).first; }
+- DhtNode* node() const { return (**this).second; }
+- };
+-
+- typedef accessor_wrapper<const_iterator> const_accessor;
+- typedef accessor_wrapper<iterator> accessor;
+-
+- DhtNode* add_node(DhtNode* n);
+-
+-};
+-
+-class DhtTrackerList : public std::map<HashString, DhtTracker*> {
+-public:
+- typedef std::map<HashString, DhtTracker*> base_type;
+-
+- template<typename T>
+- struct accessor_wrapper : public T {
+- accessor_wrapper(const T& itr) : T(itr) { }
+-
+- const HashString& id() const { return (**this).first; }
+- DhtTracker* tracker() const { return (**this).second; }
+- };
+-
+- typedef accessor_wrapper<const_iterator> const_accessor;
+- typedef accessor_wrapper<iterator> accessor;
+-
+-};
+-#endif // HAVE_TR1
+-
+ inline
+ DhtNode* DhtNodeList::add_node(DhtNode* n) {
+ insert(std::make_pair((const HashString*)n, (DhtNode*)n));
+diff --git a/src/download/chunk_selector.h b/src/download/chunk_selector.h
+index 52c31fd5..ab1b8c17 100644
+--- a/src/download/chunk_selector.h
++++ b/src/download/chunk_selector.h
+@@ -37,7 +37,7 @@
+ #ifndef LIBTORRENT_DOWNLOAD_CHUNK_SELECTOR_H
+ #define LIBTORRENT_DOWNLOAD_CHUNK_SELECTOR_H
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <rak/partial_queue.h>
+
+ #include "torrent/bitfield.h"
+diff --git a/src/download/chunk_statistics.h b/src/download/chunk_statistics.h
+index 816ec6c9..62f76c5a 100644
+--- a/src/download/chunk_statistics.h
++++ b/src/download/chunk_statistics.h
+@@ -37,7 +37,7 @@
+ #ifndef LIBTORRENT_DOWNLOAD_CHUNK_STATISTICS_H
+ #define LIBTORRENT_DOWNLOAD_CHUNK_STATISTICS_H
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <vector>
+
+ namespace torrent {
+diff --git a/src/download/delegator.cc b/src/download/delegator.cc
+index 711cd461..27ae42ec 100644
+--- a/src/download/delegator.cc
++++ b/src/download/delegator.cc
+@@ -39,7 +39,7 @@
+ #include "config.h"
+
+ #include <algorithm>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ #include "torrent/exceptions.h"
+ #include "torrent/bitfield.h"
+diff --git a/src/download/delegator.h b/src/download/delegator.h
+index 3b997b81..b75d4c2a 100644
+--- a/src/download/delegator.h
++++ b/src/download/delegator.h
+@@ -37,9 +37,9 @@
+ #ifndef LIBTORRENT_DELEGATOR_H
+ #define LIBTORRENT_DELEGATOR_H
+
++#include <functional>
+ #include <string>
+ #include <vector>
+-#include lt_tr1_functional
+
+ #include "torrent/data/transfer_list.h"
+
+diff --git a/src/download/download_constructor.h b/src/download/download_constructor.h
+index 7d43aba3..6a09b7f1 100644
+--- a/src/download/download_constructor.h
++++ b/src/download/download_constructor.h
+@@ -39,7 +39,7 @@
+
+ #include <list>
+ #include <string>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ namespace torrent {
+
+diff --git a/src/globals.cc b/src/globals.cc
+index 88130c19..20644fbd 100644
+--- a/src/globals.cc
++++ b/src/globals.cc
+@@ -37,11 +37,24 @@
+ #include "config.h"
+
+ #include "globals.h"
+-#include "torrent/common.h"
++#include "manager.h"
++#include "torrent/connection_manager.h"
++#include "torrent/event.h"
++#include "torrent/poll.h"
+
+ namespace torrent {
+
+ LIBTORRENT_EXPORT rak::priority_queue_default taskScheduler;
+ LIBTORRENT_EXPORT rak::timer cachedTime;
+
++void poll_event_open(Event* event) { manager->poll()->open(event); manager->connection_manager()->inc_socket_count(); }
++void poll_event_close(Event* event) { manager->poll()->close(event); manager->connection_manager()->dec_socket_count(); }
++void poll_event_closed(Event* event) { manager->poll()->closed(event); manager->connection_manager()->dec_socket_count(); }
++void poll_event_insert_read(Event* event) { manager->poll()->insert_read(event); }
++void poll_event_insert_write(Event* event) { manager->poll()->insert_write(event); }
++void poll_event_insert_error(Event* event) { manager->poll()->insert_error(event); }
++void poll_event_remove_read(Event* event) { manager->poll()->remove_read(event); }
++void poll_event_remove_write(Event* event) { manager->poll()->remove_write(event); }
++void poll_event_remove_error(Event* event) { manager->poll()->remove_error(event); }
++
+ }
+diff --git a/src/globals.h b/src/globals.h
+index 564ac86d..e9fe1177 100644
+--- a/src/globals.h
++++ b/src/globals.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_GLOBALS_H
+ #define LIBTORRENT_GLOBALS_H
+
+diff --git a/src/manager.cc b/src/manager.cc
+index c503974d..11ef4b0f 100644
+--- a/src/manager.cc
++++ b/src/manager.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include "torrent/exceptions.h"
+diff --git a/src/net/Makefile.am b/src/net/Makefile.am
+index fb4da4f3..e3a8c7e1 100644
+--- a/src/net/Makefile.am
++++ b/src/net/Makefile.am
+@@ -15,6 +15,8 @@ libsub_net_la_SOURCES = \
+ socket_datagram.h \
+ socket_fd.cc \
+ socket_fd.h \
++ socket_listen.cc \
++ socket_listen.h \
+ socket_set.cc \
+ socket_set.h \
+ socket_stream.cc \
+diff --git a/src/net/data_buffer.h b/src/net/data_buffer.h
+index d35cdc48..5dd0cb30 100644
+--- a/src/net/data_buffer.h
++++ b/src/net/data_buffer.h
+@@ -38,7 +38,7 @@
+ #define LIBTORRENT_NET_DATA_BUFFER_H
+
+ #include <memory>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ namespace torrent {
+
+diff --git a/src/net/listen.cc b/src/net/listen.cc
+index d424e94c..61fedbf8 100644
+--- a/src/net/listen.cc
++++ b/src/net/listen.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #define __STDC_FORMAT_MACROS
+@@ -95,7 +59,7 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre
+ manager->poll()->insert_read(this);
+ manager->poll()->insert_error(this);
+
+- lt_log_print(LOG_CONNECTION_INFO, "listen port %" PRIu16 " opened with backlog set to %i",
++ lt_log_print(LOG_CONNECTION_LISTEN, "listen port %" PRIu16 " opened with backlog set to %i",
+ m_port, backlog);
+
+ return true;
+@@ -107,7 +71,7 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre
+ get_fd().close();
+ get_fd().clear();
+
+- lt_log_print(LOG_CONNECTION_INFO, "failed to open listen port");
++ lt_log_print(LOG_CONNECTION_LISTEN, "failed to open listen port");
+
+ return false;
+ }
+diff --git a/src/net/listen.h b/src/net/listen.h
+index b3c845aa..58c06c7e 100644
+--- a/src/net/listen.h
++++ b/src/net/listen.h
+@@ -37,8 +37,8 @@
+ #ifndef LIBTORRENT_LISTEN_H
+ #define LIBTORRENT_LISTEN_H
+
+-#include <inttypes.h>
+-#include lt_tr1_functional
++#include <cinttypes>
++#include <functional>
+ #include <rak/socket_address.h>
+
+ #include "socket_base.h"
+diff --git a/src/net/protocol_buffer.h b/src/net/protocol_buffer.h
+index b64d47ea..f9711ded 100644
+--- a/src/net/protocol_buffer.h
++++ b/src/net/protocol_buffer.h
+@@ -38,7 +38,7 @@
+ #define LIBTORRENT_NET_PROTOCOL_BUFFER_H
+
+ #include <memory>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <netinet/in.h>
+
+ #include "torrent/exceptions.h"
+diff --git a/src/net/socket_base.h b/src/net/socket_base.h
+index 02c9497d..20ae1d9f 100644
+--- a/src/net/socket_base.h
++++ b/src/net/socket_base.h
+@@ -38,7 +38,7 @@
+ #define LIBTORRENT_NET_SOCKET_BASE_H
+
+ #include <list>
+-#include <inttypes.h>
++#include <cinttypes>
+
+ #include "torrent/event.h"
+ #include "socket_fd.h"
+diff --git a/src/net/socket_fd.cc b/src/net/socket_fd.cc
+index f04059f6..c36ff4b9 100644
+--- a/src/net/socket_fd.cc
++++ b/src/net/socket_fd.cc
+@@ -210,6 +210,11 @@ SocketFd::bind(const rak::socket_address& sa, unsigned int length) {
+ return !::bind(m_fd, sa.c_sockaddr(), length);
+ }
+
++bool
++SocketFd::bind_sa(const sockaddr* sa) {
++ return bind(*rak::socket_address::cast_from(sa));
++}
++
+ bool
+ SocketFd::connect(const rak::socket_address& sa) {
+ check_valid();
+@@ -222,6 +227,11 @@ SocketFd::connect(const rak::socket_address& sa) {
+ return !::connect(m_fd, sa.c_sockaddr(), sa.length()) || errno == EINPROGRESS;
+ }
+
++bool
++SocketFd::connect_sa(const sockaddr* sa) {
++ return connect(*rak::socket_address::cast_from(sa));
++}
++
+ bool
+ SocketFd::getsockname(rak::socket_address *sa) {
+ check_valid();
+diff --git a/src/net/socket_fd.h b/src/net/socket_fd.h
+index 2329b4e9..4db0087b 100644
+--- a/src/net/socket_fd.h
++++ b/src/net/socket_fd.h
+@@ -39,6 +39,8 @@
+
+ #include <unistd.h>
+
++struct sockaddr;
++
+ namespace rak {
+ class socket_address;
+ }
+@@ -80,7 +82,11 @@ public:
+
+ bool bind(const rak::socket_address& sa);
+ bool bind(const rak::socket_address& sa, unsigned int length);
++ bool bind_sa(const sockaddr* sa);
++
+ bool connect(const rak::socket_address& sa);
++ bool connect_sa(const sockaddr* sa);
++
+ bool getsockname(rak::socket_address* sa);
+
+ bool listen(int size);
+diff --git a/src/net/socket_listen.cc b/src/net/socket_listen.cc
+new file mode 100644
+index 00000000..97f006e0
+--- /dev/null
++++ b/src/net/socket_listen.cc
+@@ -0,0 +1,137 @@
++#include "config.h"
++
++#include "socket_listen.h"
++
++#include <algorithm>
++
++#include "torrent/connection_manager.h"
++#include "torrent/exceptions.h"
++#include "torrent/utils/log.h"
++#include "torrent/utils/random.h"
++
++#define LT_LOG_SAP(log_fmt, sap, ...) \
++ lt_log_print(LOG_CONNECTION_LISTEN, "listen->%s: " log_fmt, sap_pretty_str(sap).c_str(), __VA_ARGS__);
++
++namespace torrent {
++
++socket_listen::socket_listen() : m_backlog(SOMAXCONN) {
++}
++
++void
++socket_listen::set_backlog(int backlog) {
++ if (backlog < 0 || backlog > SOMAXCONN)
++ throw internal_error("Could not set socket_listen backlog, out-of-range value.");
++
++ m_backlog = backlog;
++}
++
++bool
++socket_listen::open(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, uint16_t start_port, fd_flags open_flags) {
++ if (is_open())
++ throw internal_error("socket_listen::open: already open");
++
++ if (!(sap_is_inet(sap) || sap_is_inet6(sap)) || sap_is_v4mapped(sap) || !sap_is_port_any(sap) || sap_is_broadcast(sap))
++ throw internal_error("socket_listen::open: socket address must be inet/inet6 with no port, and not v4mapped nor broadcast: " + sap_pretty_str(sap));
++
++ if (sap_is_inet(sap) && !(open_flags & fd_flag_v4only))
++ throw internal_error("socket_listen::open: socket address is inet without v4only flag");
++
++ if (first_port == 0 || last_port == 0 || start_port == 0 ||
++ !(first_port <= last_port && first_port <= start_port && start_port <= last_port))
++ throw internal_error("socket_listen::open: port range not valid");
++
++ int fd = fd_open(open_flags);
++
++ if (fd == -1) {
++ LT_LOG_SAP("open failed (flags:0x%x errno:%i message:'%s')", sap, open_flags, errno, std::strerror(errno));
++ return false;
++ }
++
++ uint16_t p = start_port;
++
++ do {
++ if (m_open_port(fd, sap, p))
++ return is_open();
++
++ if (p == last_port)
++ p = first_port;
++ else
++ p++;
++ } while (p != start_port);
++
++ LT_LOG_SAP("listen ports exhausted (fd:%i first_port:%" PRIu16 " last_port:%" PRIu16 ")",
++ sap, fd, first_port, last_port);
++ fd_close(fd);
++ return false;
++}
++
++bool
++socket_listen::open_randomize(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, fd_flags open_flags) {
++ if (last_port < first_port)
++ throw internal_error("socket_listen::open_randomize: port range not valid");
++
++ return open(std::move(sap), first_port, last_port, random_uniform_uint16(first_port, last_port), open_flags);
++}
++
++bool
++socket_listen::open_sequential(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, fd_flags open_flags) {
++ return open(std::move(sap), first_port, last_port, first_port, open_flags);
++}
++
++void
++socket_listen::close() {
++ if (!is_open())
++ return;
++
++ torrent::poll_event_closed(this);
++
++ fd_close(file_descriptor());
++ set_file_descriptor(-1);
++ m_socket_address.reset();
++}
++
++void
++socket_listen::event_read() {
++}
++
++void
++socket_listen::event_error() {
++}
++
++// Returns true if open is successful or if we cannot bind to the
++// address, returns false if other ports can be used.
++bool
++socket_listen::m_open_port(int fd, sa_unique_ptr& sap, uint16_t port) {
++ sap_set_port(sap, port);
++
++ if (!fd_bind(fd, sap.get())) {
++ if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT) {
++ LT_LOG_SAP("listen address not usable (fd:%i errno:%i message:'%s')",
++ sap, fd, errno, std::strerror(errno));
++ fd_close(fd);
++ return true;
++ }
++
++ return false;
++ }
++
++ if (!fd_listen(fd, m_backlog)) {
++ LT_LOG_SAP("call to listen failed (fd:%i backlog:%i errno:%i message:'%s')",
++ sap, fd, m_backlog, errno, std::strerror(errno));
++ fd_close(fd);
++ return true;
++ }
++
++ LT_LOG_SAP("open listen port success (fd:%i backlog:%i)", sap, fd, m_backlog);
++
++ m_fileDesc = fd;
++ m_socket_address.swap(sap);
++
++ torrent::poll_event_open(this);
++ torrent::poll_event_insert_read(this);
++ torrent::poll_event_insert_error(this);
++
++ return true;
++}
++
++}
+diff --git a/src/net/socket_listen.h b/src/net/socket_listen.h
+new file mode 100644
+index 00000000..817d5fa5
+--- /dev/null
++++ b/src/net/socket_listen.h
+@@ -0,0 +1,46 @@
++#ifndef LIBTORRENT_SOCKET_LISTEN_H
++#define LIBTORRENT_SOCKET_LISTEN_H
++
++#include <cinttypes>
++#include <functional>
++
++#include "torrent/net/fd.h"
++#include "torrent/net/socket_address.h"
++#include "torrent/net/socket_event.h"
++
++namespace torrent {
++
++class socket_listen : public socket_event {
++public:
++ typedef std::function<void (int, sa_unique_ptr)> accepted_ftor;
++
++ socket_listen();
++
++ int backlog() const;
++
++ void set_backlog(int backlog);
++ void set_slot_accepted(accepted_ftor&& ftor);
++
++ bool open(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, uint16_t start_port, fd_flags open_flags);
++ bool open_randomize(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, fd_flags open_flags);
++ bool open_sequential(sa_unique_ptr&& sap, uint16_t first_port, uint16_t last_port, fd_flags open_flags);
++ void close();
++
++ void event_read() override;
++ void event_error() override;
++
++ const char* type_name() const override { return "socket_listen"; }
++
++private:
++ bool m_open_port(int fd, sa_unique_ptr& sap, uint16_t port);
++
++ int m_backlog;
++ accepted_ftor m_slot_accepted;
++};
++
++inline int socket_listen::backlog() const { return m_backlog; }
++inline void socket_listen::set_slot_accepted(accepted_ftor&& ftor) { m_slot_accepted = ftor; }
++
++}
++
++#endif
+diff --git a/src/net/socket_set.h b/src/net/socket_set.h
+index 9264edf7..78443c88 100644
+--- a/src/net/socket_set.h
++++ b/src/net/socket_set.h
+@@ -39,7 +39,7 @@
+
+ #include <list>
+ #include <vector>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <rak/allocators.h>
+
+ #include "torrent/exceptions.h"
+diff --git a/src/net/throttle_node.h b/src/net/throttle_node.h
+index 77cb6cc7..5af27a22 100644
+--- a/src/net/throttle_node.h
++++ b/src/net/throttle_node.h
+@@ -37,7 +37,7 @@
+ #ifndef LIBTORRENT_NET_THROTTLE_NODE_H
+ #define LIBTORRENT_NET_THROTTLE_NODE_H
+
+-#include lt_tr1_functional
++#include <functional>
+
+ #include "torrent/rate.h"
+
+diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc
+index 6b41bbe3..1b877c7a 100644
+--- a/src/protocol/handshake.cc
++++ b/src/protocol/handshake.cc
+@@ -46,6 +46,7 @@
+ #include "torrent/error.h"
+ #include "torrent/poll.h"
+ #include "torrent/throttle.h"
++#include "torrent/utils/log.h"
+ #include "utils/diffie_hellman.h"
+
+ #include "globals.h"
+@@ -55,6 +56,10 @@
+ #include "handshake.h"
+ #include "handshake_manager.h"
+
++#define LT_LOG(log_fmt, ...) \
++ lt_log_print(LOG_CONNECTION_HANDSHAKE, "handshake->%s: " log_fmt, \
++ m_address.pretty_address_str().c_str(), __VA_ARGS__);
++
+ namespace torrent {
+
+ const char* Handshake::m_protocol = "BitTorrent protocol";
+@@ -862,7 +867,7 @@ restart:
+ m_manager->receive_failed(this, e.type(), e.error());
+
+ } catch (network_error& e) {
+- m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error);
++ m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_read_error);
+ }
+ }
+
+@@ -969,7 +974,7 @@ Handshake::event_write() {
+ m_manager->receive_failed(this, e.type(), e.error());
+
+ } catch (network_error& e) {
+- m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error);
++ m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_write_error);
+ }
+ }
+
+@@ -1070,7 +1075,7 @@ Handshake::prepare_peer_info() {
+ m_peerInfo = m_download->peer_list()->connected(m_address.c_sockaddr(), PeerList::connect_incoming);
+
+ if (m_peerInfo == NULL)
+- throw handshake_error(ConnectionManager::handshake_failed, e_handshake_network_error);
++ throw handshake_error(ConnectionManager::handshake_failed, e_handshake_no_peer_info);
+
+ if (m_peerInfo->failed_counter() > m_manager->max_failed)
+ throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_toomanyfailed);
+@@ -1221,7 +1226,7 @@ Handshake::event_error() {
+ if (m_state == INACTIVE)
+ throw internal_error("Handshake::event_error() called on an inactive handshake.");
+
+- m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_error);
++ m_manager->receive_failed(this, ConnectionManager::handshake_failed, e_handshake_network_socket_error);
+ }
+
+ }
+diff --git a/src/protocol/handshake_manager.cc b/src/protocol/handshake_manager.cc
+index b52c8d4e..99592ba8 100644
+--- a/src/protocol/handshake_manager.cc
++++ b/src/protocol/handshake_manager.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <rak/socket_address.h>
+@@ -54,10 +18,10 @@
+
+ #include "manager.h"
+
+-#define LT_LOG_SA(log_level, sa, log_fmt, ...) \
+- lt_log_print(LOG_CONNECTION_##log_level, "handshake_manager->%s: " log_fmt, (sa)->address_str().c_str(), __VA_ARGS__);
+-#define LT_LOG_SA_C(log_level, sa, log_fmt, ...) \
+- lt_log_print(LOG_CONNECTION_##log_level, "handshake_manager->%s: " log_fmt, \
++#define LT_LOG_SA(sa, log_fmt, ...) \
++ lt_log_print(LOG_CONNECTION_HANDSHAKE, "handshake_manager->%s: " log_fmt, (sa)->address_str().c_str(), __VA_ARGS__);
++#define LT_LOG_SA_C(sa, log_fmt, ...) \
++ lt_log_print(LOG_CONNECTION_HANDSHAKE, "handshake_manager->%s: " log_fmt, \
+ reinterpret_cast<const rak::socket_address*>(sa)->address_str().c_str(), __VA_ARGS__);
+
+ namespace torrent {
+@@ -122,7 +86,7 @@ HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) {
+ return;
+ }
+
+- LT_LOG_SA(INFO, &sa, "Adding incoming connection: fd:%i.", fd.get_fd());
++ LT_LOG_SA(&sa, "Adding incoming connection: fd:%i.", fd.get_fd());
+
+ manager->connection_manager()->inc_socket_count();
+
+@@ -183,7 +147,7 @@ HandshakeManager::create_outgoing(const rak::socket_address& sa, DownloadMain* d
+ else
+ message = ConnectionManager::handshake_outgoing;
+
+- LT_LOG_SA(INFO, &sa, "Adding outcoming connection: encryption:%x message:%x.", encryptionOptions, message);
++ LT_LOG_SA(&sa, "Adding outcoming connection: encryption:%x message:%x.", encryptionOptions, message);
+ manager->connection_manager()->inc_socket_count();
+
+ Handshake* handshake = new Handshake(fd, this, encryptionOptions);
+@@ -213,7 +177,7 @@ HandshakeManager::receive_succeeded(Handshake* handshake) {
+ handshake->extensions())) != NULL) {
+
+ manager->client_list()->retrieve_id(&handshake->peer_info()->mutable_client_info(), handshake->peer_info()->id());
+- LT_LOG_SA_C(INFO, handshake->peer_info()->socket_address(), "Handshake success.", 0);
++ LT_LOG_SA_C(handshake->peer_info()->socket_address(), "Handshake success.", 0);
+
+ pcb->peer_chunks()->set_have_timer(handshake->initialized_time());
+
+@@ -237,7 +201,7 @@ HandshakeManager::receive_succeeded(Handshake* handshake) {
+ else
+ reason = e_handshake_duplicate;
+
+- LT_LOG_SA_C(INFO, handshake->peer_info()->socket_address(), "Handshake dropped: %s.", strerror(reason));
++ LT_LOG_SA_C(handshake->peer_info()->socket_address(), "Handshake dropped: %s.", strerror(reason));
+ handshake->destroy_connection();
+ }
+
+@@ -255,13 +219,13 @@ HandshakeManager::receive_failed(Handshake* handshake, int message, int error) {
+ handshake->deactivate_connection();
+ handshake->destroy_connection();
+
+- LT_LOG_SA(INFO, sa, "Received error: message:%x %s.", message, strerror(error));
++ LT_LOG_SA(sa, "Received error: message:%x %s.", message, strerror(error));
+
+ if (handshake->encryption()->should_retry()) {
+ int retry_options = handshake->retry_options() | ConnectionManager::encryption_retrying;
+ DownloadMain* download = handshake->download();
+
+- LT_LOG_SA(INFO, sa, "Retrying %s.",
++ LT_LOG_SA(sa, "Retrying %s.",
+ retry_options & ConnectionManager::encryption_try_outgoing ? "encrypted" : "plaintext");
+
+ create_outgoing(*sa, download, retry_options);
+diff --git a/src/protocol/handshake_manager.h b/src/protocol/handshake_manager.h
+index cfd52aa0..dc398e3e 100644
+--- a/src/protocol/handshake_manager.h
++++ b/src/protocol/handshake_manager.h
+@@ -1,45 +1,9 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_NET_HANDSHAKE_MANAGER_H
+ #define LIBTORRENT_NET_HANDSHAKE_MANAGER_H
+
+-#include <string>
++#include <functional>
+ #include <inttypes.h>
+-#include lt_tr1_functional
++#include <string>
+ #include <rak/functional.h>
+ #include <rak/unordered_vector.h>
+ #include <rak/socket_address.h>
+diff --git a/src/protocol/peer_connection_base.cc b/src/protocol/peer_connection_base.cc
+index c02998fb..bd870425 100644
+--- a/src/protocol/peer_connection_base.cc
++++ b/src/protocol/peer_connection_base.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include <cstdio>
+ #include <fcntl.h>
+ #include <rak/error_number.h>
+diff --git a/src/protocol/request_list.cc b/src/protocol/request_list.cc
+index a4338bcb..ea5d388a 100644
+--- a/src/protocol/request_list.cc
++++ b/src/protocol/request_list.cc
+@@ -1,44 +1,8 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <algorithm>
+ #include <functional>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <rak/functional.h>
+
+ #include "torrent/data/block.h"
+diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am
+index 1bdfde3d..8cd26ce7 100644
+--- a/src/torrent/Makefile.am
++++ b/src/torrent/Makefile.am
+@@ -22,6 +22,7 @@ libsub_torrent_la_SOURCES = \
+ download_info.h \
+ error.cc \
+ error.h \
++ event.cc \
+ event.h \
+ exceptions.cc \
+ exceptions.h \
+diff --git a/src/torrent/common.h b/src/torrent/common.h
+index 3363143d..42cc3246 100644
+--- a/src/torrent/common.h
++++ b/src/torrent/common.h
+@@ -37,10 +37,13 @@
+ #ifndef LIBTORRENT_COMMON_H
+ #define LIBTORRENT_COMMON_H
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <cstddef>
+
+ struct sockaddr;
++struct sockaddr_in;
++struct sockaddr_in6;
++struct sockaddr_un;
+
+ namespace torrent {
+
+diff --git a/src/torrent/connection_manager.h b/src/torrent/connection_manager.h
+index 2dcf2b37..cf43b0bf 100644
+--- a/src/torrent/connection_manager.h
++++ b/src/torrent/connection_manager.h
+@@ -39,13 +39,13 @@
+ #ifndef LIBTORRENT_CONNECTION_MANAGER_H
+ #define LIBTORRENT_CONNECTION_MANAGER_H
+
++#include <functional>
+ #include <list>
+ #include <arpa/inet.h>
+ #include <netinet/in.h>
+ #include <netinet/in_systm.h>
+ #include <netinet/ip.h>
+ #include <sys/socket.h>
+-#include lt_tr1_functional
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/data/download_data.h b/src/torrent/data/download_data.h
+index 2b9c9412..fc212047 100644
+--- a/src/torrent/data/download_data.h
++++ b/src/torrent/data/download_data.h
+@@ -37,7 +37,7 @@
+ #ifndef LIBTORRENT_DATA_DOWNLOAD_DATA_H
+ #define LIBTORRENT_DATA_DOWNLOAD_DATA_H
+
+-#include lt_tr1_functional
++#include <functional>
+
+ #include <torrent/common.h>
+ #include <torrent/bitfield.h>
+diff --git a/src/torrent/data/file_list.cc b/src/torrent/data/file_list.cc
+index 4721bdbd..2e334fa8 100644
+--- a/src/torrent/data/file_list.cc
++++ b/src/torrent/data/file_list.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include <algorithm>
+ #include <cstring>
+ #include <functional>
+diff --git a/src/torrent/data/transfer_list.h b/src/torrent/data/transfer_list.h
+index 0a359b5e..9813af12 100644
+--- a/src/torrent/data/transfer_list.h
++++ b/src/torrent/data/transfer_list.h
+@@ -37,9 +37,10 @@
+ #ifndef LIBTORRENT_TRANSFER_LIST_H
+ #define LIBTORRENT_TRANSFER_LIST_H
+
++#include <functional>
+ #include <vector>
++
+ #include <torrent/common.h>
+-#include lt_tr1_functional
+
+ namespace torrent {
+
+diff --git a/src/torrent/download.cc b/src/torrent/download.cc
+index edddedfb..f72c9351 100644
+--- a/src/torrent/download.cc
++++ b/src/torrent/download.cc
+@@ -36,9 +36,7 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+-#include <inttypes.h>
++#include <cinttypes>
+
+ #include "data/block.h"
+ #include "data/block_list.h"
+diff --git a/src/torrent/download/choke_group.cc b/src/torrent/download/choke_group.cc
+index a1540fc1..d9b25a9c 100644
+--- a/src/torrent/download/choke_group.cc
++++ b/src/torrent/download/choke_group.cc
+@@ -37,7 +37,7 @@
+ #include "config.h"
+
+ #include <algorithm>
+-#include lt_tr1_functional
++#include <functional>
+
+ #include "choke_group.h"
+ #include "choke_queue.h"
+diff --git a/src/torrent/download/choke_group.h b/src/torrent/download/choke_group.h
+index 93fd1d02..50804b3e 100644
+--- a/src/torrent/download/choke_group.h
++++ b/src/torrent/download/choke_group.h
+@@ -39,7 +39,7 @@
+
+ #include <string>
+ #include <vector>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <torrent/common.h>
+ #include <torrent/download/choke_queue.h>
+
+diff --git a/src/torrent/download/choke_queue.cc b/src/torrent/download/choke_queue.cc
+index 3827e25e..7c00b686 100644
+--- a/src/torrent/download/choke_queue.cc
++++ b/src/torrent/download/choke_queue.cc
+@@ -37,10 +37,9 @@
+ #include "config.h"
+
+ #include <algorithm>
++#include <cstdlib>
+ #include <functional>
+ #include <numeric>
+-#include <cstdlib>
+-#include lt_tr1_functional
+ #include <rak/functional.h>
+
+ #include "protocol/peer_connection_base.h"
+diff --git a/src/torrent/download/choke_queue.h b/src/torrent/download/choke_queue.h
+index 973f6522..5e274a99 100644
+--- a/src/torrent/download/choke_queue.h
++++ b/src/torrent/download/choke_queue.h
+@@ -39,10 +39,11 @@
+
+ #include <torrent/common.h>
+
++#include <cinttypes>
++#include <functional>
+ #include <list>
+ #include <vector>
+-#include <inttypes.h>
+-#include lt_tr1_functional
++
+ #include <torrent/download/group_entry.h>
+
+ namespace torrent {
+diff --git a/src/torrent/download/group_entry.h b/src/torrent/download/group_entry.h
+index e167ecbb..a7c9e429 100644
+--- a/src/torrent/download/group_entry.h
++++ b/src/torrent/download/group_entry.h
+@@ -38,8 +38,9 @@
+ #define LIBTORRENT_DOWNLOAD_GROUP_ENTRY_H
+
+ #include <algorithm>
++#include <functional>
+ #include <vector>
+-#include lt_tr1_functional
++
+ #include <torrent/common.h>
+ #include <torrent/exceptions.h>
+
+diff --git a/src/torrent/download/resource_manager.cc b/src/torrent/download/resource_manager.cc
+index bc6374d2..51434c91 100644
+--- a/src/torrent/download/resource_manager.cc
++++ b/src/torrent/download/resource_manager.cc
+@@ -38,7 +38,6 @@
+
+ #include <algorithm>
+ #include <functional>
+-#include lt_tr1_functional
+ #include <limits>
+ #include <numeric>
+ #include <rak/functional.h>
+diff --git a/src/torrent/download/resource_manager.h b/src/torrent/download/resource_manager.h
+index b2f861af..ba61b45f 100644
+--- a/src/torrent/download/resource_manager.h
++++ b/src/torrent/download/resource_manager.h
+@@ -39,7 +39,7 @@
+
+ #include <string>
+ #include <vector>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/download_info.h b/src/torrent/download_info.h
+index 341e4c25..2c4dbaf2 100644
+--- a/src/torrent/download_info.h
++++ b/src/torrent/download_info.h
+@@ -37,10 +37,10 @@
+ #ifndef LIBTORRENT_DOWNLOAD_INFO_H
+ #define LIBTORRENT_DOWNLOAD_INFO_H
+
++#include <cinttypes>
++#include <functional>
+ #include <list>
+ #include <string>
+-#include <inttypes.h>
+-#include lt_tr1_functional
+
+ #include <torrent/rate.h>
+ #include <torrent/hash_string.h>
+diff --git a/src/torrent/error.cc b/src/torrent/error.cc
+index 5010c803..eea9bb83 100644
+--- a/src/torrent/error.cc
++++ b/src/torrent/error.cc
+@@ -54,11 +54,15 @@ static const char* errorStrings[e_last + 1] = {
+ "unencrypted connection rejected", // eh_unencrypted_rejected
+ "invalid encryption method", // eh_invalid_encryption
+ "encryption sync failed", // eh_encryption_sync_failed
+- "network error", // eh_network_error
++ "<deprecated>", // eh_
+ "network unreachable", // eh_network_unreachable
+ "network timeout", // eh_network_timeout
+ "invalid message order", // eh_invalid_order
+ "too many failed chunks", // eh_toomanyfailed
++ "no peer info", // eh_no_peer_info
++ "network socket error", // eh_network_socket_error
++ "network read error", // eh_network_read_error
++ "network write error", // eh_network_write_error
+
+ // "", // e_handshake_incoming
+ // "", // e_handshake_outgoing
+diff --git a/src/torrent/error.h b/src/torrent/error.h
+index f3fac463..295a595b 100644
+--- a/src/torrent/error.h
++++ b/src/torrent/error.h
+@@ -55,11 +55,14 @@ const int e_handshake_invalid_value = 8;
+ const int e_handshake_unencrypted_rejected = 9;
+ const int e_handshake_invalid_encryption = 10;
+ const int e_handshake_encryption_sync_failed = 11;
+-const int e_handshake_network_error = 12;
+ const int e_handshake_network_unreachable = 13;
+ const int e_handshake_network_timeout = 14;
+ const int e_handshake_invalid_order = 15;
+ const int e_handshake_toomanyfailed = 16;
++const int e_handshake_no_peer_info = 17;
++const int e_handshake_network_socket_error = 18;
++const int e_handshake_network_read_error = 19;
++const int e_handshake_network_write_error = 20;
+
+ // const int e_handshake_incoming = 13;
+ // const int e_handshake_outgoing = 14;
+@@ -69,7 +72,7 @@ const int e_handshake_toomanyfailed = 16;
+ // const int e_handshake_retry_plaintext = 18;
+ // const int e_handshake_retry_encrypted = 19;
+
+-const int e_last = 16;
++const int e_last = 20;
+
+ const char* strerror(int err) LIBTORRENT_EXPORT;
+
+diff --git a/src/torrent/event.cc b/src/torrent/event.cc
+new file mode 100644
+index 00000000..e68974ea
+--- /dev/null
++++ b/src/torrent/event.cc
+@@ -0,0 +1,19 @@
++#include "config.h"
++
++#include "event.h"
++
++#include "torrent/exceptions.h"
++#include "torrent/net/fd.h"
++
++namespace torrent {
++
++void
++Event::close_file_descriptor() {
++ if (!is_open())
++ throw internal_error("Tried to close already closed file descriptor on event type " + std::string(type_name()));
++
++ fd_close(m_fileDesc);
++ m_fileDesc = -1;
++}
++
++}
+diff --git a/src/torrent/event.h b/src/torrent/event.h
+index f3549762..73f87e46 100644
+--- a/src/torrent/event.h
++++ b/src/torrent/event.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_TORRENT_EVENT_H
+ #define LIBTORRENT_TORRENT_EVENT_H
+
+@@ -43,26 +7,48 @@ namespace torrent {
+
+ class LIBTORRENT_EXPORT Event {
+ public:
+- virtual ~Event() {}
++ Event();
++ virtual ~Event();
+
+- // These are not virtual as the fd is heavily used in select based
+- // polling, thus fast access is critical to performance.
+- int file_descriptor() const { return m_fileDesc; }
++ // TODO: Disable override.
++ bool is_open() const;
+
+- virtual void event_read() = 0;
+- virtual void event_write() = 0;
+- virtual void event_error() = 0;
++ int file_descriptor() const;
+
+- // Require all event types to define this function.
+- virtual const char* type_name() const { return "default"; }
++ virtual void event_read() = 0;
++ virtual void event_write() = 0;
++ virtual void event_error() = 0;
+
+- // Event closed?
++ // TODO: Require all to define their ownh typename.
++ virtual const char* type_name() const { return "default"; }
+
+ protected:
+- int m_fileDesc;
+- bool m_ipv6_socket;
++ void close_file_descriptor();
++ void set_file_descriptor(int fd);
++
++ int m_fileDesc;
++
++ // TODO: Deprecate.
++ bool m_ipv6_socket;
+ };
+
++inline Event::Event() : m_fileDesc(-1), m_ipv6_socket(false) {}
++inline Event::~Event() {}
++inline bool Event::is_open() const { return file_descriptor() != -1; }
++inline int Event::file_descriptor() const { return m_fileDesc; }
++inline void Event::set_file_descriptor(int fd) { m_fileDesc = fd; }
++
++// Defined in 'src/globals.cc'.
++[[gnu::weak]] void poll_event_open(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_close(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_closed(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_insert_read(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_insert_write(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_insert_error(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_remove_read(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_remove_write(Event* event) LIBTORRENT_EXPORT;
++[[gnu::weak]] void poll_event_remove_error(Event* event) LIBTORRENT_EXPORT;
++
+ }
+
+ #endif
+diff --git a/src/torrent/http.h b/src/torrent/http.h
+index c68d3933..c605afa5 100644
+--- a/src/torrent/http.h
++++ b/src/torrent/http.h
+@@ -38,9 +38,9 @@
+ #define LIBTORRENT_HTTP_H
+
+ #include <string>
++#include <functional>
+ #include <iosfwd>
+ #include <list>
+-#include lt_tr1_functional
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/net/Makefile.am b/src/torrent/net/Makefile.am
+index 51999d19..35dd4774 100644
+--- a/src/torrent/net/Makefile.am
++++ b/src/torrent/net/Makefile.am
+@@ -1,11 +1,25 @@
+ noinst_LTLIBRARIES = libsub_torrentnet.la
+
+ libsub_torrentnet_la_SOURCES = \
++ address_info.cc \
++ address_info.h \
++ fd.cc \
++ fd.h \
++ socket_address.cc \
++ socket_address.h \
+ socket_address_key.cc \
+- socket_address_key.h
++ socket_address_key.h \
++ socket_event.cc \
++ socket_event.h \
++ types.h
+
+ AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+
+ libtorrentincludedir = $(includedir)/torrent/net
+ libtorrentinclude_HEADERS = \
+- socket_address_key.h
++ address_info.h \
++ fd.h \
++ socket_address.h \
++ socket_address_key.h \
++ socket_event.h \
++ types.h
+diff --git a/src/torrent/net/address_info.cc b/src/torrent/net/address_info.cc
+new file mode 100644
+index 00000000..25a51ebd
+--- /dev/null
++++ b/src/torrent/net/address_info.cc
+@@ -0,0 +1,43 @@
++#include "config.h"
++
++#include "address_info.h"
++
++namespace torrent {
++
++int
++ai_get_addrinfo(const char* nodename, const char* servname, const addrinfo* hints, ai_unique_ptr& res) {
++ addrinfo* ai;
++ int err = ::getaddrinfo(nodename, servname, hints, &ai);
++
++ if (err != 0)
++ return err;
++
++ res.reset(ai);
++ return 0;
++}
++
++sa_unique_ptr
++ai_get_first_sa(const char* nodename, const char* servname, const addrinfo* hints) {
++ ai_unique_ptr aip;
++
++ if (ai_get_addrinfo(nodename, servname, hints, aip) != 0)
++ return nullptr;
++
++ return sa_copy(aip->ai_addr);
++}
++
++int
++ai_each_inet_inet6_first(const char* nodename, ai_sockaddr_func lambda) {
++ int err;
++ ai_unique_ptr ai;
++
++ // TODO: Change to a single call using hints with both inet/inet6.
++ if ((err = ai_get_addrinfo(nodename, NULL, ai_make_hint(0, PF_INET, SOCK_STREAM).get(), ai)) != 0 &&
++ (err = ai_get_addrinfo(nodename, NULL, ai_make_hint(0, PF_INET6, SOCK_STREAM).get(), ai)) != 0)
++ return err;
++
++ lambda(ai->ai_addr);
++ return 0;
++}
++
++}
+diff --git a/src/torrent/net/address_info.h b/src/torrent/net/address_info.h
+new file mode 100644
+index 00000000..c0b1c082
+--- /dev/null
++++ b/src/torrent/net/address_info.h
+@@ -0,0 +1,69 @@
++#ifndef LIBTORRENT_NET_ADDRESS_INFO_H
++#define LIBTORRENT_NET_ADDRESS_INFO_H
++
++#include <cstring>
++#include <functional>
++#include <memory>
++#include <string>
++#include <netdb.h>
++#include <torrent/common.h>
++#include <torrent/net/socket_address.h>
++
++namespace torrent {
++
++struct ai_deleter {
++ void operator()(addrinfo* ai) const { freeaddrinfo(ai); }
++};
++
++typedef std::unique_ptr<addrinfo, ai_deleter> ai_unique_ptr;
++typedef std::unique_ptr<const addrinfo, ai_deleter> c_ai_unique_ptr;
++typedef std::function<void (const sockaddr*)> ai_sockaddr_func;
++
++inline void ai_clear(addrinfo* ai);
++inline ai_unique_ptr ai_make_hint(int flags, int family, int socktype);
++
++int ai_get_addrinfo(const char* nodename, const char* servname, const addrinfo* hints, ai_unique_ptr& res) LIBTORRENT_EXPORT;
++
++// Helper functions:
++
++// TODO: Consider servname "0".
++// TODO: ai_get_first_sa_err that returns a tuple?
++sa_unique_ptr ai_get_first_sa(const char* nodename, const char* servname = nullptr, const addrinfo* hints = nullptr) LIBTORRENT_EXPORT;
++
++int ai_each_inet_inet6_first(const char* nodename, ai_sockaddr_func lambda) LIBTORRENT_EXPORT;
++
++// Get all addrinfo's, iterate, etc.
++
++//
++// Safe conversion from unique_ptr arguments:
++//
++
++inline void aip_clear(ai_unique_ptr& aip) { return ai_clear(aip.get()); }
++
++inline int aip_get_addrinfo(const char* nodename, const char* servname, const ai_unique_ptr& hints, ai_unique_ptr& res) { return ai_get_addrinfo(nodename, servname, hints.get(), res); }
++inline int aip_get_addrinfo(const char* nodename, const char* servname, const c_ai_unique_ptr& hints, ai_unique_ptr& res) { return ai_get_addrinfo(nodename, servname, hints.get(), res); }
++
++//
++// Implementations:
++//
++
++inline void
++ai_clear(addrinfo* ai) {
++ std::memset(ai, 0, sizeof(addrinfo));
++}
++
++inline ai_unique_ptr
++ai_make_hint(int flags, int family, int socktype) {
++ ai_unique_ptr aip(new addrinfo);
++
++ aip_clear(aip);
++ aip->ai_flags = flags;
++ aip->ai_family = family;
++ aip->ai_socktype = socktype;
++
++ return aip;
++}
++
++}
++
++#endif
+diff --git a/src/torrent/net/fd.cc b/src/torrent/net/fd.cc
+new file mode 100644
+index 00000000..07c91779
+--- /dev/null
++++ b/src/torrent/net/fd.cc
+@@ -0,0 +1,209 @@
++#include "config.h"
++
++#include "fd.h"
++
++#include <cerrno>
++#include <fcntl.h>
++#include <unistd.h>
++#include <netinet/in.h>
++#include <netinet/in_systm.h>
++#include <netinet/ip.h>
++
++#include "torrent/exceptions.h"
++#include "torrent/net/socket_address.h"
++#include "torrent/utils/log.h"
++
++#define LT_LOG(log_fmt, ...) \
++ lt_log_print(LOG_CONNECTION_FD, "fd: " log_fmt, __VA_ARGS__);
++#define LT_LOG_FLAG(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd: " log_fmt " (flags:0x%x)", flags);
++#define LT_LOG_FLAG_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd: " log_fmt " (flags:0x%x errno:%i message:'%s')", \
++ flags, errno, std::strerror(errno));
++#define LT_LOG_FD(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt, fd);
++#define LT_LOG_FD_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (errno:%i message:'%s')", \
++ fd, errno, std::strerror(errno));
++#define LT_LOG_FD_SOCKADDR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (address:%s)", \
++ fd, sa_pretty_str(sa).c_str());
++#define LT_LOG_FD_SOCKADDR_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (address:%s errno:%i message:'%s')", \
++ fd, sa_pretty_str(sa).c_str(), errno, std::strerror(errno));
++#define LT_LOG_FD_FLAG(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (flags:0x%x)", fd, flags);
++#define LT_LOG_FD_FLAG_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (flags:0x%x errno:%i message:'%s')", \
++ fd, flags, errno, std::strerror(errno));
++#define LT_LOG_FD_VALUE(log_fmt, value) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (value:%i)", fd, (int)value);
++#define LT_LOG_FD_VALUE_ERROR(log_fmt, value) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (value:%i errno:%i message:'%s')", \
++ fd, (int)value, errno, std::strerror(errno));
++
++namespace torrent {
++
++int fd__accept(int socket, sockaddr *address, socklen_t *address_len) { return ::accept(socket, address, address_len); }
++int fd__bind(int socket, const sockaddr *address, socklen_t address_len) { return ::bind(socket, address, address_len); }
++int fd__close(int fildes) { return ::close(fildes); }
++int fd__connect(int socket, const sockaddr *address, socklen_t address_len) { return ::connect(socket, address, address_len); }
++int fd__fcntl_int(int fildes, int cmd, int arg) { return ::fcntl(fildes, cmd, arg); }
++int fd__listen(int socket, int backlog) { return ::listen(socket, backlog); }
++int fd__setsockopt_int(int socket, int level, int option_name, int option_value) { return ::setsockopt(socket, level, option_name, &option_value, sizeof(int)); }
++int fd__socket(int domain, int type, int protocol) { return ::socket(domain, type, protocol); }
++
++int
++fd_open(fd_flags flags) {
++ int domain;
++ int protocol;
++
++ if (!fd_valid_flags(flags))
++ throw internal_error("torrent::fd_open failed: invalid fd_flags");
++
++ if ((flags & fd_flag_stream)) {
++ domain = SOCK_STREAM;
++ protocol = IPPROTO_TCP;
++ } else {
++ LT_LOG_FLAG("fd_open missing socket type");
++ errno = EINVAL;
++ return -1;
++ }
++
++ int fd = -1;
++
++ if (fd == -1 && !(flags & fd_flag_v4only)) {
++ LT_LOG_FLAG("fd_open opening ipv6 socket");
++ fd = fd__socket(PF_INET6, domain, protocol);
++ }
++
++ if (fd == -1 && !(flags & fd_flag_v6only)) {
++ LT_LOG_FLAG("fd_open opening ipv4 socket");
++ fd = fd__socket(PF_INET, domain, protocol);
++ }
++
++ if (fd == -1) {
++ LT_LOG_FLAG_ERROR("fd_open failed to open socket");
++ return -1;
++ }
++
++ if ((flags & fd_flag_v6only) && !fd_set_v6only(fd, true)) {
++ LT_LOG_FD_FLAG_ERROR("fd_open failed to set v6only");
++ fd_close(fd);
++ return -1;
++ }
++
++ if ((flags & fd_flag_nonblock) && !fd_set_nonblock(fd)) {
++ LT_LOG_FD_FLAG_ERROR("fd_open failed to set nonblock");
++ fd_close(fd);
++ return -1;
++ }
++
++ if ((flags & fd_flag_reuse_address) && !fd_set_reuse_address(fd, true)) {
++ LT_LOG_FD_FLAG_ERROR("fd_open failed to set reuse_address");
++ fd_close(fd);
++ return -1;
++ }
++
++ LT_LOG_FD_FLAG("fd_open succeeded");
++ return fd;
++}
++
++void
++fd_close(int fd) {
++ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
++ throw internal_error("torrent::fd_close: tried to close stdin/out/err");
++
++ if (fd__close(fd) == -1)
++ throw internal_error("torrent::fd_close: " + std::string(strerror(errno)));
++
++ LT_LOG_FD("fd_close succeeded");
++}
++
++fd_sap_tuple
++fd_accept(int fd) {
++ sa_unique_ptr sap = sa_make_inet6();
++ socklen_t socklen = sap_length(sap);
++
++ int accept_fd = fd__accept(fd, sap.get(), &socklen);
++
++ if (accept_fd == -1) {
++ LT_LOG_FD_ERROR("fd_accept failed");
++ return fd_sap_tuple{-1, nullptr};
++ }
++
++ return fd_sap_tuple{accept_fd, std::move(sap)};
++}
++
++bool
++fd_bind(int fd, const sockaddr* sa) {
++ if (fd__bind(fd, sa, sa_length(sa)) == -1) {
++ LT_LOG_FD_SOCKADDR_ERROR("fd_bind failed");
++ return false;
++ }
++
++ LT_LOG_FD_SOCKADDR("fd_bind succeeded");
++ return true;
++}
++
++bool
++fd_connect(int fd, const sockaddr* sa) {
++ if (fd__connect(fd, sa, sa_length(sa)) == 0) {
++ LT_LOG_FD_SOCKADDR("fd_connect succeeded");
++ return true;
++ }
++
++ if (errno == EINPROGRESS) {
++ LT_LOG_FD_SOCKADDR("fd_connect succeeded and in progress");
++ return true;
++ }
++
++ LT_LOG_FD_SOCKADDR_ERROR("fd_connect failed");
++ return false;
++}
++
++bool
++fd_listen(int fd, int backlog) {
++ if (fd__listen(fd, backlog) == -1) {
++ LT_LOG_FD_VALUE_ERROR("fd_listen failed", backlog);
++ return false;
++ }
++
++ LT_LOG_FD_VALUE("fd_listen succeeded", backlog);
++ return true;
++}
++
++bool
++fd_set_nonblock(int fd) {
++ if (fd__fcntl_int(fd, F_SETFL, O_NONBLOCK) == -1) {
++ LT_LOG_FD_ERROR("fd_set_nonblock failed");
++ return false;
++ }
++
++ LT_LOG_FD("fd_set_nonblock succeeded");
++ return true;
++}
++
++bool
++fd_set_reuse_address(int fd, bool state) {
++ if (fd__setsockopt_int(fd, SOL_SOCKET, SO_REUSEADDR, state) == -1) {
++ LT_LOG_FD_VALUE_ERROR("fd_set_reuse_address failed", state);
++ return false;
++ }
++
++ LT_LOG_FD_VALUE("fd_set_reuse_address succeeded", state);
++ return true;
++}
++
++bool
++fd_set_v6only(int fd, bool state) {
++ if (fd__setsockopt_int(fd, IPPROTO_IPV6, IPV6_V6ONLY, state) == -1) {
++ LT_LOG_FD_VALUE_ERROR("fd_set_v6only failed", state);
++ return false;
++ }
++
++ LT_LOG_FD_VALUE("fd_set_v6only succeeded", state);
++ return true;
++}
++
++}
+diff --git a/src/torrent/net/fd.h b/src/torrent/net/fd.h
+new file mode 100644
+index 00000000..a7094646
+--- /dev/null
++++ b/src/torrent/net/fd.h
+@@ -0,0 +1,63 @@
++#ifndef LIBTORRENT_NET_FD_H
++#define LIBTORRENT_NET_FD_H
++
++#include <string>
++#include <torrent/common.h>
++#include <torrent/net/types.h>
++
++namespace torrent {
++
++enum fd_flags : int {
++ fd_flag_stream = 0x1,
++ fd_flag_nonblock = 0x10,
++ fd_flag_reuse_address = 0x20,
++ fd_flag_v4only = 0x40,
++ fd_flag_v6only = 0x80,
++ fd_flag_all = 0xff,
++};
++
++constexpr bool fd_valid_flags(fd_flags flags);
++
++int fd_open(fd_flags flags) LIBTORRENT_EXPORT;
++void fd_close(int fd) LIBTORRENT_EXPORT;
++
++fd_sap_tuple fd_accept(int fd) LIBTORRENT_EXPORT;
++
++bool fd_bind(int fd, const sockaddr* sa) LIBTORRENT_EXPORT;
++bool fd_connect(int fd, const sockaddr* sa) LIBTORRENT_EXPORT;
++bool fd_listen(int fd, int backlog) LIBTORRENT_EXPORT;
++
++bool fd_set_nonblock(int fd) LIBTORRENT_EXPORT;
++bool fd_set_reuse_address(int fd, bool state) LIBTORRENT_EXPORT;
++bool fd_set_v6only(int fd, bool state) LIBTORRENT_EXPORT;
++
++[[gnu::weak]] int fd__accept(int socket, sockaddr *address, socklen_t *address_len) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__bind(int socket, const sockaddr *address, socklen_t address_len) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__close(int fildes) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__connect(int socket, const sockaddr *address, socklen_t address_len) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__fcntl_int(int fildes, int cmd, int arg) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__listen(int socket, int backlog) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__setsockopt_int(int socket, int level, int option_name, int option_value) LIBTORRENT_EXPORT;
++[[gnu::weak]] int fd__socket(int domain, int type, int protocol) LIBTORRENT_EXPORT;
++
++constexpr fd_flags
++operator |(fd_flags lhs, fd_flags rhs) {
++ return static_cast<fd_flags>(static_cast<int>(lhs) | static_cast<int>(rhs));
++}
++
++inline fd_flags&
++operator |=(fd_flags& lhs, fd_flags rhs) {
++ return (lhs = lhs | rhs);
++}
++
++constexpr bool
++fd_valid_flags(fd_flags flags) {
++ return
++ (flags & fd_flag_stream) &&
++ !((flags & fd_flag_v4only) && (flags & fd_flag_v6only)) &&
++ !(flags & ~(fd_flag_all));
++}
++
++}
++
++#endif
+diff --git a/src/torrent/net/socket_address.cc b/src/torrent/net/socket_address.cc
+new file mode 100644
+index 00000000..c36ba0ae
+--- /dev/null
++++ b/src/torrent/net/socket_address.cc
+@@ -0,0 +1,559 @@
++#include "config.h"
++
++#include "socket_address.h"
++
++#include <cstring>
++#include <arpa/inet.h>
++#include <sys/un.h>
++
++// TODO: Deprecate.
++#include "rak/socket_address.h"
++
++#include "torrent/exceptions.h"
++
++namespace torrent {
++
++constexpr uint32_t
++sin6_addr32_index(const sockaddr_in6* sa, unsigned int index) {
++ return
++ (sa->sin6_addr.s6_addr[index * 4 + 0] << 24) +
++ (sa->sin6_addr.s6_addr[index * 4 + 1] << 16) +
++ (sa->sin6_addr.s6_addr[index * 4 + 2] << 8) +
++ (sa->sin6_addr.s6_addr[index * 4 + 3] << 0);
++}
++
++inline void
++sin6_addr32_set(sockaddr_in6* sa, unsigned int index, uint32_t value) {
++ sa->sin6_addr.s6_addr[index * 4 + 0] = (value >> 24);
++ sa->sin6_addr.s6_addr[index * 4 + 1] = (value >> 16);
++ sa->sin6_addr.s6_addr[index * 4 + 2] = (value >> 8);
++ sa->sin6_addr.s6_addr[index * 4 + 3] = (value >> 0);
++}
++
++inline in6_addr
++sin6_make_addr32(uint32_t addr0, uint32_t addr1, uint32_t addr2, uint32_t addr3) {
++ uint32_t addr32[4];
++ addr32[0] = htonl(addr0);
++ addr32[1] = htonl(addr1);
++ addr32[2] = htonl(addr2);
++ addr32[3] = htonl(addr3);
++
++ return *reinterpret_cast<in6_addr*>(addr32);
++}
++
++bool
++sa_is_unspec(const sockaddr* sa) {
++ return sa != NULL && sa->sa_family == AF_UNSPEC;
++}
++
++bool
++sa_is_inet(const sockaddr* sa) {
++ return sa != NULL && sa->sa_family == AF_INET;
++}
++
++bool
++sa_is_inet6(const sockaddr* sa) {
++ return sa != NULL && sa->sa_family == AF_INET6;
++}
++
++bool
++sa_is_inet_inet6(const sockaddr* sa) {
++ return sa != NULL && (sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
++}
++
++bool
++sa_is_any(const sockaddr* sa) {
++ switch (sa->sa_family) {
++ case AF_INET:
++ return sin_is_any(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ if (sa_is_v4mapped(sa))
++ return sin6_addr32_index(reinterpret_cast<const sockaddr_in6*>(sa), 3) == htonl(INADDR_ANY);
++ return sin6_is_any(reinterpret_cast<const sockaddr_in6*>(sa));
++ default:
++ return true;
++ }
++}
++
++bool
++sin_is_any(const sockaddr_in* sa) {
++ return sa->sin_addr.s_addr == htonl(INADDR_ANY);
++}
++
++bool
++sin6_is_any(const sockaddr_in6* sa) {
++ return std::memcmp(&sa->sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0;
++}
++
++bool
++sa_is_broadcast(const sockaddr* sa) {
++ switch (sa->sa_family) {
++ case AF_INET:
++ return sin_is_broadcast(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ if (sa_is_v4mapped(sa))
++ return sin6_addr32_index(reinterpret_cast<const sockaddr_in6*>(sa), 3) == htonl(INADDR_BROADCAST);
++ return false;
++ default:
++ return false;
++ }
++}
++
++bool
++sin_is_broadcast(const sockaddr_in* sa) {
++ return sa->sin_addr.s_addr == htonl(INADDR_BROADCAST);
++}
++
++bool
++sa_is_v4mapped(const sockaddr* sa) {
++ return sa != NULL && sa->sa_family == AF_INET6 && sin6_is_v4mapped(reinterpret_cast<const sockaddr_in6*>(sa));
++}
++
++bool
++sin6_is_v4mapped(const sockaddr_in6* sa) {
++ return sa != NULL && IN6_IS_ADDR_V4MAPPED(&sa->sin6_addr);
++}
++
++bool
++sa_is_port_any(const sockaddr* sa) {
++ return sa_port(sa) == 0;
++}
++
++size_t
++sa_length(const sockaddr* sa) {
++ switch(sa->sa_family) {
++ case AF_INET:
++ return sizeof(sockaddr_in);
++ case AF_INET6:
++ return sizeof(sockaddr_in6);
++ default:
++ return sizeof(sa);
++ }
++}
++
++sa_unique_ptr
++sa_make_unspec() {
++ sa_unique_ptr sa(new sockaddr);
++
++ std::memset(sa.get(), 0, sizeof(sa));
++ sa.get()->sa_family = AF_UNSPEC;
++
++ return sa;
++}
++
++sa_unique_ptr
++sa_make_inet() {
++ return sa_unique_ptr(reinterpret_cast<sockaddr*>(sin_make().release()));
++}
++
++sa_unique_ptr
++sa_make_inet6() {
++ return sa_unique_ptr(reinterpret_cast<sockaddr*>(sin6_make().release()));
++}
++
++sa_unique_ptr
++sa_make_unix(const std::string& pathname) {
++ if (!pathname.empty())
++ throw internal_error("torrent::sa_make_unix: function not implemented");
++
++ sun_unique_ptr sunp(new sockaddr_un);
++
++ std::memset(sunp.get(), 0, sizeof(sockaddr_un));
++ sunp->sun_family = AF_UNIX;
++ // TODO: verify length, copy pathname
++
++ return sa_unique_ptr(reinterpret_cast<sockaddr*>(sunp.release()));
++}
++
++sa_unique_ptr
++sa_convert(const sockaddr* sa) {
++ if (sa == NULL)
++ return sa_make_unspec();
++
++ switch(sa->sa_family) {
++ case AF_INET:
++ return sa_copy_in(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ if (sin6_is_v4mapped(reinterpret_cast<const sockaddr_in6*>(sa)))
++ return sa_from_v4mapped_in6(reinterpret_cast<const sockaddr_in6*>(sa));
++
++ return sa_copy_in6(reinterpret_cast<const sockaddr_in6*>(sa));
++ case AF_UNSPEC:
++ return sa_make_unspec();
++ default:
++ throw internal_error("torrent::sa_convert: sockaddr is not a valid family");
++ }
++}
++
++sa_unique_ptr
++sa_copy(const sockaddr* sa) {
++ if (sa == nullptr)
++ throw internal_error("torrent::sa_copy: sockaddr is a nullptr");
++
++ switch(sa->sa_family) {
++ case AF_INET:
++ return sa_copy_in(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ return sa_copy_in6(reinterpret_cast<const sockaddr_in6*>(sa));
++ case AF_UNSPEC:
++ return sa_make_unspec();
++ default:
++ throw internal_error("torrent::sa_copy: sockaddr is not a valid family");
++ }
++}
++
++sa_unique_ptr
++sa_copy_in(const sockaddr_in* sa) {
++ sa_unique_ptr result(reinterpret_cast<sockaddr*>(new sockaddr_in));
++ std::memcpy(result.get(), sa, sizeof(sockaddr_in));
++ return result;
++}
++
++sa_unique_ptr
++sa_copy_in6(const sockaddr_in6* sa) {
++ sa_unique_ptr result(reinterpret_cast<sockaddr*>(new sockaddr_in6));
++ std::memcpy(result.get(), sa, sizeof(sockaddr_in6));
++ return result;
++}
++
++sa_unique_ptr
++sa_copy_addr(const sockaddr* sa, uint16_t port) {
++ if (sa == nullptr)
++ throw internal_error("torrent::sa_copy_addr: sockaddr is a nullptr");
++
++ switch(sa->sa_family) {
++ case AF_INET:
++ return sa_copy_addr_in(reinterpret_cast<const sockaddr_in*>(sa), port);
++ case AF_INET6:
++ return sa_copy_addr_in6(reinterpret_cast<const sockaddr_in6*>(sa), port);
++ case AF_UNSPEC:
++ return sa_make_unspec();
++ default:
++ throw internal_error("torrent::sa_copy_addr: sockaddr is not a valid family");
++ }
++}
++
++sa_unique_ptr
++sa_copy_addr_in(const sockaddr_in* sa, uint16_t port) {
++ sa_unique_ptr result(reinterpret_cast<sockaddr*>(new sockaddr_in));
++ std::memset(result.get(), 0, sizeof(sockaddr_in));
++ reinterpret_cast<sockaddr_in*>(result.get())->sin_family = AF_INET;
++ reinterpret_cast<sockaddr_in*>(result.get())->sin_addr = sa->sin_addr;
++ reinterpret_cast<sockaddr_in*>(result.get())->sin_port = htons(port);
++ return result;
++}
++
++sa_unique_ptr
++sa_copy_addr_in6(const sockaddr_in6* sa, uint16_t port) {
++ sa_unique_ptr result(reinterpret_cast<sockaddr*>(new sockaddr_in6));
++ std::memset(result.get(), 0, sizeof(sockaddr_in6));
++ reinterpret_cast<sockaddr_in6*>(result.get())->sin6_family = AF_INET6;
++ std::memcpy(&reinterpret_cast<sockaddr_in6*>(result.get())->sin6_addr, &sa->sin6_addr, sizeof(in6_addr));
++ reinterpret_cast<sockaddr_in6*>(result.get())->sin6_port = htons(port);
++ return result;
++}
++
++sin_unique_ptr
++sin_copy(const sockaddr_in* sa) {
++ sin_unique_ptr result(new sockaddr_in);
++ std::memcpy(result.get(), sa, sizeof(sockaddr_in));
++ return result;
++}
++
++sin6_unique_ptr
++sin6_copy(const sockaddr_in6* sa) {
++ sin6_unique_ptr result(new sockaddr_in6);
++ std::memcpy(result.get(), sa, sizeof(sockaddr_in6));
++ return result;
++}
++
++sin_unique_ptr
++sin_make() {
++ sin_unique_ptr sa(new sockaddr_in);
++ std::memset(sa.get(), 0, sizeof(sockaddr_in));
++ sa.get()->sin_family = AF_INET;
++
++ return sa;
++}
++
++sin6_unique_ptr
++sin6_make() {
++ sin6_unique_ptr sa(new sockaddr_in6);
++ std::memset(sa.get(), 0, sizeof(sockaddr_in6));
++ sa.get()->sin6_family = AF_INET6;
++
++ return sa;
++}
++
++sa_unique_ptr
++sa_from_v4mapped(const sockaddr* sa) {
++ if (!sa_is_inet6(sa))
++ throw internal_error("torrent::sa_from_v4mapped: sockaddr is not inet6");
++
++ return sa_from_in(sin_from_v4mapped_in6(reinterpret_cast<const sockaddr_in6*>(sa)));
++}
++
++sa_unique_ptr
++sa_to_v4mapped(const sockaddr* sa) {
++ if (!sa_is_inet(sa))
++ throw internal_error("torrent::sa_to_v4mapped: sockaddr is not inet");
++
++ return sa_from_in6(sin6_to_v4mapped_in(reinterpret_cast<const sockaddr_in*>(sa)));
++}
++
++sin_unique_ptr
++sin_from_v4mapped_in6(const sockaddr_in6* sin6) {
++ if (!sin6_is_v4mapped(sin6))
++ throw internal_error("torrent::sin6_is_v4mapped: sockaddr_in6 is not v4mapped");
++
++ sin_unique_ptr result = sin_make();
++ result.get()->sin_addr.s_addr = reinterpret_cast<in_addr_t>(htonl(sin6_addr32_index(sin6, 3)));
++ result.get()->sin_port = sin6->sin6_port;
++
++ return result;
++}
++
++sin6_unique_ptr
++sin6_to_v4mapped_in(const sockaddr_in* sin) {
++ sin6_unique_ptr result = sin6_make();
++
++ result.get()->sin6_addr = sin6_make_addr32(0, 0, 0xffff, ntohl(sin->sin_addr.s_addr));
++ result.get()->sin6_port = sin->sin_port;
++
++ return result;
++}
++
++sin_unique_ptr
++sin_from_sa(sa_unique_ptr&& sap) {
++ if (!sap_is_inet(sap))
++ throw internal_error("torrent::sin_from_sa: sockaddr is nullptr or not inet");
++
++ return sin_unique_ptr(reinterpret_cast<sockaddr_in*>(sap.release()));
++}
++
++sin6_unique_ptr
++sin6_from_sa(sa_unique_ptr&& sap) {
++ if (!sap_is_inet6(sap))
++ throw internal_error("torrent::sin6_from_sa: sockaddr is nullptr or not inet6");
++
++ return sin6_unique_ptr(reinterpret_cast<sockaddr_in6*>(sap.release()));
++}
++
++c_sin_unique_ptr
++sin_from_c_sa(c_sa_unique_ptr&& sap) {
++ if (!sap_is_inet(sap))
++ throw internal_error("torrent::sin_from_c_sa: sockaddr is nullptr or not inet");
++
++ return c_sin_unique_ptr(reinterpret_cast<const sockaddr_in*>(sap.release()));
++}
++
++c_sin6_unique_ptr
++sin6_from_c_sa(sa_unique_ptr&& sap) {
++ if (!sap_is_inet6(sap))
++ throw internal_error("torrent::sin6_from_c_sa: sockaddr is nullptr or not inet6");
++
++ return c_sin6_unique_ptr(reinterpret_cast<const sockaddr_in6*>(sap.release()));
++}
++
++void
++sa_clear_inet6(sockaddr_in6* sa) {
++ std::memset(sa, 0, sizeof(sockaddr_in6));
++ sa->sin6_family = AF_INET6;
++}
++
++uint16_t
++sa_port(const sockaddr* sa) {
++ if (sa == NULL)
++ return 0;
++
++ switch(sa->sa_family) {
++ case AF_INET:
++ return ntohs(reinterpret_cast<const sockaddr_in*>(sa)->sin_port);
++ case AF_INET6:
++ return ntohs(reinterpret_cast<const sockaddr_in6*>(sa)->sin6_port);
++ default:
++ return 0;
++ }
++}
++
++void
++sa_set_port(sockaddr* sa, uint16_t port) {
++ switch(sa->sa_family) {
++ case AF_INET:
++ reinterpret_cast<sockaddr_in*>(sa)->sin_port = htons(port);
++ return;
++ case AF_INET6:
++ reinterpret_cast<sockaddr_in6*>(sa)->sin6_port = htons(port);
++ return;
++ default:
++ throw internal_error("torrent::sa_set_port: invalid family type");
++ }
++}
++
++bool
++sa_equal(const sockaddr* lhs, const sockaddr* rhs) {
++ switch(rhs->sa_family) {
++ case AF_INET:
++ case AF_INET6:
++ case AF_UNSPEC:
++ break;
++ default:
++ throw internal_error("torrent::sa_equal: rhs sockaddr is not a valid family");
++ }
++
++ switch(lhs->sa_family) {
++ case AF_INET:
++ return lhs->sa_family == rhs->sa_family &&
++ sin_equal(reinterpret_cast<const sockaddr_in*>(lhs), reinterpret_cast<const sockaddr_in*>(rhs));
++ case AF_INET6:
++ return lhs->sa_family == rhs->sa_family &&
++ sin6_equal(reinterpret_cast<const sockaddr_in6*>(lhs), reinterpret_cast<const sockaddr_in6*>(rhs));
++ case AF_UNSPEC:
++ return lhs->sa_family == rhs->sa_family;
++ default:
++ throw internal_error("torrent::sa_equal: lhs sockaddr is not a valid family");
++ }
++}
++
++bool
++sin_equal(const sockaddr_in* lhs, const sockaddr_in* rhs) {
++ return lhs->sin_port == rhs->sin_port && lhs->sin_addr.s_addr == rhs->sin_addr.s_addr;
++}
++
++bool
++sin6_equal(const sockaddr_in6* lhs, const sockaddr_in6* rhs) {
++ return lhs->sin6_port == rhs->sin6_port && std::equal(lhs->sin6_addr.s6_addr, lhs->sin6_addr.s6_addr + 16, rhs->sin6_addr.s6_addr);
++}
++
++bool
++sa_equal_addr(const sockaddr* lhs, const sockaddr* rhs) {
++ switch(rhs->sa_family) {
++ case AF_INET:
++ case AF_INET6:
++ case AF_UNSPEC:
++ break;
++ default:
++ throw internal_error("torrent::sa_equal_addr: rhs sockaddr is not a valid family");
++ }
++
++ switch(lhs->sa_family) {
++ case AF_INET:
++ return lhs->sa_family == rhs->sa_family &&
++ sin_equal_addr(reinterpret_cast<const sockaddr_in*>(lhs), reinterpret_cast<const sockaddr_in*>(rhs));
++ case AF_INET6:
++ return lhs->sa_family == rhs->sa_family &&
++ sin6_equal_addr(reinterpret_cast<const sockaddr_in6*>(lhs), reinterpret_cast<const sockaddr_in6*>(rhs));
++ case AF_UNSPEC:
++ return lhs->sa_family == rhs->sa_family;
++ default:
++ throw internal_error("torrent::sa_equal_addr: lhs sockaddr is not a valid family");
++ }
++}
++
++bool
++sin_equal_addr(const sockaddr_in* lhs, const sockaddr_in* rhs) {
++ return lhs->sin_addr.s_addr == rhs->sin_addr.s_addr;
++}
++
++bool
++sin6_equal_addr(const sockaddr_in6* lhs, const sockaddr_in6* rhs) {
++ return std::equal(lhs->sin6_addr.s6_addr, lhs->sin6_addr.s6_addr + 16, rhs->sin6_addr.s6_addr);
++}
++
++std::string
++sa_addr_str(const sockaddr* sa) {
++ if (sa == NULL)
++ return "unspec";
++
++ switch (sa->sa_family) {
++ case AF_INET:
++ return sin_addr_str(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ return sin6_addr_str(reinterpret_cast<const sockaddr_in6*>(sa));
++ case AF_UNSPEC:
++ return "unspec";
++ default:
++ return "invalid";
++ }
++}
++
++std::string
++sin_addr_str(const sockaddr_in* sa) {
++ char buffer[INET_ADDRSTRLEN];
++
++ if (inet_ntop(AF_INET, &sa->sin_addr, buffer, INET_ADDRSTRLEN) == NULL)
++ return "inet_error";
++
++ return buffer;
++}
++
++
++std::string
++sin6_addr_str(const sockaddr_in6* sa) {
++ char buffer[INET6_ADDRSTRLEN];
++
++ if (inet_ntop(AF_INET6, &sa->sin6_addr, buffer, INET6_ADDRSTRLEN) == NULL)
++ return "inet6_error";
++
++ return buffer;
++}
++
++std::string
++sa_pretty_str(const sockaddr* sa) {
++ if (sa == nullptr)
++ return "nullptr";
++
++ switch (sa->sa_family) {
++ case AF_INET:
++ return sin_pretty_str(reinterpret_cast<const sockaddr_in*>(sa));
++ case AF_INET6:
++ return sin6_pretty_str(reinterpret_cast<const sockaddr_in6*>(sa));
++ case AF_UNSPEC:
++ return "unspec";
++ default:
++ return "invalid";
++ }
++}
++
++std::string
++sin_pretty_str(const sockaddr_in* sa) {
++ auto result = sin_addr_str(sa);
++
++ if (sa->sin_port != 0)
++ result += ':' + std::to_string(ntohs(sa->sin_port));
++
++ return result;
++}
++
++std::string
++sin6_pretty_str(const sockaddr_in6* sa) {
++ auto result = "[" + sin6_addr_str(sa) + "]";
++
++ if (sa->sin6_port != 0)
++ result += ':' + std::to_string(ntohs(sa->sin6_port));
++
++ return result;
++}
++
++// Deprecated:
++
++void
++sa_inet_mapped_inet6(const sockaddr_in* sa, sockaddr_in6* mapped) {
++ uint32_t addr32[4];
++ addr32[0] = 0;
++ addr32[1] = 0;
++ addr32[2] = htonl(0xffff);
++ addr32[3] = sa->sin_addr.s_addr;
++
++ sa_clear_inet6(mapped);
++
++ mapped->sin6_addr = *reinterpret_cast<in6_addr*>(addr32);
++ mapped->sin6_port = sa->sin_port;
++}
++
++std::string
++sa_pretty_address_str(const sockaddr* sa) {
++ return sa_pretty_str(sa);
++}
++
++}
+diff --git a/src/torrent/net/socket_address.h b/src/torrent/net/socket_address.h
+new file mode 100644
+index 00000000..f64aee68
+--- /dev/null
++++ b/src/torrent/net/socket_address.h
+@@ -0,0 +1,229 @@
++#ifndef LIBTORRENT_NET_SOCKET_ADDRESS_H
++#define LIBTORRENT_NET_SOCKET_ADDRESS_H
++
++#include <memory>
++#include <string>
++#include <netinet/in.h>
++#include <sys/socket.h>
++#include <torrent/common.h>
++#include <torrent/net/types.h>
++
++namespace torrent {
++
++bool sa_is_unspec(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sa_is_inet(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sa_is_inet6(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sa_is_inet_inet6(const sockaddr* sa) LIBTORRENT_EXPORT;
++
++bool sa_is_any(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sin_is_any(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++bool sin6_is_any(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++bool sa_is_broadcast(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sin_is_broadcast(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++
++bool sa_is_v4mapped(const sockaddr* sa) LIBTORRENT_EXPORT;
++bool sin6_is_v4mapped(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++bool sa_is_port_any(const sockaddr* sa) LIBTORRENT_EXPORT;
++
++size_t sa_length(const sockaddr* sa) LIBTORRENT_EXPORT;
++
++sa_unique_ptr sa_make_unspec() LIBTORRENT_EXPORT;
++sa_unique_ptr sa_make_inet() LIBTORRENT_EXPORT;
++sa_unique_ptr sa_make_inet6() LIBTORRENT_EXPORT;
++sa_unique_ptr sa_make_unix(const std::string& pathname) LIBTORRENT_EXPORT;
++
++sa_unique_ptr sa_convert(const sockaddr* sa) LIBTORRENT_EXPORT;
++
++sa_unique_ptr sa_copy(const sockaddr* sa) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_copy_in(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_copy_in6(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_copy_addr(const sockaddr* sa, uint16_t port = 0) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_copy_addr_in(const sockaddr_in* sa, uint16_t port = 0) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_copy_addr_in6(const sockaddr_in6* sa, uint16_t port = 0) LIBTORRENT_EXPORT;
++sin_unique_ptr sin_copy(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++sin6_unique_ptr sin6_copy(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++sin_unique_ptr sin_make() LIBTORRENT_EXPORT;
++sin6_unique_ptr sin6_make() LIBTORRENT_EXPORT;
++
++sa_unique_ptr sa_from_v4mapped(const sockaddr* sa) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_to_v4mapped(const sockaddr* sa) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_from_v4mapped_in6(const sockaddr_in6* sin6) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_to_v4mapped_in(const sockaddr_in* sin) LIBTORRENT_EXPORT;
++sin_unique_ptr sin_from_v4mapped_in6(const sockaddr_in6* sin6) LIBTORRENT_EXPORT;
++sin6_unique_ptr sin6_to_v4mapped_in(const sockaddr_in* sin) LIBTORRENT_EXPORT;
++
++sa_unique_ptr sa_from_in(sin_unique_ptr&& sinp) LIBTORRENT_EXPORT;
++c_sa_unique_ptr sa_from_in(c_sin_unique_ptr&& sinp) LIBTORRENT_EXPORT;
++sa_unique_ptr sa_from_in6(sin6_unique_ptr&& sin6p) LIBTORRENT_EXPORT;
++c_sa_unique_ptr sa_from_in6(c_sin6_unique_ptr&& sin6p) LIBTORRENT_EXPORT;
++sin_unique_ptr sin_from_sa(sa_unique_ptr&& sap) LIBTORRENT_EXPORT;
++sin6_unique_ptr sin6_from_sa(sa_unique_ptr&& sap) LIBTORRENT_EXPORT;
++c_sin_unique_ptr sin_from_c_sa(c_sa_unique_ptr&& sap) LIBTORRENT_EXPORT;
++c_sin6_unique_ptr sin6_from_c_sa(c_sa_unique_ptr&& sap) LIBTORRENT_EXPORT;
++
++void sa_clear_inet6(sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++uint16_t sa_port(const sockaddr* sa) LIBTORRENT_EXPORT;
++void sa_set_port(sockaddr* sa, uint16_t port) LIBTORRENT_EXPORT;
++
++bool sa_equal(const sockaddr* lhs, const sockaddr* rhs) LIBTORRENT_EXPORT;
++bool sin_equal(const sockaddr_in* lhs, const sockaddr_in* rhs) LIBTORRENT_EXPORT;
++bool sin6_equal(const sockaddr_in6* lhs, const sockaddr_in6* rhs) LIBTORRENT_EXPORT;
++
++bool sa_equal_addr(const sockaddr* lhs, const sockaddr* rhs) LIBTORRENT_EXPORT;
++bool sin_equal_addr(const sockaddr_in* lhs, const sockaddr_in* rhs) LIBTORRENT_EXPORT;
++bool sin6_equal_addr(const sockaddr_in6* lhs, const sockaddr_in6* rhs) LIBTORRENT_EXPORT;
++
++std::string sa_addr_str(const sockaddr* sa) LIBTORRENT_EXPORT;
++std::string sin_addr_str(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++std::string sin6_addr_str(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++std::string sa_pretty_str(const sockaddr* sa) LIBTORRENT_EXPORT;
++std::string sin_pretty_str(const sockaddr_in* sa) LIBTORRENT_EXPORT;
++std::string sin6_pretty_str(const sockaddr_in6* sa) LIBTORRENT_EXPORT;
++
++// Rename/replace:
++void sa_inet_mapped_inet6(const sockaddr_in* sa, sockaddr_in6* mapped) LIBTORRENT_EXPORT;
++
++std::string sa_pretty_address_str(const sockaddr* sa) LIBTORRENT_EXPORT;
++
++//
++// Tuples:
++//
++
++bool fd_sap_equal(const fd_sap_tuple& lhs, const fd_sap_tuple& rhs) LIBTORRENT_EXPORT;
++
++//
++// Safe conversion from unique_ptr arguments:
++//
++
++inline bool sap_is_unspec(const sa_unique_ptr& sap) { return sa_is_unspec(sap.get()); }
++inline bool sap_is_unspec(const c_sa_unique_ptr& sap) { return sa_is_unspec(sap.get()); }
++inline bool sap_is_inet(const c_sa_unique_ptr& sap) { return sa_is_inet(sap.get()); }
++inline bool sap_is_inet(const sa_unique_ptr& sap) { return sa_is_inet(sap.get()); }
++inline bool sap_is_inet6(const sa_unique_ptr& sap) { return sa_is_inet6(sap.get()); }
++inline bool sap_is_inet6(const c_sa_unique_ptr& sap) { return sa_is_inet6(sap.get()); }
++inline bool sap_is_inet_inet6(const sa_unique_ptr& sap) { return sa_is_inet_inet6(sap.get()); }
++inline bool sap_is_inet_inet6(const c_sa_unique_ptr& sap) { return sa_is_inet_inet6(sap.get()); }
++
++inline bool sap_is_any(const sa_unique_ptr& sap) { return sa_is_any(sap.get()); }
++inline bool sap_is_any(const c_sa_unique_ptr& sap) { return sa_is_any(sap.get()); }
++inline bool sinp_is_any(const sin_unique_ptr& sinp) { return sin_is_any(sinp.get()); }
++inline bool sinp_is_any(const c_sin_unique_ptr& sinp) { return sin_is_any(sinp.get()); }
++inline bool sinp6_is_any(const sin6_unique_ptr& sin6p) { return sin6_is_any(sin6p.get()); }
++inline bool sinp6_is_any(const c_sin6_unique_ptr& sin6p) { return sin6_is_any(sin6p.get()); }
++
++inline bool sap_is_broadcast(const sa_unique_ptr& sap) { return sa_is_broadcast(sap.get()); }
++inline bool sap_is_broadcast(const c_sa_unique_ptr& sap) { return sa_is_broadcast(sap.get()); }
++inline bool sinp_is_broadcast(const sin_unique_ptr& sap) { return sin_is_broadcast(sap.get()); }
++inline bool sinp_is_broadcast(const c_sin_unique_ptr& sap) { return sin_is_broadcast(sap.get()); }
++
++inline bool sap_is_v4mapped(const sa_unique_ptr& sap) { return sa_is_v4mapped(sap.get()); }
++inline bool sap_is_v4mapped(const c_sa_unique_ptr& sap) { return sa_is_v4mapped(sap.get()); }
++inline bool sinp6_is_v4mapped(const sin6_unique_ptr& sin6p) { return sin6_is_v4mapped(sin6p.get()); }
++inline bool sinp6_is_v4mapped(const c_sin6_unique_ptr& sin6p) { return sin6_is_v4mapped(sin6p.get()); }
++
++inline bool sap_is_port_any(const sa_unique_ptr& sap) { return sa_is_port_any(sap.get()); }
++inline bool sap_is_port_any(const c_sa_unique_ptr& sap) { return sa_is_port_any(sap.get()); }
++
++inline size_t sap_length(const sa_unique_ptr& sap) { return sa_length(sap.get()); }
++inline size_t sap_length(const c_sa_unique_ptr& sap) { return sa_length(sap.get()); }
++
++inline sa_unique_ptr sap_copy(const sa_unique_ptr& sap) { return sa_copy(sap.get()); }
++inline sa_unique_ptr sap_copy(const c_sa_unique_ptr& sap) { return sa_copy(sap.get()); }
++inline sa_unique_ptr sap_copy_addr(const sa_unique_ptr& sap, uint16_t port = 0) { return sa_copy_addr(sap.get(), port); }
++inline sa_unique_ptr sap_copy_addr(const c_sa_unique_ptr& sap, uint16_t port = 0) { return sa_copy_addr(sap.get(), port); }
++inline sa_unique_ptr sap_copy_in(const sin_unique_ptr& sinp) { return sa_copy_in(sinp.get()); }
++inline sa_unique_ptr sap_copy_in(const c_sin_unique_ptr& sinp) { return sa_copy_in(sinp.get()); }
++inline sa_unique_ptr sap_copy_in6(const sin6_unique_ptr& sin6p) { return sa_copy_in6(sin6p.get()); }
++inline sa_unique_ptr sap_copy_in6(const c_sin6_unique_ptr& sin6p) { return sa_copy_in6(sin6p.get()); }
++
++inline sa_unique_ptr sap_from_v4mapped(const sa_unique_ptr& sap) { return sa_from_v4mapped(sap.get()); }
++inline sa_unique_ptr sap_from_v4mapped(const c_sa_unique_ptr& sap) { return sa_from_v4mapped(sap.get()); }
++inline sa_unique_ptr sap_to_v4mapped(const sa_unique_ptr& sap) { return sa_to_v4mapped(sap.get()); }
++inline sa_unique_ptr sap_to_v4mapped(const c_sa_unique_ptr& sap) { return sa_to_v4mapped(sap.get()); }
++inline sin_unique_ptr sinp_from_v4mapped_in6(const sin6_unique_ptr& sin6p) { return sin_from_v4mapped_in6(sin6p.get()); }
++inline sin_unique_ptr sinp_from_v4mapped_in6(const c_sin6_unique_ptr& sin6p) { return sin_from_v4mapped_in6(sin6p.get()); }
++inline sin6_unique_ptr sin6p_to_v4mapped_in(const sin_unique_ptr& sinp) { return sin6_to_v4mapped_in(sinp.get()); }
++inline sin6_unique_ptr sin6p_to_v4mapped_in(const c_sin_unique_ptr& sinp) { return sin6_to_v4mapped_in(sinp.get()); }
++
++inline uint16_t sap_port(const sa_unique_ptr& sap) { return sa_port(sap.get()); }
++inline uint16_t sap_port(const c_sa_unique_ptr& sap) { return sa_port(sap.get()); }
++inline void sap_set_port(const sa_unique_ptr& sap, uint16_t port) { sa_set_port(sap.get(), port); }
++
++inline bool sap_equal(const sa_unique_ptr& lhs, const sa_unique_ptr& rhs) { return sa_equal(lhs.get(), rhs.get()); }
++inline bool sap_equal(const sa_unique_ptr& lhs, const c_sa_unique_ptr& rhs) { return sa_equal(lhs.get(), rhs.get()); }
++inline bool sap_equal(const c_sa_unique_ptr& lhs, const sa_unique_ptr& rhs) { return sa_equal(lhs.get(), rhs.get()); }
++inline bool sap_equal(const c_sa_unique_ptr& lhs, const c_sa_unique_ptr& rhs) { return sa_equal(lhs.get(), rhs.get()); }
++inline bool sinp_equal(const sin_unique_ptr& lhs, const sin_unique_ptr& rhs) { return sin_equal(lhs.get(), rhs.get()); }
++inline bool sinp_equal(const sin_unique_ptr& lhs, const c_sin_unique_ptr& rhs) { return sin_equal(lhs.get(), rhs.get()); }
++inline bool sinp_equal(const c_sin_unique_ptr& lhs, const sin_unique_ptr& rhs) { return sin_equal(lhs.get(), rhs.get()); }
++inline bool sinp_equal(const c_sin_unique_ptr& lhs, const c_sin_unique_ptr& rhs) { return sin_equal(lhs.get(), rhs.get()); }
++inline bool sin6p_equal(const sin6_unique_ptr& lhs, const sin6_unique_ptr& rhs) { return sin6_equal(lhs.get(), rhs.get()); }
++inline bool sin6p_equal(const sin6_unique_ptr& lhs, const c_sin6_unique_ptr& rhs) { return sin6_equal(lhs.get(), rhs.get()); }
++inline bool sin6p_equal(const c_sin6_unique_ptr& lhs, const sin6_unique_ptr& rhs) { return sin6_equal(lhs.get(), rhs.get()); }
++inline bool sin6p_equal(const c_sin6_unique_ptr& lhs, const c_sin6_unique_ptr& rhs) { return sin6_equal(lhs.get(), rhs.get()); }
++
++inline bool sap_equal_addr(const sa_unique_ptr& lhs, const sa_unique_ptr& rhs) { return sa_equal_addr(lhs.get(), rhs.get()); }
++inline bool sap_equal_addr(const sa_unique_ptr& lhs, const c_sa_unique_ptr& rhs) { return sa_equal_addr(lhs.get(), rhs.get()); }
++inline bool sap_equal_addr(const c_sa_unique_ptr& lhs, const sa_unique_ptr& rhs) { return sa_equal_addr(lhs.get(), rhs.get()); }
++inline bool sap_equal_addr(const c_sa_unique_ptr& lhs, const c_sa_unique_ptr& rhs) { return sa_equal_addr(lhs.get(), rhs.get()); }
++inline bool sinp_equal_addr(const sin_unique_ptr& lhs, const sin_unique_ptr& rhs) { return sin_equal_addr(lhs.get(), rhs.get()); }
++inline bool sinp_equal_addr(const sin_unique_ptr& lhs, const c_sin_unique_ptr& rhs) { return sin_equal_addr(lhs.get(), rhs.get()); }
++inline bool sinp_equal_addr(const c_sin_unique_ptr& lhs, const sin_unique_ptr& rhs) { return sin_equal_addr(lhs.get(), rhs.get()); }
++inline bool sinp_equal_addr(const c_sin_unique_ptr& lhs, const c_sin_unique_ptr& rhs) { return sin_equal_addr(lhs.get(), rhs.get()); }
++inline bool sin6p_equal_addr(const sin6_unique_ptr& lhs, const sin6_unique_ptr& rhs) { return sin6_equal_addr(lhs.get(), rhs.get()); }
++inline bool sin6p_equal_addr(const sin6_unique_ptr& lhs, const c_sin6_unique_ptr& rhs) { return sin6_equal_addr(lhs.get(), rhs.get()); }
++inline bool sin6p_equal_addr(const c_sin6_unique_ptr& lhs, const sin6_unique_ptr& rhs) { return sin6_equal_addr(lhs.get(), rhs.get()); }
++inline bool sin6p_equal_addr(const c_sin6_unique_ptr& lhs, const c_sin6_unique_ptr& rhs) { return sin6_equal_addr(lhs.get(), rhs.get()); }
++
++inline std::string sap_addr_str(const sa_unique_ptr& sap) { return sa_addr_str(sap.get()); }
++inline std::string sap_addr_str(const c_sa_unique_ptr& sap) { return sa_addr_str(sap.get()); }
++inline std::string sap_pretty_str(const sa_unique_ptr& sap) { return sa_pretty_str(sap.get()); }
++inline std::string sap_pretty_str(const c_sa_unique_ptr& sap) { return sa_pretty_str(sap.get()); }
++
++//
++// Implementations:
++//
++
++inline sa_unique_ptr
++sa_from_v4mapped_in6(const sockaddr_in6* sin6) {
++ return sa_from_in(sin_from_v4mapped_in6(sin6));
++}
++
++inline sa_unique_ptr
++sa_to_v4mapped_in(const sockaddr_in* sin) {
++ return sa_from_in6(sin6_to_v4mapped_in(sin));
++}
++
++inline sa_unique_ptr
++sa_from_in(sin_unique_ptr&& sinp) {
++ return sa_unique_ptr(reinterpret_cast<sockaddr*>(sinp.release()));
++}
++
++inline c_sa_unique_ptr
++sa_from_in(c_sin_unique_ptr&& sinp) {
++ return c_sa_unique_ptr(reinterpret_cast<const sockaddr*>(sinp.release()));
++}
++
++inline sa_unique_ptr
++sa_from_in6(sin6_unique_ptr&& sin6p) {
++ return sa_unique_ptr(reinterpret_cast<sockaddr*>(sin6p.release()));
++}
++
++inline c_sa_unique_ptr
++sa_from_in6(c_sin6_unique_ptr&& sin6p) {
++ return c_sa_unique_ptr(reinterpret_cast<const sockaddr*>(sin6p.release()));
++}
++
++inline bool
++fd_sap_equal(const fd_sap_tuple& lhs, const fd_sap_tuple& rhs) {
++ return std::get<0>(lhs) == std::get<0>(rhs) && sap_equal(std::get<1>(lhs), std::get<1>(rhs));
++}
++
++}
++
++#endif
+diff --git a/src/torrent/net/socket_address_key.h b/src/torrent/net/socket_address_key.h
+index 9d6e0c49..0fd0feb3 100644
+--- a/src/torrent/net/socket_address_key.h
++++ b/src/torrent/net/socket_address_key.h
+@@ -5,7 +5,7 @@
+ #define LIBTORRENT_UTILS_SOCKET_ADDRESS_KEY_H
+
+ #include <cstring>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <netinet/in.h>
+
+ // Unique key for the socket address, excluding port numbers, etc.
+diff --git a/src/torrent/net/socket_event.cc b/src/torrent/net/socket_event.cc
+new file mode 100644
+index 00000000..e6805290
+--- /dev/null
++++ b/src/torrent/net/socket_event.cc
+@@ -0,0 +1,29 @@
++#include "config.h"
++
++#include "socket_event.h"
++
++#include "torrent/exceptions.h"
++
++namespace torrent {
++
++socket_event::~socket_event() {
++ if (is_open())
++ throw internal_error("Called socket_event::~socket_event while still open on type " + std::string(type_name()));
++}
++
++void
++socket_event::event_read() {
++ throw internal_error("Called unsupported socket_event::event_read on type " + std::string(type_name()));
++}
++
++void
++socket_event::event_write() {
++ throw internal_error("Called unsupported socket_event::event_write on type " + std::string(type_name()));
++}
++
++void
++socket_event::event_error() {
++ throw internal_error("Called unsupported socket_event::event_error on type " + std::string(type_name()));
++}
++
++}
+diff --git a/src/torrent/net/socket_event.h b/src/torrent/net/socket_event.h
+new file mode 100644
+index 00000000..d9904bd6
+--- /dev/null
++++ b/src/torrent/net/socket_event.h
+@@ -0,0 +1,31 @@
++#ifndef LIBTORRENT_SOCKET_EVENT_H
++#define LIBTORRENT_SOCKET_EVENT_H
++
++#include <cinttypes>
++
++#include "torrent/event.h"
++#include "torrent/net/socket_address.h"
++
++namespace torrent {
++
++class LIBTORRENT_EXPORT socket_event : public Event {
++public:
++ ~socket_event() override;
++
++ const sockaddr* socket_address() const;
++ uint16_t socket_address_port() const;
++
++ void event_read() override;
++ void event_write() override;
++ void event_error() override;
++
++protected:
++ sa_unique_ptr m_socket_address;
++};
++
++inline const sockaddr* socket_event::socket_address() const { return m_socket_address.get(); }
++inline uint16_t socket_event::socket_address_port() const { return sap_port(m_socket_address); }
++
++}
++
++#endif
+diff --git a/src/torrent/net/types.h b/src/torrent/net/types.h
+new file mode 100644
+index 00000000..016e8b85
+--- /dev/null
++++ b/src/torrent/net/types.h
+@@ -0,0 +1,33 @@
++#ifndef LIBTORRENT_NET_TYPES_H
++#define LIBTORRENT_NET_TYPES_H
++
++#include <memory>
++#include <tuple>
++#include <sys/socket.h>
++
++struct sockaddr_in;
++struct sockaddr_in6;
++struct sockaddr_un;
++
++namespace torrent {
++
++typedef std::unique_ptr<sockaddr> sa_unique_ptr;
++typedef std::unique_ptr<sockaddr_in> sin_unique_ptr;
++typedef std::unique_ptr<sockaddr_in6> sin6_unique_ptr;
++typedef std::unique_ptr<sockaddr_un> sun_unique_ptr;
++
++typedef std::unique_ptr<const sockaddr> c_sa_unique_ptr;
++typedef std::unique_ptr<const sockaddr_in> c_sin_unique_ptr;
++typedef std::unique_ptr<const sockaddr_in6> c_sin6_unique_ptr;
++typedef std::unique_ptr<const sockaddr_un> c_sun_unique_ptr;
++
++typedef std::tuple<int, std::unique_ptr<sockaddr>> fd_sap_tuple;
++
++struct listen_result_type {
++ int fd;
++ sa_unique_ptr address;
++};
++
++}
++
++#endif
+diff --git a/src/torrent/object.h b/src/torrent/object.h
+index 3325a434..3f9fe7e4 100644
+--- a/src/torrent/object.h
++++ b/src/torrent/object.h
+@@ -37,8 +37,9 @@
+ #ifndef LIBTORRENT_OBJECT_H
+ #define LIBTORRENT_OBJECT_H
+
+-#include <string>
++#include <limits>
+ #include <map>
++#include <string>
+ #include <vector>
+ #include <torrent/common.h>
+ #include <torrent/exceptions.h>
+@@ -162,6 +163,7 @@ public:
+ string_type& as_string() { check_throw(TYPE_STRING); return _string(); }
+ const string_type& as_string() const { check_throw(TYPE_STRING); return _string(); }
+ const string_type& as_string_c() const { check_throw(TYPE_STRING); return _string(); }
++ const char* as_c_str() const { check_throw(TYPE_STRING); return _string().c_str(); }
+ list_type& as_list() { check_throw(TYPE_LIST); return _list(); }
+ const list_type& as_list() const { check_throw(TYPE_LIST); return _list(); }
+ map_type& as_map() { check_throw(TYPE_MAP); return _map(); }
+@@ -179,6 +181,8 @@ public:
+ raw_map& as_raw_map() { check_throw(TYPE_RAW_MAP); return _raw_map(); }
+ const raw_map& as_raw_map() const { check_throw(TYPE_RAW_MAP); return _raw_map(); }
+
++ template <typename T> T as_value_type(const char* err_msg) const { check_value_throw<T>(err_msg); return _value(); }
++
+ bool has_key(const key_type& k) const { check_throw(TYPE_MAP); return _map().find(k) != _map().end(); }
+ bool has_key_value(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_VALUE); }
+ bool has_key_string(const key_type& k) const { check_throw(TYPE_MAP); return check(_map().find(k), TYPE_STRING); }
+@@ -246,6 +250,8 @@ public:
+ inline bool check(map_type::const_iterator itr, type_type t) const { return itr != _map().end() && itr->second.type() == t; }
+ inline void check_throw(type_type t) const { if (t != type()) throw bencode_error("Wrong object type."); }
+
++ template <typename T> void check_value_throw(const char* err_msg) const;
++
+ uint32_t m_flags;
+
+ #ifndef HAVE_STDCXX_0X
+@@ -484,6 +490,19 @@ object_equal(const Object& left, const Object& right) {
+ }
+ }
+
++template <typename T>
++inline void
++Object::check_value_throw(const char* err_msg) const {
++ if (!std::numeric_limits<T>::is_integer)
++ throw internal_error("Tried to check value with non-integer type.");
++
++ if (!is_value())
++ throw bencode_error(err_msg);
++
++ if (!(_value() >= std::numeric_limits<T>::min() && _value() <= std::numeric_limits<T>::max()))
++ throw bencode_error(err_msg);
++}
++
+ }
+
+ #endif
+diff --git a/src/torrent/peer/client_list.cc b/src/torrent/peer/client_list.cc
+index c857f62d..9c18aa50 100644
+--- a/src/torrent/peer/client_list.cc
++++ b/src/torrent/peer/client_list.cc
+@@ -37,8 +37,8 @@
+ #include "config.h"
+
+ #include <algorithm>
++#include <functional>
+ #include <rak/string_manip.h>
+-#include lt_tr1_functional
+
+ #include "client_list.h"
+ #include "exceptions.h"
+diff --git a/src/torrent/peer/connection_list.h b/src/torrent/peer/connection_list.h
+index ec26835f..eb058784 100644
+--- a/src/torrent/peer/connection_list.h
++++ b/src/torrent/peer/connection_list.h
+@@ -37,9 +37,10 @@
+ #ifndef LIBTORRENT_PEER_CONNECTION_LIST_H
+ #define LIBTORRENT_PEER_CONNECTION_LIST_H
+
++#include <functional>
+ #include <list>
+ #include <vector>
+-#include lt_tr1_functional
++
+ #include <torrent/common.h>
+ #include <torrent/hash_string.h>
+
+diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc
+index aa60939a..080a7f13 100644
+--- a/src/torrent/peer/peer_list.cc
++++ b/src/torrent/peer/peer_list.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include <algorithm>
+ #include <functional>
+ #include <rak/functional.h>
+@@ -56,6 +54,8 @@
+
+ #define LT_LOG_EVENTS(log_fmt, ...) \
+ lt_log_print_info(LOG_PEER_LIST_EVENTS, m_info, "peer_list", log_fmt, __VA_ARGS__);
++#define LT_LOG_ADDRESS(log_fmt, ...) \
++ lt_log_print_info(LOG_PEER_LIST_ADDRESS, m_info, "peer_list", log_fmt, __VA_ARGS__);
+ #define LT_LOG_SA_FMT "'%s:%" PRIu16 "'"
+
+ namespace torrent {
+@@ -196,6 +196,7 @@ PeerList::insert_available(const void* al) {
+ for (; itr != last; itr++) {
+ if (!socket_address_key::is_comparable_sockaddr(itr->c_sockaddr()) || itr->port() == 0) {
+ invalid++;
++ LT_LOG_ADDRESS("skipped invalid address " LT_LOG_SA_FMT, itr->address_str().c_str(), itr->port());
+ continue;
+ }
+
+@@ -242,6 +243,8 @@ PeerList::insert_available(const void* al) {
+
+ inserted++;
+ m_available_list->push_back(&*itr);
++
++ LT_LOG_ADDRESS("added available address " LT_LOG_SA_FMT, itr->address_str().c_str(), itr->port());
+ }
+
+ LT_LOG_EVENTS("inserted peers"
+diff --git a/src/torrent/poll.h b/src/torrent/poll.h
+index b12c8ec2..15a73897 100644
+--- a/src/torrent/poll.h
++++ b/src/torrent/poll.h
+@@ -37,8 +37,7 @@
+ #ifndef LIBTORRENT_TORRENT_POLL_H
+ #define LIBTORRENT_TORRENT_POLL_H
+
+-#include lt_tr1_functional
+-
++#include <functional>
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/torrent.cc b/src/torrent/torrent.cc
+index 339c2c4f..fb70d247 100644
+--- a/src/torrent/torrent.cc
++++ b/src/torrent/torrent.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <rak/address_info.h>
+@@ -139,8 +103,8 @@ main_thread() {
+
+ ChunkManager* chunk_manager() { return manager->chunk_manager(); }
+ ClientList* client_list() { return manager->client_list(); }
+-FileManager* file_manager() { return manager->file_manager(); }
+ ConnectionManager* connection_manager() { return manager->connection_manager(); }
++FileManager* file_manager() { return manager->file_manager(); }
+ DhtManager* dht_manager() { return manager->dht_manager(); }
+ ResourceManager* resource_manager() { return manager->resource_manager(); }
+
+@@ -189,8 +153,10 @@ download_add(Object* object) {
+ download->main()->set_metadata_size(metadata_size);
+ }
+
++ std::string local_id = PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size());
++
+ download->set_hash_queue(manager->hash_queue());
+- download->initialize(infoHash, PEER_NAME + rak::generate_random<std::string>(20 - std::string(PEER_NAME).size()));
++ download->initialize(infoHash, local_id);
+
+ // Add trackers, etc, after setting the info hash so that log
+ // entries look sane.
+diff --git a/src/torrent/torrent.h b/src/torrent/torrent.h
+index 7bcf88fe..0cdfdaa7 100644
+--- a/src/torrent/torrent.h
++++ b/src/torrent/torrent.h
+@@ -44,6 +44,11 @@
+
+ namespace torrent {
+
++class FileManager;
++class ResourceManager;
++
++class thread_base;
++
+ // Make sure you seed srandom and srand48 if available.
+ void initialize() LIBTORRENT_EXPORT;
+
+@@ -53,16 +58,12 @@ void cleanup() LIBTORRENT_EXPORT;
+
+ bool is_inactive() LIBTORRENT_EXPORT;
+
+-class FileManager;
+-class ResourceManager;
+-class thread_base;
+-
+ thread_base* main_thread() LIBTORRENT_EXPORT;
+
+ ChunkManager* chunk_manager() LIBTORRENT_EXPORT;
+ ClientList* client_list() LIBTORRENT_EXPORT;
+-FileManager* file_manager() LIBTORRENT_EXPORT;
+ ConnectionManager* connection_manager() LIBTORRENT_EXPORT;
++FileManager* file_manager() LIBTORRENT_EXPORT;
+ DhtManager* dht_manager() LIBTORRENT_EXPORT;
+ ResourceManager* resource_manager() LIBTORRENT_EXPORT;
+
+diff --git a/src/torrent/tracker.h b/src/torrent/tracker.h
+index a528ef6a..bd7546a9 100644
+--- a/src/torrent/tracker.h
++++ b/src/torrent/tracker.h
+@@ -2,7 +2,7 @@
+ #define LIBTORRENT_TRACKER_H
+
+ #include <string>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/tracker_controller.h b/src/torrent/tracker_controller.h
+index 70d1b43f..9452be0f 100644
+--- a/src/torrent/tracker_controller.h
++++ b/src/torrent/tracker_controller.h
+@@ -37,8 +37,9 @@
+ #ifndef LIBTORRENT_TRACKER_CONTROLLER_H
+ #define LIBTORRENT_TRACKER_CONTROLLER_H
+
++#include <functional>
+ #include <string>
+-#include lt_tr1_functional
++
+ #include <torrent/common.h>
+ #include <torrent/tracker.h>
+
+diff --git a/src/torrent/tracker_list.h b/src/torrent/tracker_list.h
+index c6817b3a..bb06f8af 100644
+--- a/src/torrent/tracker_list.h
++++ b/src/torrent/tracker_list.h
+@@ -38,10 +38,10 @@
+ #define LIBTORRENT_TRACKER_LIST_H
+
+ #include <algorithm>
++#include <functional>
+ #include <string>
+ #include <vector>
+ #include <torrent/common.h>
+-#include lt_tr1_functional
+
+ namespace torrent {
+
+diff --git a/src/torrent/utils/Makefile.am b/src/torrent/utils/Makefile.am
+index 51c9a026..a48786c6 100644
+--- a/src/torrent/utils/Makefile.am
++++ b/src/torrent/utils/Makefile.am
+@@ -8,10 +8,10 @@ libsub_torrentutils_la_SOURCES = \
+ log.h \
+ log_buffer.cc \
+ log_buffer.h \
+- net.cc \
+- net.h \
+ option_strings.cc \
+ option_strings.h \
++ random.cc \
++ random.h \
+ ranges.h \
+ resume.cc \
+ resume.h \
+@@ -32,7 +32,6 @@ libtorrentinclude_HEADERS = \
+ extents.h \
+ log.h \
+ log_buffer.h \
+- net.h \
+ option_strings.h \
+ ranges.h \
+ resume.h \
+diff --git a/src/torrent/utils/directory_events.h b/src/torrent/utils/directory_events.h
+index 30fa0508..fd9246c5 100644
+--- a/src/torrent/utils/directory_events.h
++++ b/src/torrent/utils/directory_events.h
+@@ -37,9 +37,10 @@
+ #ifndef LIBTORRENT_DIRECTORY_EVENTS_H
+ #define LIBTORRENT_DIRECTORY_EVENTS_H
+
++#include <functional>
+ #include <string>
+ #include <vector>
+-#include lt_tr1_functional
++
+ #include <torrent/event.h>
+
+ namespace torrent {
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index 6c605474..b855a2c6 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -1,45 +1,6 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include "log.h"
+-#include "log_buffer.h"
+
+ #include "globals.h"
+ #include "torrent/exceptions.h"
+@@ -54,8 +15,6 @@
+ #include <fstream>
+ #include <functional>
+ #include <memory>
+-#include lt_tr1_functional
+-#include lt_tr1_memory
+
+ namespace torrent {
+
+@@ -232,7 +191,6 @@ log_initialize() {
+
+ LOG_CASCADE(LOG_CRITICAL);
+
+- LOG_CASCADE(LOG_CONNECTION_CRITICAL);
+ LOG_CASCADE(LOG_PEER_CRITICAL);
+ LOG_CASCADE(LOG_SOCKET_CRITICAL);
+ LOG_CASCADE(LOG_STORAGE_CRITICAL);
+@@ -240,7 +198,6 @@ log_initialize() {
+ LOG_CASCADE(LOG_TRACKER_CRITICAL);
+ LOG_CASCADE(LOG_TORRENT_CRITICAL);
+
+- LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_CONNECTION_CRITICAL);
+ LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_PEER_CRITICAL);
+ LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_SOCKET_CRITICAL);
+ LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_STORAGE_CRITICAL);
+@@ -248,6 +205,12 @@ log_initialize() {
+ LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_TRACKER_CRITICAL);
+ LOG_CHILDREN_CASCADE(LOG_CRITICAL, LOG_TORRENT_CRITICAL);
+
++ LOG_LINK(LOG_CONNECTION, LOG_CONNECTION_BIND);
++ LOG_LINK(LOG_CONNECTION, LOG_CONNECTION_FD);
++ LOG_LINK(LOG_CONNECTION, LOG_CONNECTION_FILTER);
++ LOG_LINK(LOG_CONNECTION, LOG_CONNECTION_HANDSHAKE);
++ LOG_LINK(LOG_CONNECTION, LOG_CONNECTION_LISTEN);
++
+ LOG_LINK(LOG_DHT_ALL, LOG_DHT_MANAGER);
+ LOG_LINK(LOG_DHT_ALL, LOG_DHT_NODE);
+ LOG_LINK(LOG_DHT_ALL, LOG_DHT_ROUTER);
+@@ -447,21 +410,4 @@ log_open_gz_file_output(const char* name, const char* filename, bool append) {
+ std::placeholders::_3));
+ }
+
+-log_buffer*
+-log_open_log_buffer(const char* name) {
+- log_buffer* buffer = new log_buffer;
+-
+- try {
+- log_open_output(name, std::bind(&log_buffer::lock_and_push_log, buffer,
+- std::placeholders::_1,
+- std::placeholders::_2,
+- std::placeholders::_3));
+- return buffer;
+-
+- } catch (torrent::input_error& e) {
+- delete buffer;
+- throw;
+- }
+-}
+-
+ }
+diff --git a/src/torrent/utils/log.h b/src/torrent/utils/log.h
+index 0dfdc86b..fe6127d6 100644
+--- a/src/torrent/utils/log.h
++++ b/src/torrent/utils/log.h
+@@ -1,47 +1,12 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_UTILS_LOG_H
+ #define LIBTORRENT_UTILS_LOG_H
+
++#include <array>
+ #include <bitset>
++#include <functional>
+ #include <string>
+ #include <vector>
+-#include lt_tr1_array
+-#include lt_tr1_functional
++
+ #include <torrent/common.h>
+
+ namespace torrent {
+@@ -55,13 +20,6 @@ enum {
+ LOG_INFO,
+ LOG_DEBUG,
+
+- LOG_CONNECTION_CRITICAL,
+- LOG_CONNECTION_ERROR,
+- LOG_CONNECTION_WARN,
+- LOG_CONNECTION_NOTICE,
+- LOG_CONNECTION_INFO,
+- LOG_CONNECTION_DEBUG,
+-
+ LOG_DHT_CRITICAL,
+ LOG_DHT_ERROR,
+ LOG_DHT_WARN,
+@@ -113,6 +71,14 @@ enum {
+
+ LOG_NON_CASCADING,
+
++ LOG_CONNECTION,
++ LOG_CONNECTION_BIND,
++ LOG_CONNECTION_FD,
++ LOG_CONNECTION_FILTER,
++ LOG_CONNECTION_HANDSHAKE,
++ LOG_CONNECTION_LISTEN,
++
++ // TODO: Rename dht_all to just dht.
+ LOG_DHT_ALL,
+ LOG_DHT_MANAGER,
+ LOG_DHT_NODE,
+@@ -125,7 +91,10 @@ enum {
+ LOG_INSTRUMENTATION_POLLING,
+ LOG_INSTRUMENTATION_TRANSFERS,
+
++ LOG_MOCK_CALLS,
++
+ LOG_PEER_LIST_EVENTS,
++ LOG_PEER_LIST_ADDRESS,
+
+ LOG_PROTOCOL_PIECE_EVENTS,
+ LOG_PROTOCOL_METADATA_EVENTS,
+@@ -137,6 +106,8 @@ enum {
+ LOG_RPC_EVENTS,
+ LOG_RPC_DUMP,
+
++ LOG_SYSTEM,
++
+ LOG_UI_EVENTS,
+
+ LOG_GROUP_MAX_SIZE
+@@ -145,34 +116,32 @@ enum {
+ #define lt_log_is_valid(log_group) (torrent::log_groups[log_group].valid())
+
+ #define lt_log_print(log_group, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(NULL, NULL, NULL, 0, __VA_ARGS__);
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(NULL, NULL, NULL, 0, __VA_ARGS__); }
+
+ #define lt_log_print_info(log_group, log_info, log_subsystem, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, NULL, 0, __VA_ARGS__);
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, NULL, 0, __VA_ARGS__); }
+
+ #define lt_log_print_data(log_group, log_data, log_subsystem, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(&log_data->hash(), log_subsystem, NULL, 0, __VA_ARGS__);
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(&log_data->hash(), log_subsystem, NULL, 0, __VA_ARGS__); }
+
+ #define lt_log_print_dump(log_group, log_dump_data, log_dump_size, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(NULL, NULL, log_dump_data, log_dump_size, __VA_ARGS__); \
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(NULL, NULL, log_dump_data, log_dump_size, __VA_ARGS__); }
+
+ #define lt_log_print_hash(log_group, log_hash, log_subsystem, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(&log_hash, log_subsystem, NULL, 0, __VA_ARGS__);
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(&log_hash, log_subsystem, NULL, 0, __VA_ARGS__); }
+
+ #define lt_log_print_info_dump(log_group, log_dump_data, log_dump_size, log_info, log_subsystem, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, log_dump_data, log_dump_size, __VA_ARGS__); \
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(&log_info->hash(), log_subsystem, log_dump_data, log_dump_size, __VA_ARGS__); }
+
+ #define lt_log_print_subsystem(log_group, log_subsystem, ...) \
+- if (torrent::log_groups[log_group].valid()) \
+- torrent::log_groups[log_group].internal_print(NULL, log_subsystem, NULL, 0, __VA_ARGS__);
+-
+-class log_buffer;
++ { if (torrent::log_groups[log_group].valid()) \
++ torrent::log_groups[log_group].internal_print(NULL, log_subsystem, NULL, 0, __VA_ARGS__); }
+
+ typedef std::function<void (const char*, unsigned int, int)> log_slot;
+
+@@ -222,7 +191,7 @@ private:
+
+ typedef std::array<log_group, LOG_GROUP_MAX_SIZE> log_group_list;
+
+-extern log_group_list log_groups LIBTORRENT_EXPORT;
++extern log_group_list log_groups LIBTORRENT_EXPORT;
+
+ void log_initialize() LIBTORRENT_EXPORT;
+ void log_cleanup() LIBTORRENT_EXPORT;
+@@ -237,9 +206,8 @@ void log_remove_group_output(int group, const char* name) LIBTORRENT_EXPORT;
+ void log_add_child(int group, int child) LIBTORRENT_EXPORT;
+ void log_remove_child(int group, int child) LIBTORRENT_EXPORT;
+
+-void log_open_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
+-void log_open_gz_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
+-log_buffer* log_open_log_buffer(const char* name) LIBTORRENT_EXPORT;
++void log_open_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
++void log_open_gz_file_output(const char* name, const char* filename, bool append = false) LIBTORRENT_EXPORT;
+
+ //
+ // Implementation:
+diff --git a/src/torrent/utils/log_buffer.cc b/src/torrent/utils/log_buffer.cc
+index f82d57e0..5bf159a4 100644
+--- a/src/torrent/utils/log_buffer.cc
++++ b/src/torrent/utils/log_buffer.cc
+@@ -1,46 +1,8 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include "log_buffer.h"
+
+-#include <functional>
+-#include lt_tr1_functional
+-
++#include "log.h"
+ #include "globals.h"
+
+ namespace torrent {
+@@ -72,4 +34,19 @@ log_buffer::lock_and_push_log(const char* data, size_t length, int group) {
+ unlock();
+ }
+
++static void
++log_buffer_deleter(log_buffer* lb) {
++ delete lb;
++}
++
++log_buffer_ptr
++log_open_log_buffer(const char* name) {
++ // TODO: Deregister when deleting.
++ auto buffer = log_buffer_ptr(new log_buffer, std::bind(&log_buffer_deleter, std::placeholders::_1));
++
++ log_open_output(name, std::bind(&log_buffer::lock_and_push_log, buffer.get(),
++ std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
++ return buffer;
++}
++
+ }
+diff --git a/src/torrent/utils/log_buffer.h b/src/torrent/utils/log_buffer.h
+index befd780b..259e5910 100644
+--- a/src/torrent/utils/log_buffer.h
++++ b/src/torrent/utils/log_buffer.h
+@@ -1,47 +1,11 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-#ifndef LIBTORRENT_UTILS_LOG_BUFFER_H
+-#define LIBTORRENT_UTILS_LOG_BUFFER_H
++#ifndef LIBTORRENT_TORRENT_UTILS_LOG_BUFFER_H
++#define LIBTORRENT_TORRENT_UTILS_LOG_BUFFER_H
+
+ #include <string>
+ #include <deque>
++#include <functional>
++#include <memory>
+ #include <pthread.h>
+-#include lt_tr1_functional
+-#include <torrent/common.h>
+
+ namespace torrent {
+
+@@ -57,9 +21,9 @@ struct log_entry {
+ std::string message;
+ };
+
+-class LIBTORRENT_EXPORT log_buffer : private std::deque<log_entry> {
++class [[gnu::visibility("default")]] log_buffer : private std::deque<log_entry> {
+ public:
+- typedef std::deque<log_entry> base_type;
++ typedef std::deque<log_entry> base_type;
+ typedef std::function<void ()> slot_void;
+
+ using base_type::iterator;
+@@ -97,6 +61,10 @@ private:
+ slot_void m_slot_update;
+ };
+
++typedef std::unique_ptr<log_buffer, std::function<void (log_buffer*)>> log_buffer_ptr;
++
++[[gnu::visibility("default")]] log_buffer_ptr log_open_log_buffer(const char* name);
++
+ }
+
+ #endif
+diff --git a/src/torrent/utils/net.cc b/src/torrent/utils/net.cc
+deleted file mode 100644
+index 83c9b506..00000000
+--- a/src/torrent/utils/net.cc
++++ /dev/null
+@@ -1,72 +0,0 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-#include "config.h"
+-
+-#include "net.h"
+-#include "exceptions.h"
+-
+-#include <cstring>
+-
+-namespace torrent {
+-
+-addrinfo*
+-address_info_lookup(const char* hostname, int family, int socktype) {
+- addrinfo hints;
+- std::memset(&hints, 0, sizeof(addrinfo));
+- hints.ai_family = family;
+- hints.ai_socktype = socktype;
+-
+- addrinfo* res = NULL;
+- int err = ::getaddrinfo(hostname, NULL, &hints, &res);
+-
+- if (err)
+- throw address_info_error(err);
+-
+- return res;
+-}
+-
+-bool
+-address_info_call(addrinfo* ai, int flags, slot_ai_success slot_success) {
+- while (ai != NULL) {
+- slot_success(ai->ai_addr, ai->ai_addrlen);
+- return true;
+- }
+-
+- return false;
+-}
+-
+-}
+diff --git a/src/torrent/utils/net.h b/src/torrent/utils/net.h
+deleted file mode 100644
+index f5af7cc0..00000000
+--- a/src/torrent/utils/net.h
++++ /dev/null
+@@ -1,56 +0,0 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-#ifndef LIBTORRENT_UTILS_NET_H
+-#define LIBTORRENT_UTILS_NET_H
+-
+-#include <netdb.h>
+-#include lt_tr1_functional
+-
+-namespace torrent {
+-
+-typedef std::function<void (sockaddr*, socklen_t)> slot_ai_success;
+-//typedef std::function<void (const char*, int)> slot_ai_failure;
+-
+-// Throws address_info_error on lookup failure.
+-addrinfo* address_info_lookup(const char* hostname, int family, int socktype);
+-inline void address_info_free(addrinfo* ai) { ::freeaddrinfo(ai); }
+-
+-bool address_info_call(addrinfo* ai, int flags, slot_ai_success slot_success);
+-
+-}
+-
+-#endif
+diff --git a/src/torrent/utils/option_strings.cc b/src/torrent/utils/option_strings.cc
+index 5992cf37..101e2688 100644
+--- a/src/torrent/utils/option_strings.cc
++++ b/src/torrent/utils/option_strings.cc
+@@ -62,7 +62,7 @@ struct option_pair {
+ unsigned int value;
+ };
+
+-option_pair option_list_connection[] = {
++option_pair option_list_connection_type[] = {
+ { "leech", Download::CONNECTION_LEECH },
+ { "seed", Download::CONNECTION_SEED },
+ { "initial_seed", Download::CONNECTION_INITIAL_SEED },
+@@ -124,6 +124,19 @@ option_pair option_list_tracker_mode[] = {
+ { NULL, 0 }
+ };
+
++const char* option_list_handshake_connection[] = {
++ "none",
++ "incoming",
++ "outgoing_normal",
++ "outgoing_encrypted",
++ "outgoing_proxy",
++ "success",
++ "dropped",
++ "failed",
++ "retry_plaintext",
++ "retry_encrypted"
++};
++
+ const char* option_list_log_group[] = {
+ "critical",
+ "error",
+@@ -132,13 +145,6 @@ const char* option_list_log_group[] = {
+ "info",
+ "debug",
+
+- "connection_critical",
+- "connection_error",
+- "connection_warn",
+- "connection_notice",
+- "connection_info",
+- "connection_debug",
+-
+ "dht_critical",
+ "dht_error",
+ "dht_warn",
+@@ -190,6 +196,13 @@ const char* option_list_log_group[] = {
+
+ "__non_cascading__",
+
++ "connection",
++ "connection_bind",
++ "connection_fd",
++ "connection_filter",
++ "connection_hanshake",
++ "connection_listen",
++
+ "dht_all",
+ "dht_manager",
+ "dht_node",
+@@ -202,7 +215,10 @@ const char* option_list_log_group[] = {
+ "instrumentation_polling",
+ "instrumentation_transfers",
+
++ "mock_calls",
++
+ "peer_list_events",
++ "peer_list_address",
+
+ "protocol_piece_events",
+ "protocol_metadata_events",
+@@ -214,6 +230,8 @@ const char* option_list_log_group[] = {
+ "rpc_events",
+ "rpc_dump",
+
++ "system",
++
+ "ui_events",
+
+ NULL
+@@ -230,7 +248,7 @@ const char* option_list_tracker_event[] = {
+ };
+
+ option_pair* option_pair_lists[OPTION_START_COMPACT] = {
+- option_list_connection,
++ option_list_connection_type,
+ option_list_heuristics,
+ option_list_heuristics_download,
+ option_list_heuristics_upload,
+@@ -244,6 +262,7 @@ option_pair* option_pair_lists[OPTION_START_COMPACT] = {
+ { sizeof(single_name) / sizeof(const char*) - 1, single_name }
+
+ option_single option_single_lists[OPTION_SINGLE_SIZE] = {
++ OPTION_SINGLE_ENTRY(option_list_handshake_connection),
+ OPTION_SINGLE_ENTRY(option_list_log_group),
+ OPTION_SINGLE_ENTRY(option_list_tracker_event),
+ };
+@@ -267,11 +286,11 @@ option_find_string(option_enum opt_enum, const char* name) {
+ } while (*++itr != NULL);
+ }
+
+- throw input_error("Invalid option name.");
++ throw input_error("Invalid option name.");
+ }
+
+ const char*
+-option_as_string(option_enum opt_enum, unsigned int value) {
++option_to_string(option_enum opt_enum, unsigned int value, const char* not_found) {
+ if (opt_enum < OPTION_START_COMPACT) {
+ option_pair* itr = option_pair_lists[opt_enum];
+
+@@ -285,7 +304,27 @@ option_as_string(option_enum opt_enum, unsigned int value) {
+ return option_single_lists[opt_enum - OPTION_START_COMPACT].name[value];
+ }
+
+- throw input_error("Invalid option value.");
++ return not_found;
++}
++
++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);
++ else
++ return result;
++}
++
++const char*
++option_as_string(option_enum opt_enum, unsigned int value) {
++ const char* result = option_to_string(opt_enum, value, NULL);
++
++ if (result == NULL)
++ throw input_error("Invalid option value.");
++ else
++ return result;
+ }
+
+ torrent::Object
+diff --git a/src/torrent/utils/option_strings.h b/src/torrent/utils/option_strings.h
+index 1b57efa8..f9e5ef77 100644
+--- a/src/torrent/utils/option_strings.h
++++ b/src/torrent/utils/option_strings.h
+@@ -54,17 +54,22 @@ enum option_enum {
+ OPTION_IP_TOS,
+ OPTION_TRACKER_MODE,
+
++ OPTION_HANDSHAKE_CONNECTION,
+ OPTION_LOG_GROUP,
+ OPTION_TRACKER_EVENT,
+
+ OPTION_MAX_SIZE,
+- OPTION_START_COMPACT = OPTION_LOG_GROUP,
++ OPTION_START_COMPACT = OPTION_HANDSHAKE_CONNECTION,
+ OPTION_SINGLE_SIZE = OPTION_MAX_SIZE - OPTION_START_COMPACT
+ };
+
+ 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;
++
++// TODO: Deprecated.
+ const char* option_as_string(option_enum opt_enum, unsigned int value) LIBTORRENT_EXPORT;
+
+ torrent::Object option_list_strings(option_enum opt_enum) LIBTORRENT_EXPORT;
+diff --git a/src/torrent/utils/random.cc b/src/torrent/utils/random.cc
+new file mode 100644
+index 00000000..6a045429
+--- /dev/null
++++ b/src/torrent/utils/random.cc
+@@ -0,0 +1,29 @@
++#include "config.h"
++
++#include "random.h"
++
++#include "torrent/exceptions.h"
++
++namespace torrent {
++
++// TODO: Replace with std and thread_local generator.
++
++template <typename T>
++T
++random_uniform_template(T min, T max) {
++ if (min > max)
++ throw internal_error("random_uniform: min > max");
++
++ if (min == max)
++ return min;
++
++ std::random_device rd;
++ std::mt19937 mt(rd());
++
++ return min + std::uniform_int_distribution<T>(min, max)(mt) % (max - min + 1);
++}
++
++uint16_t random_uniform_uint16(uint16_t min, uint16_t max) { return random_uniform_template<uint16_t>(min, max); }
++uint32_t random_uniform_uint32(uint32_t min, uint32_t max) { return random_uniform_template<uint32_t>(min, max); }
++
++}
+diff --git a/src/torrent/utils/random.h b/src/torrent/utils/random.h
+new file mode 100644
+index 00000000..d5992ab6
+--- /dev/null
++++ b/src/torrent/utils/random.h
+@@ -0,0 +1,15 @@
++#ifndef LIBTORRENT_TORRENT_UTILS_RANDOM_H
++#define LIBTORRENT_TORRENT_UTILS_RANDOM_H
++
++#include <cinttypes>
++#include <limits>
++#include <random>
++
++namespace torrent {
++
++[[gnu::weak]] [[gnu::visibility("default")]] uint16_t random_uniform_uint16(uint16_t min = std::numeric_limits<uint16_t>::min(), uint16_t max = std::numeric_limits<uint16_t>::max());
++[[gnu::weak]] [[gnu::visibility("default")]] uint32_t random_uniform_uint32(uint32_t min = std::numeric_limits<uint32_t>::min(), uint32_t max = std::numeric_limits<uint32_t>::max());
++
++}
++
++#endif
+diff --git a/src/torrent/utils/ranges.h b/src/torrent/utils/ranges.h
+index e784b084..7b1f8cb0 100644
+--- a/src/torrent/utils/ranges.h
++++ b/src/torrent/utils/ranges.h
+@@ -40,7 +40,6 @@
+ #include <algorithm>
+ #include <vector>
+
+-// TODO: Use tr1 functional instead.
+ #include <rak/functional.h>
+
+ namespace torrent {
+diff --git a/src/torrent/utils/resume.cc b/src/torrent/utils/resume.cc
+index 3f528c14..f8467d54 100644
+--- a/src/torrent/utils/resume.cc
++++ b/src/torrent/utils/resume.cc
+@@ -34,8 +34,6 @@
+ // Skomakerveien 33
+ // 3185 Skoppum, NORWAY
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include "config.h"
+
+ #include <rak/file_stat.h>
+diff --git a/src/torrent/utils/signal_bitfield.h b/src/torrent/utils/signal_bitfield.h
+index b9f57a60..ffa336d2 100644
+--- a/src/torrent/utils/signal_bitfield.h
++++ b/src/torrent/utils/signal_bitfield.h
+@@ -37,7 +37,8 @@
+ #ifndef LIBTORRENT_UTILS_SIGNAL_BITFIELD_H
+ #define LIBTORRENT_UTILS_SIGNAL_BITFIELD_H
+
+-#include lt_tr1_functional
++#include <functional>
++
+ #include <torrent/common.h>
+
+ namespace torrent {
+diff --git a/src/torrent/utils/thread_base.h b/src/torrent/utils/thread_base.h
+index bfd443ae..b92a98ba 100644
+--- a/src/torrent/utils/thread_base.h
++++ b/src/torrent/utils/thread_base.h
+@@ -37,11 +37,12 @@
+ #ifndef LIBTORRENT_UTILS_THREAD_BASE_H
+ #define LIBTORRENT_UTILS_THREAD_BASE_H
+
++#include <functional>
+ #include <pthread.h>
+ #include <sys/types.h>
++
+ #include <torrent/common.h>
+ #include <torrent/utils/signal_bitfield.h>
+-#include lt_tr1_functional
+
+ namespace torrent {
+
+diff --git a/src/utils/instrumentation.cc b/src/utils/instrumentation.cc
+index 729b20e2..178d6a19 100644
+--- a/src/utils/instrumentation.cc
++++ b/src/utils/instrumentation.cc
+@@ -36,8 +36,6 @@
+
+ #include "config.h"
+
+-#define __STDC_FORMAT_MACROS
+-
+ #include "instrumentation.h"
+
+ namespace torrent {
+diff --git a/src/utils/instrumentation.h b/src/utils/instrumentation.h
+index 956429bf..11e77f6d 100644
+--- a/src/utils/instrumentation.h
++++ b/src/utils/instrumentation.h
+@@ -37,9 +37,8 @@
+ #ifndef LIBTORRENT_UTILS_INSTRUMENTATION_H
+ #define LIBTORRENT_UTILS_INSTRUMENTATION_H
+
+-#include lt_tr1_array
+-
+ #include <algorithm>
++#include <array>
+
+ #include "torrent/common.h"
+ #include "torrent/utils/log.h"
+diff --git a/src/utils/queue_buckets.h b/src/utils/queue_buckets.h
+index de8584ff..b9174f27 100644
+--- a/src/utils/queue_buckets.h
++++ b/src/utils/queue_buckets.h
+@@ -38,9 +38,9 @@
+ #define LIBTORRENT_QUEUE_BUCKETS_H
+
+ #include <algorithm>
++#include <array>
+ #include <deque>
+-#include lt_tr1_functional
+-#include lt_tr1_array
++#include <functional>
+
+ namespace torrent {
+
+diff --git a/src/utils/sha_fast.h b/src/utils/sha_fast.h
+index f7ce3b87..eb357864 100644
+--- a/src/utils/sha_fast.h
++++ b/src/utils/sha_fast.h
+@@ -41,7 +41,7 @@
+ #ifndef _SHA_FAST_H_
+ #define _SHA_FAST_H_
+
+-#include <inttypes.h>
++#include <cinttypes>
+
+ namespace torrent {
+
+diff --git a/test/Makefile.am b/test/Makefile.am
+index d7a9d5b3..b60a86a6 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -18,6 +18,17 @@ LibTorrentTest_LDADD = \
+ ../src/torrent/utils/libsub_torrentutils.la
+
+ LibTorrentTest_SOURCES = \
++ helpers/expect_fd.h \
++ helpers/expect_utils.h \
++ helpers/mock_compare.h \
++ helpers/mock_function.cc \
++ helpers/mock_function.h \
++ helpers/network.h \
++ helpers/progress_listener.cc \
++ helpers/progress_listener.h \
++ helpers/test_fixture.cc \
++ helpers/test_fixture.h \
++ \
+ ../src/thread_disk.cc \
+ ../src/thread_disk.h \
+ \
+@@ -31,22 +42,28 @@ LibTorrentTest_SOURCES = \
+ data/hash_check_queue_test.h \
+ data/hash_queue_test.cc \
+ data/hash_queue_test.h \
++ \
++ net/test_socket_listen.cc \
++ net/test_socket_listen.h \
++ \
+ protocol/test_request_list.cc \
+ protocol/test_request_list.h \
+ \
+- torrent/net/test_socket_address_key.cc \
+- torrent/net/test_socket_address_key.h \
++ torrent/net/test_address_info.cc \
++ torrent/net/test_address_info.h \
++ torrent/net/test_fd.cc \
++ torrent/net/test_fd.h \
++ torrent/net/test_socket_address.cc \
++ torrent/net/test_socket_address.h \
+ \
+ torrent/utils/log_test.cc \
+ torrent/utils/log_test.h \
+- torrent/utils/log_buffer_test.cc \
+- torrent/utils/log_buffer_test.h \
+- torrent/utils/net_test.cc \
+- torrent/utils/net_test.h \
+ torrent/utils/option_strings_test.cc \
+ torrent/utils/option_strings_test.h \
+ torrent/utils/test_extents.cc \
+ torrent/utils/test_extents.h \
++ torrent/utils/test_log_buffer.cc \
++ torrent/utils/test_log_buffer.h \
+ torrent/utils/test_queue_buckets.cc \
+ torrent/utils/test_queue_buckets.h \
+ torrent/utils/test_uri_parser.cc \
+diff --git a/test/data/hash_check_queue_test.cc b/test/data/hash_check_queue_test.cc
+index c6bdeaec..4b15245e 100644
+--- a/test/data/hash_check_queue_test.cc
++++ b/test/data/hash_check_queue_test.cc
+@@ -1,7 +1,7 @@
+ #include "config.h"
+
++#include <functional>
+ #include <signal.h>
+-#include lt_tr1_functional
+
+ #include "data/hash_queue_node.h"
+ #include "utils/sha1.h"
+diff --git a/test/data/hash_queue_test.cc b/test/data/hash_queue_test.cc
+index 287c28e9..d7ce3ba8 100644
+--- a/test/data/hash_queue_test.cc
++++ b/test/data/hash_queue_test.cc
+@@ -1,7 +1,7 @@
+ #include "config.h"
+
++#include <functional>
+ #include <signal.h>
+-#include lt_tr1_functional
+
+ #include "data/hash_queue_node.h"
+ #include "torrent/chunk_manager.h"
+diff --git a/test/helpers/expect_fd.h b/test/helpers/expect_fd.h
+new file mode 100644
+index 00000000..178cbabc
+--- /dev/null
++++ b/test/helpers/expect_fd.h
+@@ -0,0 +1,107 @@
++#ifndef LIBTORRENT_HELPER_EXPECT_FD_H
++#define LIBTORRENT_HELPER_EXPECT_FD_H
++
++#include "helpers/mock_function.h"
++
++#include <fcntl.h>
++#include <torrent/event.h>
++#include <torrent/net/fd.h>
++#include <torrent/net/socket_address.h>
++
++typedef std::vector<torrent::sa_unique_ptr> sap_cache_type;
++
++inline const sockaddr*
++sap_cache_copy_addr_c_ptr(sap_cache_type& sap_cache, const torrent::c_sa_unique_ptr& sap, uint16_t port = 0) {
++ sap_cache.push_back(torrent::sap_copy_addr(sap, port));
++ return sap_cache.back().get();
++}
++
++inline void
++expect_event_open_re(int idx) {
++ mock_expect(&torrent::poll_event_open, mock_compare_map<torrent::Event>::begin_pointer + idx);
++ mock_expect(&torrent::poll_event_insert_read, mock_compare_map<torrent::Event>::begin_pointer + idx);
++ mock_expect(&torrent::poll_event_insert_error, mock_compare_map<torrent::Event>::begin_pointer + idx);
++}
++
++inline void
++expect_event_closed_fd(int idx, int fd) {
++ mock_expect(&torrent::fd__close, 0, fd);
++ mock_expect(&torrent::poll_event_closed, mock_compare_map<torrent::Event>::begin_pointer + idx);
++}
++
++inline void
++expect_fd_inet_tcp(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++}
++
++inline void
++expect_fd_inet6_tcp(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET6, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++}
++
++inline void
++expect_fd_inet_tcp_nonblock(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++}
++
++inline void
++expect_fd_inet6_tcp_nonblock(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET6, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++}
++
++inline void
++expect_fd_inet_tcp_nonblock_reuseaddr(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++ mock_expect(&torrent::fd__setsockopt_int, 0, fd, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++}
++
++inline void
++expect_fd_inet6_tcp_nonblock_reuseaddr(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET6, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++ mock_expect(&torrent::fd__setsockopt_int, 0, fd, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++}
++
++inline void
++expect_fd_inet6_tcp_v6only_nonblock(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET6, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__setsockopt_int, 0, fd, (int)IPPROTO_IPV6, (int)IPV6_V6ONLY, (int)true);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++}
++
++inline void
++expect_fd_inet6_tcp_v6only_nonblock_reuseaddr(int fd) {
++ mock_expect(&torrent::fd__socket, fd, (int)PF_INET6, (int)SOCK_STREAM, (int)IPPROTO_TCP);
++ mock_expect(&torrent::fd__setsockopt_int, 0, fd, (int)IPPROTO_IPV6, (int)IPV6_V6ONLY, (int)true);
++ mock_expect(&torrent::fd__fcntl_int, 0, fd, F_SETFL, O_NONBLOCK);
++ mock_expect(&torrent::fd__setsockopt_int, 0, fd, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++}
++
++inline void
++expect_fd_bind_connect(int fd, const torrent::c_sa_unique_ptr& bind_sap, const torrent::c_sa_unique_ptr& connect_sap) {
++ mock_expect(&torrent::fd__bind, 0, fd, bind_sap.get(), (socklen_t)torrent::sap_length(bind_sap));
++ mock_expect(&torrent::fd__connect, 0, fd, connect_sap.get(), (socklen_t)torrent::sap_length(connect_sap));
++}
++
++inline void
++expect_fd_bind_fail_range(int fd, sap_cache_type& sap_cache, const torrent::c_sa_unique_ptr& sap, uint16_t first_port, uint16_t last_port) {
++ do {
++ mock_expect(&torrent::fd__bind, -1, fd, sap_cache_copy_addr_c_ptr(sap_cache, sap, first_port), (socklen_t)torrent::sap_length(sap));
++ } while (first_port++ != last_port);
++}
++
++inline void
++expect_fd_bind_listen(int fd, const torrent::c_sa_unique_ptr& sap) {
++ mock_expect(&torrent::fd__bind, 0, fd, sap.get(), (socklen_t)torrent::sap_length(sap));
++ mock_expect(&torrent::fd__listen, 0, fd, SOMAXCONN);
++}
++
++inline void
++expect_fd_connect(int fd, const torrent::c_sa_unique_ptr& sap) {
++ mock_expect(&torrent::fd__connect, 0, fd, sap.get(), (socklen_t)torrent::sap_length(sap));
++}
++
++#endif
+diff --git a/test/helpers/expect_utils.h b/test/helpers/expect_utils.h
+new file mode 100644
+index 00000000..c84a11e0
+--- /dev/null
++++ b/test/helpers/expect_utils.h
+@@ -0,0 +1,13 @@
++#ifndef LIBTORRENT_HELPER_EXPECT_UTILS_H
++#define LIBTORRENT_HELPER_EXPECT_UTILS_H
++
++#include "helpers/mock_function.h"
++
++#include <torrent/utils/random.h>
++
++inline void
++expect_random_uniform_uint16(uint16_t result, uint16_t first, uint16_t last) {
++ mock_expect(&torrent::random_uniform_uint16, result, first, last);
++}
++
++#endif
+diff --git a/test/helpers/mock_compare.h b/test/helpers/mock_compare.h
+new file mode 100644
+index 00000000..3ea90305
+--- /dev/null
++++ b/test/helpers/mock_compare.h
+@@ -0,0 +1,96 @@
++#ifndef LIBTORRENT_HELPERS_MOCK_COMPARE_H
++#define LIBTORRENT_HELPERS_MOCK_COMPARE_H
++
++#include <algorithm>
++#include <type_traits>
++#include <torrent/event.h>
++#include <torrent/net/socket_address.h>
++
++// Compare arguments to mock functions with what is expected. The lhs
++// are the expected arguments, rhs are the ones called with.
++
++template <typename Arg>
++inline bool mock_compare_arg(Arg lhs, Arg rhs) { return lhs == rhs; }
++
++template <int I, typename A, typename... Args>
++typename std::enable_if<I == 1, int>::type
++mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
++ return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : 1;
++}
++
++template <int I, typename A, typename... Args>
++typename std::enable_if<1 < I, int>::type
++mock_compare_tuple(const std::tuple<A, Args...>& lhs, const std::tuple<Args...>& rhs) {
++ auto res = mock_compare_tuple<I - 1>(lhs, rhs);
++
++ if (res != 0)
++ return res;
++
++ return mock_compare_arg(std::get<I>(lhs), std::get<I - 1>(rhs)) ? 0 : I;
++}
++
++//template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type = 0>
++template <typename T>
++struct mock_compare_map {
++ typedef std::map<const T*, const T*> values_type;
++
++ constexpr static T* begin_pointer = reinterpret_cast<T*>(0x1000);
++ constexpr static T* end_pointer = reinterpret_cast<T*>(0x2000);
++
++ static bool is_key(const T* k) {
++ return k >= begin_pointer && k < end_pointer;
++ }
++
++ static bool has_key(const T* k) {
++ return values.find(k) != values.end();
++ }
++
++ static bool has_value(const T* v) {
++ return std::find_if(values.begin(), values.end(), [v](typename values_type::value_type& kv) { return v == kv.second; }) != values.end();
++ }
++
++ static const T* get(const T* k) {
++ auto itr = values.find(k);
++ CPPUNIT_ASSERT_MESSAGE("mock_compare_map get failed, not inserted", itr != values.end());
++ return itr->second;
++ }
++
++ static values_type values;
++};
++
++template<typename T>
++typename mock_compare_map<T>::values_type mock_compare_map<T>::values;
++
++template<typename T>
++void mock_compare_add(T* v) {
++ mock_compare_map<T>::add_value(v);
++}
++
++//
++// Specialize:
++//
++
++template <>
++inline bool mock_compare_arg<sockaddr*>(sockaddr* lhs, sockaddr* rhs) {
++ return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
++}
++template <>
++inline bool mock_compare_arg<const sockaddr*>(const sockaddr* lhs, const sockaddr* rhs) {
++ return lhs != nullptr && rhs != nullptr && torrent::sa_equal(lhs, rhs);
++}
++
++template <>
++inline bool mock_compare_arg<torrent::Event*>(torrent::Event* lhs, torrent::Event* rhs) {
++ if (mock_compare_map<torrent::Event>::is_key(lhs)) {
++ if (!mock_compare_map<torrent::Event>::has_value(rhs)) {
++ mock_compare_map<torrent::Event>::values[lhs] = rhs;
++ return true;
++ }
++
++ return mock_compare_map<torrent::Event>::has_key(lhs) && mock_compare_map<torrent::Event>::get(lhs) == rhs;
++ }
++
++ return lhs == rhs;
++}
++
++#endif
+diff --git a/test/helpers/mock_function.cc b/test/helpers/mock_function.cc
+new file mode 100644
+index 00000000..83e81551
+--- /dev/null
++++ b/test/helpers/mock_function.cc
+@@ -0,0 +1,170 @@
++#include "config.h"
++
++#include "mock_function.h"
++
++#include <fcntl.h>
++#include <iostream>
++#include <torrent/event.h>
++#include <torrent/net/socket_address.h>
++#include <torrent/net/fd.h>
++#include <torrent/utils/log.h>
++#include <torrent/utils/random.h>
++
++#define MOCK_CLEANUP_MAP(MOCK_FUNC) \
++ CPPUNIT_ASSERT_MESSAGE("expected mock function calls not completed for '" #MOCK_FUNC "'", mock_cleanup_map(&MOCK_FUNC) || ignore_assert);
++#define MOCK_LOG(log_fmt, ...) \
++ lt_log_print(torrent::LOG_MOCK_CALLS, "%s: " log_fmt, __func__, __VA_ARGS__);
++
++void
++mock_clear(bool ignore_assert) {
++ MOCK_CLEANUP_MAP(torrent::fd__accept);
++ MOCK_CLEANUP_MAP(torrent::fd__bind);
++ MOCK_CLEANUP_MAP(torrent::fd__close);
++ MOCK_CLEANUP_MAP(torrent::fd__connect);
++ MOCK_CLEANUP_MAP(torrent::fd__fcntl_int);
++ MOCK_CLEANUP_MAP(torrent::fd__listen);
++ MOCK_CLEANUP_MAP(torrent::fd__setsockopt_int);
++ MOCK_CLEANUP_MAP(torrent::fd__socket);
++
++ MOCK_CLEANUP_MAP(torrent::poll_event_open);
++ MOCK_CLEANUP_MAP(torrent::poll_event_close);
++ MOCK_CLEANUP_MAP(torrent::poll_event_closed);
++ MOCK_CLEANUP_MAP(torrent::poll_event_insert_read);
++ MOCK_CLEANUP_MAP(torrent::poll_event_insert_write);
++ MOCK_CLEANUP_MAP(torrent::poll_event_insert_error);
++ MOCK_CLEANUP_MAP(torrent::poll_event_remove_read);
++ MOCK_CLEANUP_MAP(torrent::poll_event_remove_write);
++ MOCK_CLEANUP_MAP(torrent::poll_event_remove_error);
++
++ MOCK_CLEANUP_MAP(torrent::random_uniform_uint16);
++ MOCK_CLEANUP_MAP(torrent::random_uniform_uint32);
++
++ mock_compare_map<torrent::Event>::values.clear();
++};
++
++void mock_init() {
++ log_add_group_output(torrent::LOG_MOCK_CALLS, "test_output");
++ mock_clear(true);
++}
++
++void mock_cleanup() {
++ mock_clear(false);
++}
++
++namespace torrent {
++
++//
++// Mock functions for 'torrent/net/fd.h':
++//
++
++int fd__accept(int socket, sockaddr *address, socklen_t *address_len) {
++ MOCK_LOG("entry socket:%i address:%s address_len:%u",
++ socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)(*address_len));
++ auto ret = mock_call<int>(__func__, &torrent::fd__accept, socket, address, address_len);
++ MOCK_LOG("exit socket:%i address:%s address_len:%u",
++ socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)(*address_len));
++ return ret;
++}
++
++int fd__bind(int socket, const sockaddr *address, socklen_t address_len) {
++ MOCK_LOG("socket:%i address:%s address_len:%u",
++ socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)address_len);
++ return mock_call<int>(__func__, &torrent::fd__bind, socket, address, address_len);
++}
++
++int fd__close(int fildes) {
++ MOCK_LOG("filedes:%i", fildes);
++ return mock_call<int>(__func__, &torrent::fd__close, fildes);
++}
++
++int fd__connect(int socket, const sockaddr *address, socklen_t address_len) {
++ MOCK_LOG("socket:%i address:%s address_len:%u",
++ socket, torrent::sa_pretty_str(address).c_str(), (unsigned int)address_len);
++ return mock_call<int>(__func__, &torrent::fd__connect, socket, address, address_len);
++}
++
++int fd__fcntl_int(int fildes, int cmd, int arg) {
++ MOCK_LOG("filedes:%i cmd:%i arg:%i", fildes, cmd, arg);
++ return mock_call<int>(__func__, &torrent::fd__fcntl_int, fildes, cmd, arg);
++}
++
++int fd__listen(int socket, int backlog) {
++ MOCK_LOG("socket:%i backlog:%i", socket, backlog);
++ return mock_call<int>(__func__, &torrent::fd__listen, socket, backlog);
++}
++
++int fd__setsockopt_int(int socket, int level, int option_name, int option_value) {
++ MOCK_LOG("socket:%i level:%i option_name:%i option_value:%i",
++ socket, level, option_name, option_value);
++ return mock_call<int>(__func__, &torrent::fd__setsockopt_int, socket, level, option_name, option_value);
++}
++
++int fd__socket(int domain, int type, int protocol) {
++ MOCK_LOG("domain:%i type:%i protocol:%i", domain, type, protocol);
++ return mock_call<int>(__func__, &torrent::fd__socket, domain, type, protocol);
++}
++
++//
++// Mock functions for 'torrent/event.h':
++//
++
++void poll_event_open(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_open, event);
++}
++
++void poll_event_close(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_close, event);
++}
++
++void poll_event_closed(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_closed, event);
++}
++
++void poll_event_insert_read(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_insert_read, event);
++}
++
++void poll_event_insert_write(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_insert_write, event);
++}
++
++void poll_event_insert_error(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_insert_error, event);
++}
++
++void poll_event_remove_read(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_remove_read, event);
++}
++
++void poll_event_remove_write(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_remove_write, event);
++}
++
++void poll_event_remove_error(Event* event) {
++ MOCK_LOG("fd:%i type_name:%s", event->file_descriptor(), event->type_name());
++ return mock_call<void>(__func__, &torrent::poll_event_remove_error, event);
++}
++
++//
++// Mock functions for 'torrent/utils/random.h':
++//
++
++uint16_t random_uniform_uint16(uint16_t min, uint16_t max) {
++ MOCK_LOG("min:%" PRIu16 " max:%" PRIu16, min, max);
++ return mock_call<uint16_t>(__func__, &torrent::random_uniform_uint16, min, max);
++}
++
++uint32_t random_uniform_uint32(uint32_t min, uint32_t max) {
++ MOCK_LOG("min:%" PRIu32 " max:%" PRIu32, min, max);
++ return mock_call<uint32_t>(__func__, &torrent::random_uniform_uint32, min, max);
++}
++
++}
+diff --git a/test/helpers/mock_function.h b/test/helpers/mock_function.h
+new file mode 100644
+index 00000000..6c194137
+--- /dev/null
++++ b/test/helpers/mock_function.h
+@@ -0,0 +1,133 @@
++#ifndef LIBTORRENT_HELPERS_MOCK_FUNCTION_H
++#define LIBTORRENT_HELPERS_MOCK_FUNCTION_H
++
++#include <functional>
++#include <map>
++#include <string>
++#include <tuple>
++#include <type_traits>
++#include <utility>
++#include <cppunit/extensions/HelperMacros.h>
++
++#include "helpers/mock_compare.h"
++
++template<typename R, typename... Args>
++struct mock_function_map {
++ typedef std::tuple<R, Args...> call_type;
++ typedef std::vector<call_type> call_list_type;
++ typedef std::map<void*, call_list_type> func_map_type;
++
++ typedef std::function<R (Args...)> function_type;
++ typedef std::map<void*, function_type> redirect_map_type;
++
++ static func_map_type functions;
++ static redirect_map_type redirects;
++
++ static bool cleanup(void* fn) {
++ redirects.erase(fn);
++ return functions.erase(fn) == 0;
++ }
++
++ static R ret_erase(void* fn) {
++ auto itr = functions.find(fn);
++ auto ret = std::get<0>(itr->second.front());
++ itr->second.erase(itr->second.begin());
++
++ if (itr->second.empty())
++ functions.erase(itr);
++
++ return ret;
++ }
++};
++
++template<typename R, typename... Args>
++typename mock_function_map<R, Args...>::func_map_type mock_function_map<R, Args...>::functions;
++template<typename R, typename... Args>
++typename mock_function_map<R, Args...>::redirect_map_type mock_function_map<R, Args...>::redirects;
++
++struct mock_void {};
++
++template<typename R, typename... Args>
++struct mock_function_type {
++ typedef mock_function_map<R, Args...> type;
++
++ static int compare_expected(typename type::call_type lhs, Args... rhs) {
++ return mock_compare_tuple<sizeof...(Args)>(lhs, std::make_tuple(rhs...));
++ }
++
++ static R ret_erase(void* fn) { return type::ret_erase(fn); }
++ static bool has_redirect(void* fn) { return type::redirects.find(fn) != type::redirects.end(); }
++ static R call_redirect(void* fn, Args... args) { return type::redirects.find(fn)->second(args...); }
++};
++
++template<typename... Args>
++struct mock_function_type<void, Args...> {
++ typedef mock_function_map<mock_void, Args...> type;
++
++ static int compare_expected(typename type::call_type lhs, Args... rhs) {
++ return mock_compare_tuple<sizeof...(Args)>(lhs, std::make_tuple(rhs...));
++ }
++
++ static void ret_erase(void* fn) { type::ret_erase(fn); }
++ static bool has_redirect(void* fn) { return type::redirects.find(fn) != type::redirects.end(); }
++ static void call_redirect(void* fn, Args... args) { type::redirects.find(fn)->second(args...); }
++};
++
++void mock_init();
++void mock_cleanup();
++
++template<typename R, typename... Args>
++bool
++mock_cleanup_map(R fn[[gnu::unused]](Args...)) {
++ return mock_function_type<R, Args...>::type::cleanup(reinterpret_cast<void*>(fn));
++}
++
++template<typename R, typename... Args>
++void
++mock_expect(R fn(Args...), R ret, Args... args) {
++ typedef mock_function_map<R, Args...> mock_map;
++ mock_map::functions[reinterpret_cast<void*>(fn)].push_back(std::tuple<R, Args...>(ret, args...));
++}
++
++template<typename... Args>
++void
++mock_expect(void fn(Args...), Args... args) {
++ typedef mock_function_map<mock_void, Args...> mock_map;
++ mock_map::functions[reinterpret_cast<void*>(fn)].push_back(std::tuple<mock_void, Args...>(mock_void(), args...));
++}
++
++template<typename R, typename... Args>
++void
++mock_redirect(R fn(Args...), std::function<R (Args...)> func) {
++ typedef mock_function_map<R, Args...> mock_map;
++ mock_map::redirects[reinterpret_cast<void*>(fn)] = func;
++}
++
++template<typename R, typename... Args>
++auto
++mock_call_direct(std::string name, R fn(Args...), Args... args) -> decltype(fn(args...)) {
++ typedef mock_function_type<R, Args...> mock_type;
++
++ auto itr = mock_type::type::functions.find(reinterpret_cast<void*>(fn));
++ CPPUNIT_ASSERT_MESSAGE(("mock_call expected function calls exhausted by '" + name + "'").c_str(),
++ itr != mock_type::type::functions.end());
++
++ auto mismatch_arg = mock_type::compare_expected(itr->second.front(), args...);
++ CPPUNIT_ASSERT_MESSAGE(("mock_call expected function call argument " + std::to_string(mismatch_arg) + " mismatch for '" + name + "'").c_str(),
++ mismatch_arg == 0);
++
++ return mock_type::ret_erase(reinterpret_cast<void*>(fn));
++}
++
++template<typename R, typename... Args>
++auto
++mock_call(std::string name, R fn(Args...), Args... args) -> decltype(fn(args...)) {
++ typedef mock_function_type<R, Args...> mock_type;
++
++ if (mock_type::has_redirect(reinterpret_cast<void*>(fn)))
++ return mock_type::call_redirect(reinterpret_cast<void*>(fn), args...);
++
++ return mock_call_direct(name, fn, args...);
++}
++
++#endif
+diff --git a/test/helpers/network.h b/test/helpers/network.h
+new file mode 100644
+index 00000000..6cf2f870
+--- /dev/null
++++ b/test/helpers/network.h
+@@ -0,0 +1,182 @@
++#ifndef LIBTORRENT_HELPER_NETWORK_H
++#define LIBTORRENT_HELPER_NETWORK_H
++
++#include <functional>
++#include <string>
++#include <cppunit/extensions/HelperMacros.h>
++
++#include "torrent/net/address_info.h"
++
++//
++// Socket addresses
++//
++
++#define TEST_DEFAULT_SA \
++ auto sin_any = wrap_ai_get_first_sa("0.0.0.0"); \
++ auto sin_any_5000 = wrap_ai_get_first_sa("0.0.0.0", "5000"); \
++ auto sin_any_5005 = wrap_ai_get_first_sa("0.0.0.0", "5005"); \
++ auto sin_bc = wrap_ai_get_first_sa("255.255.255.255"); \
++ auto sin_bc_5000 = wrap_ai_get_first_sa("255.255.255.255", "5000"); \
++ auto sin_bnd = wrap_ai_get_first_sa("123.123.123.123"); \
++ auto sin_1 = wrap_ai_get_first_sa("1.2.3.4"); \
++ auto sin_1_5000 = wrap_ai_get_first_sa("1.2.3.4", "5000"); \
++ auto sin_1_5005 = wrap_ai_get_first_sa("1.2.3.4", "5005"); \
++ auto sin_1_5100 = wrap_ai_get_first_sa("1.2.3.4", "5100"); \
++ auto sin_2 = wrap_ai_get_first_sa("4.3.2.1"); \
++ auto sin_2_5000 = wrap_ai_get_first_sa("4.3.2.1", "5000"); \
++ auto sin_2_5100 = wrap_ai_get_first_sa("4.3.2.1", "5100"); \
++ \
++ auto sin6_any = wrap_ai_get_first_sa("::"); \
++ auto sin6_any_5000 = wrap_ai_get_first_sa("::", "5000"); \
++ auto sin6_any_5005 = wrap_ai_get_first_sa("::", "5005"); \
++ auto sin6_bnd = wrap_ai_get_first_sa("ff01::123"); \
++ auto sin6_1 = wrap_ai_get_first_sa("ff01::1"); \
++ auto sin6_1_5000 = wrap_ai_get_first_sa("ff01::1", "5000"); \
++ auto sin6_1_5005 = wrap_ai_get_first_sa("ff01::1", "5005"); \
++ auto sin6_1_5100 = wrap_ai_get_first_sa("ff01::1", "5100"); \
++ auto sin6_2 = wrap_ai_get_first_sa("ff02::2"); \
++ auto sin6_2_5000 = wrap_ai_get_first_sa("ff02::2", "5000"); \
++ auto sin6_2_5100 = wrap_ai_get_first_sa("ff02::2", "5100"); \
++ auto sin6_v4_1 = wrap_ai_get_first_sa("::ffff:1.2.3.4"); \
++ auto sin6_v4_1_5000 = wrap_ai_get_first_sa("::ffff:1.2.3.4", "5000"); \
++ auto sin6_v4_any = wrap_ai_get_first_sa("::ffff:0.0.0.0"); \
++ auto sin6_v4_any_5000 = wrap_ai_get_first_sa("::ffff:0.0.0.0", "5000"); \
++ auto sin6_v4_bc = wrap_ai_get_first_sa("::ffff:255.255.255.255"); \
++ auto sin6_v4_bc_5000 = wrap_ai_get_first_sa("::ffff:255.255.255.255", "5000"); \
++ auto sin6_v4_bnd = wrap_ai_get_first_sa("::ffff:123.123.123.123"); \
++ \
++ auto c_sin_any = wrap_ai_get_first_c_sa("0.0.0.0"); \
++ auto c_sin_any_5000 = wrap_ai_get_first_c_sa("0.0.0.0", "5000"); \
++ auto c_sin_any_5005 = wrap_ai_get_first_c_sa("0.0.0.0", "5005"); \
++ auto c_sin_any_5010 = wrap_ai_get_first_c_sa("0.0.0.0", "5010"); \
++ auto c_sin_any_6881 = wrap_ai_get_first_c_sa("0.0.0.0", "6881"); \
++ auto c_sin_any_6900 = wrap_ai_get_first_c_sa("0.0.0.0", "6900"); \
++ auto c_sin_any_6999 = wrap_ai_get_first_c_sa("0.0.0.0", "6999"); \
++ auto c_sin_bc = wrap_ai_get_first_c_sa("255.255.255.255"); \
++ auto c_sin_bc_5000 = wrap_ai_get_first_c_sa("255.255.255.255", "5000"); \
++ auto c_sin_bnd = wrap_ai_get_first_c_sa("123.123.123.123"); \
++ auto c_sin_bnd_5000 = wrap_ai_get_first_c_sa("123.123.123.123", "5000"); \
++ auto c_sin_bnd_6881 = wrap_ai_get_first_c_sa("123.123.123.123", "6881"); \
++ auto c_sin_bnd_6900 = wrap_ai_get_first_c_sa("123.123.123.123", "6900"); \
++ auto c_sin_bnd_6999 = wrap_ai_get_first_c_sa("123.123.123.123", "6999"); \
++ auto c_sin_1 = wrap_ai_get_first_c_sa("1.2.3.4"); \
++ auto c_sin_1_5000 = wrap_ai_get_first_c_sa("1.2.3.4", "5000"); \
++ auto c_sin_1_5005 = wrap_ai_get_first_c_sa("1.2.3.4", "5005"); \
++ auto c_sin_1_5010 = wrap_ai_get_first_c_sa("1.2.3.4", "5010"); \
++ auto c_sin_1_6881 = wrap_ai_get_first_c_sa("1.2.3.4", "6881"); \
++ auto c_sin_1_6900 = wrap_ai_get_first_c_sa("1.2.3.4", "6900"); \
++ auto c_sin_1_6999 = wrap_ai_get_first_c_sa("1.2.3.4", "6999"); \
++ auto c_sin_2 = wrap_ai_get_first_c_sa("4.3.2.1"); \
++ auto c_sin_2_5000 = wrap_ai_get_first_c_sa("4.3.2.1", "5000"); \
++ auto c_sin_2_5100 = wrap_ai_get_first_c_sa("4.3.2.1", "5100"); \
++ \
++ auto c_sin6_any = wrap_ai_get_first_c_sa("::"); \
++ auto c_sin6_any_5000 = wrap_ai_get_first_c_sa("::", "5000"); \
++ auto c_sin6_any_5005 = wrap_ai_get_first_c_sa("::", "5005"); \
++ auto c_sin6_any_5010 = wrap_ai_get_first_c_sa("::", "5010"); \
++ auto c_sin6_any_6881 = wrap_ai_get_first_c_sa("::", "6881"); \
++ auto c_sin6_any_6900 = wrap_ai_get_first_c_sa("::", "6900"); \
++ auto c_sin6_any_6999 = wrap_ai_get_first_c_sa("::", "6999"); \
++ auto c_sin6_bnd = wrap_ai_get_first_c_sa("ff01::123"); \
++ auto c_sin6_bnd_5000 = wrap_ai_get_first_c_sa("ff01::123", "5000"); \
++ auto c_sin6_bnd_6881 = wrap_ai_get_first_c_sa("ff01::123", "6881"); \
++ auto c_sin6_bnd_6900 = wrap_ai_get_first_c_sa("ff01::123", "6900"); \
++ auto c_sin6_bnd_6999 = wrap_ai_get_first_c_sa("ff01::123", "6999"); \
++ auto c_sin6_v4_1_5000 = wrap_ai_get_first_c_sa("::ffff:1.2.3.4", "5000"); \
++ auto c_sin6_1 = wrap_ai_get_first_c_sa("ff01::1"); \
++ auto c_sin6_1_5000 = wrap_ai_get_first_c_sa("ff01::1", "5000"); \
++ auto c_sin6_1_5005 = wrap_ai_get_first_c_sa("ff01::1", "5005"); \
++ auto c_sin6_1_5010 = wrap_ai_get_first_c_sa("ff01::1", "5010"); \
++ auto c_sin6_1_5100 = wrap_ai_get_first_c_sa("ff01::1", "5100"); \
++ auto c_sin6_1_6881 = wrap_ai_get_first_c_sa("ff01::1", "6881"); \
++ auto c_sin6_1_6900 = wrap_ai_get_first_c_sa("ff01::1", "6900"); \
++ auto c_sin6_1_6999 = wrap_ai_get_first_c_sa("ff01::1", "6999"); \
++ auto c_sin6_2 = wrap_ai_get_first_c_sa("ff02::2"); \
++ auto c_sin6_2_5000 = wrap_ai_get_first_c_sa("ff02::2", "5000"); \
++ auto c_sin6_2_5100 = wrap_ai_get_first_c_sa("ff02::2", "5100");
++
++inline bool
++compare_sin6_addr(in6_addr lhs, in6_addr rhs) {
++ return std::equal(lhs.s6_addr, lhs.s6_addr + 16, rhs.s6_addr);
++}
++
++inline bool
++compare_listen_result(const torrent::listen_result_type& lhs, int rhs_fd, const torrent::c_sa_unique_ptr& rhs_sap) {
++ return lhs.fd == rhs_fd &&
++ ((lhs.address && rhs_sap) || ((lhs.address && rhs_sap) && torrent::sap_equal(lhs.address, rhs_sap)));
++}
++
++inline torrent::sa_unique_ptr
++wrap_ai_get_first_sa(const char* nodename, const char* servname = nullptr, const addrinfo* hints = nullptr) {
++ auto sa = torrent::ai_get_first_sa(nodename, servname, hints);
++
++ CPPUNIT_ASSERT_MESSAGE(("wrap_ai_get_first_sa: nodename:'" + std::string(nodename) + "'").c_str(),
++ sa != nullptr);
++ return sa;
++}
++
++inline torrent::c_sa_unique_ptr
++wrap_ai_get_first_c_sa(const char* nodename, const char* servname = nullptr, const addrinfo* hints = nullptr) {
++ auto sa = torrent::ai_get_first_sa(nodename, servname, hints);
++
++ CPPUNIT_ASSERT_MESSAGE(("wrap_ai_get_first_sa: nodename:'" + std::string(nodename) + "'").c_str(),
++ sa != nullptr);
++ return torrent::c_sa_unique_ptr(sa.release());
++}
++
++//
++// Address info tests:
++//
++
++typedef std::function<int (torrent::ai_unique_ptr&)> test_ai_ref;
++
++enum ai_flags_enum : int {
++ aif_none = 0x0,
++ aif_inet = 0x1,
++ aif_inet6 = 0x2,
++ aif_any = 0x4,
++};
++
++constexpr ai_flags_enum operator | (ai_flags_enum a, ai_flags_enum b) {
++ return static_cast<ai_flags_enum>(static_cast<int>(a) | static_cast<int>(b));
++}
++
++template <ai_flags_enum ai_flags>
++inline bool
++test_valid_ai_ref(test_ai_ref ftor, uint16_t port = 0) {
++ torrent::ai_unique_ptr ai;
++
++ if (int err = ftor(ai)) {
++ std::cout << std::endl << "valid_ai_ref got error '" << gai_strerror(err) << "'" << std::endl;
++ return false;
++ }
++
++ if ((ai_flags & aif_inet) && !torrent::sa_is_inet(ai->ai_addr))
++ return false;
++
++ if ((ai_flags & aif_inet6) && !torrent::sa_is_inet6(ai->ai_addr))
++ return false;
++
++ if (!!(ai_flags & aif_any) == !torrent::sa_is_any(ai->ai_addr))
++ return false;
++
++ if (torrent::sa_port(ai->ai_addr) != port)
++ return false;
++
++ return true;
++}
++
++inline bool
++test_valid_ai_ref_err(test_ai_ref ftor, int expect_err) {
++ torrent::ai_unique_ptr ai;
++ int err = ftor(ai);
++
++ if (err != expect_err) {
++ std::cout << std::endl << "ai_ref_err got wrong error, expected '" << gai_strerror(expect_err) << "', got '" << gai_strerror(err) << "'" << std::endl;
++ return false;
++ }
++
++ return true;
++}
++
++#endif
+diff --git a/test/helpers/progress_listener.cc b/test/helpers/progress_listener.cc
+new file mode 100644
+index 00000000..02803ffc
+--- /dev/null
++++ b/test/helpers/progress_listener.cc
+@@ -0,0 +1,63 @@
++#include "config.h"
++
++#include "progress_listener.h"
++
++#include <algorithm>
++#include <iostream>
++#include <iterator>
++#include <numeric>
++#include <stdexcept>
++#include "torrent/utils/log.h"
++#include "torrent/utils/log_buffer.h"
++
++static std::string
++get_test_path(const test_list_type& tl) {
++ if (tl.size() < 2)
++ return "";
++
++ return std::accumulate(std::next(tl.begin()), std::prev(tl.end()), std::string(), [](std::string result, CppUnit::Test* test) {
++ return std::move(result) + test->getName() + "::";
++ });
++}
++
++void
++progress_listener::startTest(CppUnit::Test *test) {
++ std::cout << get_test_path(m_test_path) << test->getName() << std::flush;
++
++ torrent::log_cleanup();
++
++ m_last_test_failed = false;
++ m_current_log_buffer = torrent::log_open_log_buffer("test_output");
++}
++
++void
++progress_listener::addFailure(const CppUnit::TestFailure &failure) {
++ // AddFailure is called for parent test suits, so only deal with leafs.
++ if (m_current_log_buffer == nullptr)
++ return;
++
++ std::cout << " : " << (failure.isError() ? "error" : "assertion");
++
++ m_last_test_failed = true;
++ m_failures.push_back(std::move(failure_type{failure.failedTestName(), std::move(m_current_log_buffer)}));
++}
++
++void
++progress_listener::endTest(CppUnit::Test *test) {
++ std::cout << (m_last_test_failed ? "" : " : OK") << std::endl;
++
++ m_current_log_buffer.reset();
++ torrent::log_cleanup();
++}
++
++void
++progress_listener::startSuite(CppUnit::Test *suite) {
++ m_test_path.push_back(suite);
++
++ std::cout << std::endl << get_test_path(m_test_path) << suite->getName() << ":" << std::endl;
++}
++
++void
++progress_listener::endSuite(CppUnit::Test *suite) {
++ m_test_path.pop_back();
++}
+diff --git a/test/helpers/progress_listener.h b/test/helpers/progress_listener.h
+new file mode 100644
+index 00000000..18fb8faa
+--- /dev/null
++++ b/test/helpers/progress_listener.h
+@@ -0,0 +1,47 @@
++#include <memory>
++#include <vector>
++#include <cppunit/Test.h>
++#include <cppunit/TestFailure.h>
++#include <cppunit/TestListener.h>
++
++#include "torrent/utils/log_buffer.h"
++
++struct failure_type {
++ std::string name;
++ torrent::log_buffer_ptr log;
++};
++
++typedef std::unique_ptr<CppUnit::TestFailure> test_failure_ptr;
++typedef std::vector<CppUnit::Test*> test_list_type;
++typedef std::vector<failure_type> failure_list_type;
++
++class progress_listener : public CppUnit::TestListener {
++public:
++ progress_listener() : m_last_test_failed(false) {}
++
++ void startTest(CppUnit::Test *test) override;
++ void addFailure(const CppUnit::TestFailure &failure) override;
++ void endTest(CppUnit::Test *test) override;
++
++ void startSuite(CppUnit::Test *suite) override;
++ void endSuite(CppUnit::Test *suite) override;
++
++ //Called by a TestRunner before running the test.
++ // void startTestRun(CppUnit::Test *test, CppUnit::TestResult *event_manager) override;
++
++ // Called by a TestRunner after running the test.
++ // void endTestRun(CppUnit::Test *test, CppUnit::TestResult *event_manager) override;
++
++ const failure_list_type& failures() { return m_failures; }
++ failure_list_type&& move_failures() { return std::move(m_failures); }
++
++private:
++ progress_listener(const progress_listener& rhs) = delete;
++ void operator =(const progress_listener& rhs) = delete;
++
++ test_list_type m_test_path;
++ failure_list_type m_failures;
++ bool m_last_test_failed;
++
++ torrent::log_buffer_ptr m_current_log_buffer;
++};
+diff --git a/test/helpers/test_fixture.cc b/test/helpers/test_fixture.cc
+new file mode 100644
+index 00000000..4d8d7214
+--- /dev/null
++++ b/test/helpers/test_fixture.cc
+@@ -0,0 +1,18 @@
++#include "config.h"
++
++#include "test_fixture.h"
++
++#include "torrent/utils/log.h"
++
++void
++test_fixture::setUp() {
++ mock_init();
++
++ log_add_group_output(torrent::LOG_CONNECTION_BIND, "test_output");
++ log_add_group_output(torrent::LOG_CONNECTION_FD, "test_output");
++}
++
++void
++test_fixture::tearDown() {
++ mock_cleanup();
++}
+diff --git a/test/helpers/test_fixture.h b/test/helpers/test_fixture.h
+new file mode 100644
+index 00000000..312d5009
+--- /dev/null
++++ b/test/helpers/test_fixture.h
+@@ -0,0 +1,14 @@
++#ifndef LIBTORRENT_HELPER_TEST_FIXTURE_H
++#define LIBTORRENT_HELPER_TEST_FIXTURE_H
++
++#include <cppunit/extensions/HelperMacros.h>
++
++#include "helpers/mock_function.h"
++
++class test_fixture : public CppUnit::TestFixture {
++public:
++ void setUp();
++ void tearDown();
++};
++
++#endif
+diff --git a/test/helpers/utils.h b/test/helpers/utils.h
+new file mode 100644
+index 00000000..d18450c1
+--- /dev/null
++++ b/test/helpers/utils.h
+@@ -0,0 +1,60 @@
++#ifndef LIBTORRENT_HELPER_UTILS_H
++#define LIBTORRENT_HELPER_UTILS_H
++
++#include <algorithm>
++#include <iostream>
++#include <cppunit/extensions/TestFactoryRegistry.h>
++#include <torrent/utils/log.h>
++
++static void
++dump_failure_log(const failure_type& failure) {
++ if (failure.log->empty())
++ return;
++
++ std::cout << std::endl << failure.name << std::endl;
++
++ // Doesn't print dump messages as log_buffer drops them.
++ std::for_each(failure.log->begin(), failure.log->end(), [](const torrent::log_entry& entry) {
++ std::cout << entry.timestamp << ' ' << entry.message << '\n';
++ });
++
++ std::cout << std::flush;
++}
++
++static void
++dump_failures(const failure_list_type& failures) {
++ if (failures.empty())
++ return;
++
++ std::cout << std::endl
++ << "=================" << std::endl
++ << "Failed Test Logs:" << std::endl
++ << "=================" << std::endl;
++
++ std::for_each(failures.begin(), failures.end(), [](const failure_type& failure) {
++ dump_failure_log(failure);
++ });
++ std::cout << std::endl;
++}
++
++static
++void add_tests(CppUnit::TextUi::TestRunner& runner, const char* c_test_names) {
++ if (c_test_names == NULL || std::string(c_test_names).empty()) {
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
++ return;
++ }
++
++ const std::string& test_names(c_test_names);
++
++ size_t pos = 0;
++ size_t next = 0;
++
++ while ((next = test_names.find(',', pos)) < test_names.size()) {
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos, next - pos)).makeTest());
++ pos = next + 1;
++ }
++
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos)).makeTest());
++}
++
++#endif
+diff --git a/test/main.cc b/test/main.cc
+index e69d3d70..da93fead 100644
+--- a/test/main.cc
++++ b/test/main.cc
+@@ -1,44 +1,92 @@
++#include "config.h"
++
++#include <cstdlib>
+ #include <stdexcept>
++#include <signal.h>
++#include <string.h>
+ #include <cppunit/BriefTestProgressListener.h>
+ #include <cppunit/CompilerOutputter.h>
+ #include <cppunit/TestResult.h>
+ #include <cppunit/TestResultCollector.h>
++#include <cppunit/extensions/HelperMacros.h>
+ #include <cppunit/extensions/TestFactoryRegistry.h>
+ #include <cppunit/ui/text/TestRunner.h>
+
+-int main(int argc, char* argv[])
+-{
++#ifdef USE_EXECINFO
++#include <execinfo.h>
++#endif
++
++#include "helpers/progress_listener.h"
++#include "helpers/utils.h"
++
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("net");
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("torrent/net");
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("torrent/utils");
++
++void
++do_test_panic(int signum) {
++ signal(signum, SIG_DFL);
++
++ std::cout << std::endl << std::endl << "Caught " << strsignal(signum) << ", dumping stack:" << std::endl << std::endl;
++
++#ifdef USE_EXECINFO
++ void* stackPtrs[20];
++
++ // Print the stack and exit.
++ int stackSize = backtrace(stackPtrs, 20);
++ char** stackStrings = backtrace_symbols(stackPtrs, stackSize);
++
++ for (int i = 0; i < stackSize; ++i)
++ std::cout << stackStrings[i] << std::endl;
++
++#else
++ std::cout << "Stack dump not enabled." << std::endl;
++#endif
++
++ std::cout << std::endl;
++ torrent::log_cleanup();
++ std::abort();
++}
++
++void
++register_signal_handlers() {
++ struct sigaction sa;
++ sigemptyset(&sa.sa_mask);
++ sa.sa_flags = SA_RESTART;
++ sa.sa_handler = &do_test_panic;
++
++ if (sigaction(SIGSEGV, &sa, NULL) == -1) {
++ std::cout << "Could not register signal handlers." << std::endl;
++ exit(-1);
++ }
++}
++
++int main(int argc, char* argv[]) {
++ register_signal_handlers();
++
+ CppUnit::TestResult controller;
+ CppUnit::TestResultCollector result;
+- CppUnit::BriefTestProgressListener progressListener;
++ progress_listener progress;
+
+ controller.addListener( &result );
+- controller.addListener( &progressListener );
+-
+- // Get the top level suite from the registry
+- CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
++ controller.addListener( &progress );
+
+- // Adds the test to the list of test to run
+ CppUnit::TextUi::TestRunner runner;
+- runner.addTest( suite );
++ add_tests(runner, std::getenv("TEST_NAME"));
+
+- // Change the default outputter to a compiler error format outputter
+- runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(),
+- std::cerr ) );
+ try {
+ std::cout << "Running ";
+ runner.run( controller );
+
+- std::cerr << std::endl;
+-
++ // TODO: Make outputter.
++ dump_failures(progress.failures());
++
+ // Print test in a compiler compatible format.
+ CppUnit::CompilerOutputter outputter( &result, std::cerr );
+ outputter.write();
+
+ } catch ( std::invalid_argument &e ) { // Test path not resolved
+- std::cerr << std::endl
+- << "ERROR: " << e.what()
+- << std::endl;
++ std::cerr << std::endl << "ERROR: " << e.what() << std::endl;
+ return 1;
+ }
+
+diff --git a/test/net/test_socket_listen.cc b/test/net/test_socket_listen.cc
+new file mode 100644
+index 00000000..e86a078b
+--- /dev/null
++++ b/test/net/test_socket_listen.cc
+@@ -0,0 +1,398 @@
++#include "config.h"
++
++#include "test_socket_listen.h"
++
++#include "helpers/expect_fd.h"
++#include "helpers/expect_utils.h"
++#include "helpers/mock_function.h"
++#include "helpers/network.h"
++
++#include <net/socket_listen.h>
++#include <torrent/exceptions.h>
++#include <torrent/utils/log.h>
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_socket_listen, "net");
++
++struct test_sl_deleter {
++ void operator()(torrent::socket_listen* sl) const { if (!sl->is_open()) delete sl; }
++};
++
++typedef std::unique_ptr<torrent::socket_listen, test_sl_deleter> test_sl_unique_ptr;
++
++#define TEST_SL_BEGIN(name) \
++ test_sl_unique_ptr sl(new torrent::socket_listen); \
++ std::vector<torrent::sa_unique_ptr> sap_cache; \
++ lt_log_print(torrent::LOG_MOCK_CALLS, "sl_begin: %s", name); \
++ TEST_DEFAULT_SA;
++
++#define TEST_SL_ASSERT_OPEN(_sap_bind, _sap_result, _flags) \
++ TEST_SL_ASSERT_OPEN_PORT(_sap_bind, _sap_result, 5000, 5009, 5005, _flags); \
++ CPPUNIT_ASSERT(sl->socket_address_port() == 5005);
++
++#define TEST_SL_ASSERT_OPEN_PORT(_sap_bind, _sap_result, _first_port, _last_port, _itr_port, _flags) \
++ expect_event_open_re(0); \
++ CPPUNIT_ASSERT(sl->open(_sap_bind, _first_port, _last_port, _itr_port, _flags)); \
++ CPPUNIT_ASSERT(sl->is_open()); \
++ CPPUNIT_ASSERT(torrent::sa_equal(sl->socket_address(), _sap_result.get()));
++
++#define TEST_SL_ASSERT_OPEN_SEQUENTIAL(_sap_bind, _sap_result, _first_port, _last_port, _flags) \
++ expect_event_open_re(0); \
++ CPPUNIT_ASSERT(sl->open_sequential(_sap_bind, _first_port, _last_port, _flags)); \
++ CPPUNIT_ASSERT(sl->is_open()); \
++ CPPUNIT_ASSERT(torrent::sa_equal(sl->socket_address(), _sap_result.get()));
++
++#define TEST_SL_ASSERT_OPEN_RANDOMIZE(_sap_bind, _sap_result, _first_port, _last_port, _flags) \
++ expect_event_open_re(0); \
++ CPPUNIT_ASSERT(sl->open_randomize(_sap_bind, _first_port, _last_port, _flags)); \
++ CPPUNIT_ASSERT(sl->is_open()); \
++ CPPUNIT_ASSERT(torrent::sa_equal(sl->socket_address(), _sap_result.get()));
++
++#define TEST_SL_ASSERT_CLOSED() \
++ CPPUNIT_ASSERT(!sl->is_open()); \
++ CPPUNIT_ASSERT(sl->file_descriptor() == -1); \
++ CPPUNIT_ASSERT(sl->socket_address() == nullptr); \
++ CPPUNIT_ASSERT(sl->socket_address_port() == 0);
++
++#define TEST_SL_CLOSE(_fd) \
++ mock_expect(&torrent::fd__close, 0, _fd); \
++ mock_expect(&torrent::poll_event_closed, (torrent::Event*)sl.get()); \
++ CPPUNIT_ASSERT_NO_THROW(sl->close()); \
++ TEST_SL_ASSERT_CLOSED();
++
++#define TEST_SL_MOCK_CLOSED_PORT_RANGE(_src_sap, _first_port, _last_port) \
++ { uint16_t _port = _first_port; do { \
++ sap_cache.push_back(torrent::sap_copy(_src_sap)); \
++ torrent::sap_set_port(sap_cache.back(), _port); \
++ mock_expect(&torrent::fd__bind, -1, 1000, \
++ (const sockaddr*)sap_cache.back().get(), \
++ (socklen_t)torrent::sap_length(sap_cache.back())); \
++ } while (_port++ != _last_port); \
++ }
++
++void
++test_socket_listen::test_basic() {
++ TEST_SL_BEGIN("basic");
++ TEST_SL_ASSERT_CLOSED();
++ CPPUNIT_ASSERT(sl->backlog() == SOMAXCONN);
++ CPPUNIT_ASSERT(sl->type_name() == std::string("socket_listen"));
++}
++
++void
++test_socket_listen::test_open_error() {
++ { TEST_SL_BEGIN("open twice");
++ expect_fd_inet6_tcp_nonblock(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream | torrent::fd_flag_nonblock);
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5000, 5009, 5005, torrent::fd_flag_stream),
++ torrent::internal_error);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_any, stream, no v4only");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin_any), 5000, 5009, 5005, torrent::fd_flag_stream),
++ torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++}
++
++void
++test_socket_listen::test_open_sap() {
++ { TEST_SL_BEGIN("sin6_any, stream");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_any, stream|v4only");
++ expect_fd_inet_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin_any), c_sin_any_5005, torrent::fd_flag_stream | torrent::fd_flag_v4only);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_1, stream|v4only");
++ expect_fd_inet_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin_1_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin_1), c_sin_1_5005, torrent::fd_flag_stream | torrent::fd_flag_v4only);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream|v6only");
++ expect_fd_inet6_tcp(1000);
++ mock_expect(&torrent::fd__setsockopt_int, 0, 1000, (int)IPPROTO_IPV6, (int)IPV6_V6ONLY, (int)true);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream | torrent::fd_flag_v6only);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_1, stream|v6only");
++ expect_fd_inet6_tcp(1000);
++ mock_expect(&torrent::fd__setsockopt_int, 0, 1000, (int)IPPROTO_IPV6, (int)IPV6_V6ONLY, (int)true);
++ expect_fd_bind_listen(1000, c_sin6_1_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_1), c_sin6_1_5005, torrent::fd_flag_stream | torrent::fd_flag_v6only);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++void
++test_socket_listen::test_open_sap_error() {
++ { TEST_SL_BEGIN("unspec");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sa_make_unspec(), 5000, 5009, 5005, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("unix");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sa_make_unix("test"), 5000, 5009, 5005, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin_any_5005");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin_any_5005), 5000, 5009, 5005, torrent::fd_flag_stream | torrent::fd_flag_v4only), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any_5005");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any_5005), 5000, 5009, 5005, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin_any");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_to_v4mapped(sin_any), 5000, 5009, 5005, torrent::fd_flag_stream | torrent::fd_flag_v4only), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin_1, v4mapped");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_to_v4mapped(sin_1), 5000, 5009, 5005, torrent::fd_flag_stream | torrent::fd_flag_v4only), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin_broadcast");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin_bc), 5000, 5009, 5005, torrent::fd_flag_stream | torrent::fd_flag_v4only), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++}
++
++void
++test_socket_listen::test_open_flags() {
++ { TEST_SL_BEGIN("sin_any, stream|v4only|nonblock");
++ expect_fd_inet_tcp_nonblock(1000);
++ expect_fd_bind_listen(1000, c_sin_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy_addr(sin_any), c_sin_any_5005, torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_nonblock);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream|nonblock");
++ expect_fd_inet6_tcp_nonblock(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream | torrent::fd_flag_nonblock);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_any, stream|v4only|reuse_address");
++ expect_fd_inet_tcp(1000);
++ mock_expect(&torrent::fd__setsockopt_int, 0, 1000, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++ expect_fd_bind_listen(1000, c_sin_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy_addr(sin_any), c_sin_any_5005, torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_reuse_address);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream|reuse_address");
++ expect_fd_inet6_tcp(1000);
++ mock_expect(&torrent::fd__setsockopt_int, 0, 1000, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream | torrent::fd_flag_reuse_address);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_any, stream|v4only|nonblock|reuse_address");
++ expect_fd_inet_tcp_nonblock(1000);
++ mock_expect(&torrent::fd__setsockopt_int, 0, 1000, (int)SOL_SOCKET, (int)SO_REUSEADDR, (int)true);
++ expect_fd_bind_listen(1000, c_sin_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy_addr(sin_any), c_sin_any_5005, torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream|nonblock|reuse_address");
++ expect_fd_inet6_tcp_nonblock_reuseaddr(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN(torrent::sap_copy(sin6_any), c_sin6_any_5005, torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++void
++test_socket_listen::test_open_flags_error() {
++ { TEST_SL_BEGIN("sin6_any, fd_flags(0)");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5000, 5009, 5005, torrent::fd_flags(0)), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, fd_flags(0xffff)");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5000, 5009, 5005, torrent::fd_flags(0xffff)), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++}
++
++void
++test_socket_listen::test_open_port_single() {
++ { TEST_SL_BEGIN("sin6_any, stream");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5000);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5000, 5000, 5000, 5000, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin_any, stream");
++ expect_fd_inet_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin_any_5000);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin_any), c_sin_any_5000, 5000, 5000, 5000, torrent::fd_flag_stream | torrent::fd_flag_v4only);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++void
++test_socket_listen::test_open_port_single_error() {
++ { TEST_SL_BEGIN("sin6_any, 0, 0, 0");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0, 0, 0, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, 1000, 0, 0");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 1000, 0, 0, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, 0, 1000, 0");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0, 1000, 0, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, 0, 0, 500");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0, 0, 500, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, 0, 1000, 500");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0, 1000, 500, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++}
++
++void
++test_socket_listen::test_open_port_range() {
++ { TEST_SL_BEGIN("sin6_any, stream, first");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5000);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5000, 5000, 5010, 5000, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, from first to middle port");
++ expect_fd_inet6_tcp(1000);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5000, 5004);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5005, 5000, 5010, 5000, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, from first to last port");
++ expect_fd_inet6_tcp(1000);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5000, 5009);
++ expect_fd_bind_listen(1000, c_sin6_any_5010);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5010, 5000, 5010, 5000, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, middle");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5005, 5000, 5010, 5005, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, from middle to last port");
++ expect_fd_inet6_tcp(1000);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5005, 5009);
++ expect_fd_bind_listen(1000, c_sin6_any_5010);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5010, 5000, 5010, 5005, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, last");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5010);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5010, 5000, 5010, 5010, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, from last to first port");
++ expect_fd_inet6_tcp(1000);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5010, 5010);
++ expect_fd_bind_listen(1000, c_sin6_any_5000);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5000, 5000, 5010, 5010, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++ { TEST_SL_BEGIN("sin6_any, stream, from last to middle port");
++ expect_fd_inet6_tcp(1000);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5010, 5010);
++ TEST_SL_MOCK_CLOSED_PORT_RANGE(sin6_any, 5000, 5004);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN_PORT(torrent::sap_copy(sin6_any), c_sin6_any_5005, 5000, 5010, 5010, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++void
++test_socket_listen::test_open_port_range_error() {
++ { TEST_SL_BEGIN("sin6_any, first > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5000, 4999, 5000, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, first > itr");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5001, 5009, 5000, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, itr > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 5000, 5009, 5010, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, min first > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 2, 1, 2, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, min first > itr");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 2, 1000, 1, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, min itr > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 1, 2, 3, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, max first > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0xffff, 0xfffe, 0xffff, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, max first > itr");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0xffff, 0xffff, 0xfffe, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++ { TEST_SL_BEGIN("sin6_any, max itr > last");
++ CPPUNIT_ASSERT_THROW(sl->open(torrent::sap_copy(sin6_any), 0xfffe, 0xfffe, 0xffff, torrent::fd_flag_stream), torrent::internal_error);
++ TEST_SL_ASSERT_CLOSED();
++ };
++}
++
++void
++test_socket_listen::test_open_sequential() {
++ { TEST_SL_BEGIN("sin6_any, stream");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5000);
++ TEST_SL_ASSERT_OPEN_SEQUENTIAL(torrent::sap_copy(sin6_any), c_sin6_any_5000, 5000, 5010, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++void
++test_socket_listen::test_open_randomize() {
++ { TEST_SL_BEGIN("sin6_any, stream");
++ expect_random_uniform_uint16(5005, 5000, 5010);
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5005);
++ TEST_SL_ASSERT_OPEN_RANDOMIZE(torrent::sap_copy(sin6_any), c_sin6_any_5005, 5000, 5010, torrent::fd_flag_stream);
++ TEST_SL_CLOSE(1000);
++ };
++}
++
++// deal with reuse error
++
++void
++test_socket_listen::test_accept() {
++ { TEST_SL_BEGIN("sin6_any, stream");
++ expect_fd_inet6_tcp(1000);
++ expect_fd_bind_listen(1000, c_sin6_any_5000);
++ TEST_SL_ASSERT_OPEN_SEQUENTIAL(torrent::sap_copy(sin6_any), c_sin6_any_5000, 5000, 5010, torrent::fd_flag_stream);
++
++ std::vector<torrent::fd_sap_tuple> accepted_connections;
++
++ sl->set_slot_accepted([&accepted_connections](int accept_fd, torrent::sa_unique_ptr sap) {
++ accepted_connections.push_back(torrent::fd_sap_tuple{accept_fd, std::move(sap)});
++ });
++
++ // CPPUNIT_ASSERT(accepted_connections.size() > 0 && torrent::fd_sap_equal(accepted_connections[0], torrent::fd_sap_tuple{2000, torrent::sap_copy(sin6_1_5100)}));
++
++ TEST_SL_CLOSE(1000);
++ };
++}
+diff --git a/test/net/test_socket_listen.h b/test/net/test_socket_listen.h
+new file mode 100644
+index 00000000..5d06f7f3
+--- /dev/null
++++ b/test/net/test_socket_listen.h
+@@ -0,0 +1,44 @@
++#include "helpers/test_fixture.h"
++
++class test_socket_listen : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_socket_listen);
++
++ CPPUNIT_TEST(test_basic);
++
++ CPPUNIT_TEST(test_open_error);
++ CPPUNIT_TEST(test_open_sap);
++ CPPUNIT_TEST(test_open_sap_error);
++ CPPUNIT_TEST(test_open_flags);
++ CPPUNIT_TEST(test_open_flags_error);
++
++ CPPUNIT_TEST(test_open_port_single);
++ CPPUNIT_TEST(test_open_port_single_error);
++ CPPUNIT_TEST(test_open_port_range);
++ CPPUNIT_TEST(test_open_port_range_error);
++
++ CPPUNIT_TEST(test_open_sequential);
++ CPPUNIT_TEST(test_open_randomize);
++
++ CPPUNIT_TEST(test_accept);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void test_basic();
++
++ void test_open_error();
++ void test_open_sap();
++ void test_open_sap_error();
++ void test_open_flags();
++ void test_open_flags_error();
++
++ void test_open_port_single();
++ void test_open_port_single_error();
++ void test_open_port_range();
++ void test_open_port_range_error();
++
++ void test_open_sequential();
++ void test_open_randomize();
++
++ void test_accept();
++};
+diff --git a/test/torrent/net/test_address_info.cc b/test/torrent/net/test_address_info.cc
+new file mode 100644
+index 00000000..e3ee24d1
+--- /dev/null
++++ b/test/torrent/net/test_address_info.cc
+@@ -0,0 +1,62 @@
++#include "config.h"
++
++#include "test_address_info.h"
++
++#include "helpers/network.h"
++#include "torrent/net/address_info.h"
++#include "torrent/net/socket_address.h"
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_address_info, "torrent/net");
++
++void
++test_address_info::test_basic() {
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet|aif_any> (std::bind(torrent::ai_get_addrinfo, "0.0.0.0", nullptr, nullptr, std::placeholders::_1)));
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet6|aif_any>(std::bind(torrent::ai_get_addrinfo, "::", nullptr, nullptr, std::placeholders::_1)));
++
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet> (std::bind(torrent::ai_get_addrinfo, "1.1.1.1", nullptr, nullptr, std::placeholders::_1)));
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet6>(std::bind(torrent::ai_get_addrinfo, "ff01::1", nullptr, nullptr, std::placeholders::_1)));
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet6>(std::bind(torrent::ai_get_addrinfo, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", nullptr, nullptr, std::placeholders::_1)));
++
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet> (std::bind(torrent::ai_get_addrinfo, "1.1.1.1", "22123", nullptr, std::placeholders::_1), 22123));
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet6>(std::bind(torrent::ai_get_addrinfo, "2001:db8:a::", "22123", nullptr, std::placeholders::_1), 22123));
++
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_none> (std::bind(torrent::ai_get_addrinfo, "localhost", nullptr, nullptr, std::placeholders::_1)));
++
++ CPPUNIT_ASSERT(test_valid_ai_ref_err(std::bind(torrent::ai_get_addrinfo, "1.1.1.300", nullptr, nullptr, std::placeholders::_1), EAI_NONAME));
++ CPPUNIT_ASSERT(test_valid_ai_ref_err(std::bind(torrent::ai_get_addrinfo, "2001:db8:a::22123", nullptr, nullptr, std::placeholders::_1), EAI_NONAME));
++}
++
++void
++test_address_info::test_numericserv() {
++ CPPUNIT_ASSERT(test_valid_ai_ref<aif_inet> (std::bind(torrent::ai_get_addrinfo, "1.1.1.1", nullptr, torrent::ai_make_hint(AI_NUMERICHOST, 0, 0).get(), std::placeholders::_1)));
++
++ CPPUNIT_ASSERT(test_valid_ai_ref_err(std::bind(torrent::ai_get_addrinfo, "localhost", nullptr, torrent::ai_make_hint(AI_NUMERICHOST, 0, 0).get(), std::placeholders::_1), EAI_NONAME));
++}
++
++void
++test_address_info::test_helpers() {
++ torrent::sin_unique_ptr sin_zero = torrent::sin_from_sa(wrap_ai_get_first_sa("0.0.0.0"));
++ CPPUNIT_ASSERT(sin_zero != nullptr);
++ CPPUNIT_ASSERT(sin_zero->sin_family == AF_INET);
++ CPPUNIT_ASSERT(sin_zero->sin_port == 0);
++ CPPUNIT_ASSERT(sin_zero->sin_addr.s_addr == in_addr().s_addr);
++
++ torrent::sin_unique_ptr sin_1 = torrent::sin_from_sa(wrap_ai_get_first_sa("1.2.3.4"));
++ CPPUNIT_ASSERT(sin_1 != nullptr);
++ CPPUNIT_ASSERT(sin_1->sin_family == AF_INET);
++ CPPUNIT_ASSERT(sin_1->sin_port == 0);
++ CPPUNIT_ASSERT(sin_1->sin_addr.s_addr == htonl(0x01020304));
++
++ torrent::sin6_unique_ptr sin6_zero = torrent::sin6_from_sa(wrap_ai_get_first_sa("::"));
++ CPPUNIT_ASSERT(sin6_zero != nullptr);
++ CPPUNIT_ASSERT(sin6_zero->sin6_family == AF_INET6);
++ CPPUNIT_ASSERT(sin6_zero->sin6_port == 0);
++ CPPUNIT_ASSERT(compare_sin6_addr(sin6_zero->sin6_addr, in6_addr{0}));
++
++ torrent::sin6_unique_ptr sin6_1 = torrent::sin6_from_sa(wrap_ai_get_first_sa("ff01::1"));
++ CPPUNIT_ASSERT(sin6_1 != nullptr);
++ CPPUNIT_ASSERT(sin6_1->sin6_family == AF_INET6);
++ CPPUNIT_ASSERT(sin6_1->sin6_port == 0);
++ CPPUNIT_ASSERT(compare_sin6_addr(sin6_1->sin6_addr, in6_addr{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
++ CPPUNIT_ASSERT(!compare_sin6_addr(sin6_1->sin6_addr, in6_addr{0xff, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2}));
++}
+diff --git a/test/torrent/net/test_address_info.h b/test/torrent/net/test_address_info.h
+new file mode 100644
+index 00000000..c2ce9188
+--- /dev/null
++++ b/test/torrent/net/test_address_info.h
+@@ -0,0 +1,19 @@
++#include <cppunit/extensions/HelperMacros.h>
++
++class test_address_info : public CppUnit::TestFixture {
++ CPPUNIT_TEST_SUITE(test_address_info);
++
++ CPPUNIT_TEST(test_basic);
++ CPPUNIT_TEST(test_numericserv);
++ CPPUNIT_TEST(test_helpers);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void setUp() {}
++ void tearDown() {}
++
++ void test_basic();
++ void test_numericserv();
++ void test_helpers();
++};
+diff --git a/test/torrent/net/test_fd.cc b/test/torrent/net/test_fd.cc
+new file mode 100644
+index 00000000..3cab0c5e
+--- /dev/null
++++ b/test/torrent/net/test_fd.cc
+@@ -0,0 +1,24 @@
++#include "config.h"
++
++#include "test_fd.h"
++
++#include <torrent/net/fd.h>
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_fd, "torrent/net");
++
++void
++test_fd::test_valid_flags() {
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_nonblock));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_reuse_address));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v6only));
++
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_v4only | torrent::fd_flag_v6only));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_v6only));
++
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags()));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(~torrent::fd_flag_all)));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(torrent::fd_flag_stream | ~torrent::fd_flag_all)));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(0x3245132)));
++}
+diff --git a/test/torrent/net/test_fd.h b/test/torrent/net/test_fd.h
+new file mode 100644
+index 00000000..6ba718fc
+--- /dev/null
++++ b/test/torrent/net/test_fd.h
+@@ -0,0 +1,12 @@
++#include "helpers/test_fixture.h"
++
++class test_fd : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_fd);
++
++ CPPUNIT_TEST(test_valid_flags);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void test_valid_flags();
++};
+diff --git a/test/torrent/net/test_socket_address.cc b/test/torrent/net/test_socket_address.cc
+new file mode 100644
+index 00000000..8a1b0c8a
+--- /dev/null
++++ b/test/torrent/net/test_socket_address.cc
+@@ -0,0 +1,383 @@
++#include "config.h"
++
++#include "test_socket_address.h"
++
++#include "helpers/network.h"
++#include "torrent/exceptions.h"
++#include "torrent/net/socket_address.h"
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_socket_address, "torrent/net");
++
++void
++test_socket_address::test_sa_is_any() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_is_any(sin_any));
++ CPPUNIT_ASSERT(torrent::sap_is_any(sin_any_5000));
++ CPPUNIT_ASSERT(torrent::sap_is_any(sin6_v4_any));
++ CPPUNIT_ASSERT(torrent::sap_is_any(sin6_v4_any_5000));
++
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin_bc));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin6_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin_bc_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin_1_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(sin6_1_5000));
++
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin_bc));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin6_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin_bc_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin_1_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_any(c_sin6_1_5000));
++}
++
++void
++test_socket_address::test_sa_is_broadcast() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_is_broadcast(sin_bc));
++ CPPUNIT_ASSERT(torrent::sap_is_broadcast(sin_bc_5000));
++ CPPUNIT_ASSERT(torrent::sap_is_broadcast(sin6_v4_bc));
++ CPPUNIT_ASSERT(torrent::sap_is_broadcast(sin6_v4_bc_5000));
++
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin_any));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin6_any));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin6_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin_any_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin_1_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin6_any_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(sin6_1_5000));
++
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin_any));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin6_any));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin6_1));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin_any_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin_1_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin6_any_5000));
++ CPPUNIT_ASSERT(!torrent::sap_is_broadcast(c_sin6_1_5000));
++}
++
++void
++test_socket_address::test_make() {
++ torrent::sa_unique_ptr sa_unspec = torrent::sa_make_unspec();
++ CPPUNIT_ASSERT(sa_unspec != nullptr);
++ CPPUNIT_ASSERT(sa_unspec->sa_family == AF_UNSPEC);
++
++ torrent::sa_unique_ptr sa_inet = torrent::sa_make_inet();
++ CPPUNIT_ASSERT(sa_inet != nullptr);
++ CPPUNIT_ASSERT(sa_inet->sa_family == AF_INET);
++
++ sockaddr_in* sin_inet = reinterpret_cast<sockaddr_in*>(sa_inet.get());
++ CPPUNIT_ASSERT(sin_inet->sin_family == AF_INET);
++ CPPUNIT_ASSERT(sin_inet->sin_port == 0);
++ CPPUNIT_ASSERT(sin_inet->sin_addr.s_addr == in_addr().s_addr);
++
++ torrent::sa_unique_ptr sa_inet6 = torrent::sa_make_inet6();
++ CPPUNIT_ASSERT(sa_inet6 != nullptr);
++ CPPUNIT_ASSERT(sa_inet6->sa_family == AF_INET6);
++
++ sockaddr_in6* sin6_inet6 = reinterpret_cast<sockaddr_in6*>(sa_inet6.get());
++ CPPUNIT_ASSERT(sin6_inet6->sin6_family == AF_INET6);
++ CPPUNIT_ASSERT(sin6_inet6->sin6_port == 0);
++ CPPUNIT_ASSERT(sin6_inet6->sin6_flowinfo == 0);
++ CPPUNIT_ASSERT(compare_sin6_addr(sin6_inet6->sin6_addr, in6_addr{0}));
++ CPPUNIT_ASSERT(sin6_inet6->sin6_scope_id == 0);
++
++ torrent::sa_unique_ptr sa_unix = torrent::sa_make_unix("");
++ CPPUNIT_ASSERT(sa_unix != nullptr);
++ CPPUNIT_ASSERT(sa_unix->sa_family == AF_UNIX);
++}
++
++void
++test_socket_address::test_sin_from_sa() {
++ torrent::sa_unique_ptr sa_zero = wrap_ai_get_first_sa("0.0.0.0");
++ torrent::sin_unique_ptr sin_zero;
++
++ CPPUNIT_ASSERT(sa_zero != nullptr);
++ CPPUNIT_ASSERT_NO_THROW({ sin_zero = torrent::sin_from_sa(std::move(sa_zero)); });
++ CPPUNIT_ASSERT(sa_zero == nullptr);
++ CPPUNIT_ASSERT(sin_zero != nullptr);
++
++ CPPUNIT_ASSERT(sin_zero->sin_addr.s_addr == htonl(0x0));
++
++ torrent::sa_unique_ptr sa_inet = wrap_ai_get_first_sa("1.2.3.4");
++ torrent::sin_unique_ptr sin_inet;
++
++ CPPUNIT_ASSERT(sa_inet != nullptr);
++ CPPUNIT_ASSERT_NO_THROW({ sin_inet = torrent::sin_from_sa(std::move(sa_inet)); });
++ CPPUNIT_ASSERT(sa_inet == nullptr);
++ CPPUNIT_ASSERT(sin_inet != nullptr);
++
++ CPPUNIT_ASSERT(sin_inet->sin_addr.s_addr == htonl(0x01020304));
++
++ CPPUNIT_ASSERT_THROW(torrent::sin_from_sa(torrent::sa_unique_ptr()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sin_from_sa(torrent::sa_make_unspec()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sin_from_sa(torrent::sa_make_inet6()), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sin6_from_sa() {
++ torrent::sa_unique_ptr sa_zero = wrap_ai_get_first_sa("::");
++ torrent::sin6_unique_ptr sin6_zero;
++
++ CPPUNIT_ASSERT(sa_zero != nullptr);
++ CPPUNIT_ASSERT_NO_THROW({ sin6_zero = torrent::sin6_from_sa(std::move(sa_zero)); });
++ CPPUNIT_ASSERT(sa_zero == nullptr);
++ CPPUNIT_ASSERT(sin6_zero != nullptr);
++
++ CPPUNIT_ASSERT(sin6_zero->sin6_addr.s6_addr[0] == 0x0);
++ CPPUNIT_ASSERT(sin6_zero->sin6_addr.s6_addr[1] == 0x0);
++ CPPUNIT_ASSERT(sin6_zero->sin6_addr.s6_addr[15] == 0x0);
++
++ torrent::sa_unique_ptr sa_inet6 = wrap_ai_get_first_sa("ff01::1");
++ torrent::sin6_unique_ptr sin6_inet6;
++
++ CPPUNIT_ASSERT(sa_inet6 != nullptr);
++ CPPUNIT_ASSERT_NO_THROW({ sin6_inet6 = torrent::sin6_from_sa(std::move(sa_inet6)); });
++ CPPUNIT_ASSERT(sa_inet6 == nullptr);
++ CPPUNIT_ASSERT(sin6_inet6 != nullptr);
++
++ CPPUNIT_ASSERT(sin6_inet6->sin6_addr.s6_addr[0] == 0xff);
++ CPPUNIT_ASSERT(sin6_inet6->sin6_addr.s6_addr[1] == 0x01);
++ CPPUNIT_ASSERT(sin6_inet6->sin6_addr.s6_addr[15] == 0x01);
++
++ CPPUNIT_ASSERT_THROW(torrent::sin6_from_sa(torrent::sa_unique_ptr()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sin6_from_sa(torrent::sa_make_unspec()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sin6_from_sa(torrent::sa_make_inet()), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_equal() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sa_make_unspec(), torrent::sa_make_unspec()));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sa_make_inet(), torrent::sa_make_inet()));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sa_make_inet6(), torrent::sa_make_inet6()));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal(torrent::sa_make_unspec(), torrent::sa_make_inet()));
++ CPPUNIT_ASSERT(!torrent::sap_equal(torrent::sa_make_unspec(), torrent::sa_make_inet6()));
++ CPPUNIT_ASSERT(!torrent::sap_equal(torrent::sa_make_inet(), torrent::sa_make_inet6()));
++ CPPUNIT_ASSERT(!torrent::sap_equal(torrent::sa_make_inet6(), torrent::sa_make_inet()));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(sin_1, sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(sin_1, c_sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(c_sin_1, sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(c_sin_1, c_sin_1));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin_1, sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin_1, c_sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(c_sin_1, sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(c_sin_1, c_sin_2));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(sin6_1, sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(sin6_1, c_sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(c_sin6_1, sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(c_sin6_1, c_sin6_1));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin6_1, sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin6_1, c_sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(c_sin6_1, sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal(c_sin6_1, c_sin6_2));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(sin_1_5000, sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(sin6_1_5000, sin6_1_5000));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin_1_5000, sin_1_5100));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin6_1_5000, sin6_1_5100));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin_1_5000, sin_2_5000));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin6_1_5000, sin6_2_5000));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin_1_5000, sin_2_5100));
++ CPPUNIT_ASSERT(!torrent::sap_equal(sin6_1_5000, sin6_2_5100));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal(torrent::sa_make_unix(""), torrent::sa_make_unix("")), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal(torrent::sa_make_unix(""), sin6_1), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal(sin6_1, torrent::sa_make_unix("")), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_equal_addr() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sa_make_unspec(), torrent::sa_make_unspec()));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sa_make_inet(), torrent::sa_make_inet()));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sa_make_inet6(), torrent::sa_make_inet6()));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(torrent::sa_make_unspec(), torrent::sa_make_inet()));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(torrent::sa_make_unspec(), torrent::sa_make_inet6()));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(torrent::sa_make_inet(), torrent::sa_make_inet6()));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(torrent::sa_make_inet6(), torrent::sa_make_inet()));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin_1, sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin_1, c_sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(c_sin_1, sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(c_sin_1, c_sin_1));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin_1, sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin_1, c_sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(c_sin_1, sin_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(c_sin_1, c_sin_2));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin6_1, sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin6_1, c_sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(c_sin6_1, sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(c_sin6_1, c_sin6_1));
++
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin6_1, sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin6_1, c_sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(c_sin6_1, sin6_2));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(c_sin6_1, c_sin6_2));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin_1_5000, sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin6_1_5000, sin6_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin_1_5000, sin_1_5100));
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(sin6_1_5000, sin6_1_5100));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin_1_5000, sin_2_5000));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin6_1_5000, sin6_2_5000));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin_1_5000, sin_2_5100));
++ CPPUNIT_ASSERT(!torrent::sap_equal_addr(sin6_1_5000, sin6_2_5100));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal_addr(torrent::sa_make_unix(""), torrent::sa_make_unix("")), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal_addr(torrent::sa_make_unix(""), sin6_1), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_equal_addr(sin6_1, torrent::sa_make_unix("")), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_copy() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(torrent::sa_make_unspec()), torrent::sa_make_unspec()));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(torrent::sa_make_inet()), sin_any));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(torrent::sa_make_inet6()), sin6_any));
++
++ CPPUNIT_ASSERT(torrent::sap_copy(sin_1).get() != sin_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy(c_sin_1).get() != c_sin_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy(sin6_1).get() != sin6_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy(c_sin6_1).get() != c_sin6_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy(sin_1_5000).get() != sin_1_5000.get());
++ CPPUNIT_ASSERT(torrent::sap_copy(sin6_1_5000).get() != sin6_1_5000.get());
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin_1), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin_1), c_sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(c_sin_1), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(c_sin_1), c_sin_1));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin6_1), sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin6_1), c_sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(c_sin6_1), sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(c_sin6_1), c_sin6_1));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin_1_5000), sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin6_1_5000), sin6_1_5000));
++
++ auto sin6_flags = torrent::sap_copy(sin6_1_5000);
++ reinterpret_cast<sockaddr_in6*>(sin6_flags.get())->sin6_flowinfo = 0x12345678;
++ reinterpret_cast<sockaddr_in6*>(sin6_flags.get())->sin6_scope_id = 0x12345678;
++
++ // TODO: Need 'strict' equal test.
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy(sin6_flags), sin6_flags));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy(torrent::sa_unique_ptr()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy(torrent::c_sa_unique_ptr()), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_copy_addr() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_unspec()), torrent::sa_make_unspec()));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_inet()), sin_any));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_inet6()), sin6_any));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_unspec(), 5000), torrent::sa_make_unspec()));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_inet(), 5000), sin_any_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(torrent::sa_make_inet6(), 5000), sin6_any_5000));
++
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(sin_1).get() != sin_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(c_sin_1).get() != c_sin_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(sin6_1).get() != sin6_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(c_sin6_1).get() != c_sin6_1.get());
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(sin_1_5000).get() != sin_1_5000.get());
++ CPPUNIT_ASSERT(torrent::sap_copy_addr(sin6_1_5000).get() != sin6_1_5000.get());
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1), c_sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin_1), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin_1), c_sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1, 5000), sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1, 5000), c_sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin_1, 5000), sin_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin_1, 5000), c_sin_1_5000));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1), sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1), c_sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin6_1), sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin6_1), c_sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1, 5000), sin6_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1, 5000), c_sin6_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin6_1, 5000), sin6_1_5000));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(c_sin6_1, 5000), c_sin6_1_5000));
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1_5000), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1_5000), sin6_1));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin_1_5000, 5100), sin_1_5100));
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_1_5000, 5100), sin6_1_5100));
++
++ auto sin6_flags = wrap_ai_get_first_sa("ff01::1", "5555");
++ reinterpret_cast<sockaddr_in6*>(sin6_flags.get())->sin6_flowinfo = 0x12345678;
++ reinterpret_cast<sockaddr_in6*>(sin6_flags.get())->sin6_scope_id = 0x12345678;
++
++ CPPUNIT_ASSERT(torrent::sap_equal(torrent::sap_copy_addr(sin6_flags), sin6_1));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy_addr(torrent::sa_unique_ptr()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy_addr(torrent::c_sa_unique_ptr()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy_addr(torrent::sa_unique_ptr(), 5000), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_copy_addr(torrent::c_sa_unique_ptr(), 5000), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_from_v4mapped() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_from_v4mapped(sin6_v4_any), sin_any));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_from_v4mapped(sin6_v4_any)));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_from_v4mapped(sin6_v4_1), sin_1));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_from_v4mapped(sin6_v4_1)));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_from_v4mapped(sin6_v4_bc), sin_bc));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_from_v4mapped(sin6_v4_bc)));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(torrent::sa_make_unspec()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(torrent::sa_make_inet()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(torrent::sa_make_unix("")), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(sin_any), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(sin_bc), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_from_v4mapped(sin_1), torrent::internal_error);
++}
++
++void
++test_socket_address::test_sa_to_v4mapped() {
++ TEST_DEFAULT_SA;
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_to_v4mapped(sin_any), sin6_v4_any));
++ CPPUNIT_ASSERT(torrent::sap_is_v4mapped(torrent::sap_to_v4mapped(sin_any)));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_to_v4mapped(sin_any)));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_to_v4mapped(sin_bc), sin6_v4_bc));
++ CPPUNIT_ASSERT(torrent::sap_is_v4mapped(torrent::sap_to_v4mapped(sin_bc)));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_to_v4mapped(sin_bc)));
++
++ CPPUNIT_ASSERT(torrent::sap_equal_addr(torrent::sap_to_v4mapped(sin_1), sin6_v4_1));
++ CPPUNIT_ASSERT(torrent::sap_is_v4mapped(torrent::sap_to_v4mapped(sin_1)));
++ CPPUNIT_ASSERT(torrent::sap_is_port_any(torrent::sap_to_v4mapped(sin_1)));
++
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(torrent::sa_make_unspec()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(torrent::sa_make_inet6()), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(torrent::sa_make_unix("")), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(sin6_any), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(sin6_1), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(sin6_v4_any), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(sin6_v4_bc), torrent::internal_error);
++ CPPUNIT_ASSERT_THROW(torrent::sap_to_v4mapped(sin6_v4_1), torrent::internal_error);
++}
+diff --git a/test/torrent/net/test_socket_address.h b/test/torrent/net/test_socket_address.h
+new file mode 100644
+index 00000000..6157f366
+--- /dev/null
++++ b/test/torrent/net/test_socket_address.h
+@@ -0,0 +1,43 @@
++#include <cppunit/extensions/HelperMacros.h>
++
++class test_socket_address : public CppUnit::TestFixture {
++ CPPUNIT_TEST_SUITE(test_socket_address);
++
++ CPPUNIT_TEST(test_sa_is_any);
++ CPPUNIT_TEST(test_sa_is_broadcast);
++
++ CPPUNIT_TEST(test_make);
++
++ CPPUNIT_TEST(test_sin_from_sa);
++ CPPUNIT_TEST(test_sin6_from_sa);
++
++ CPPUNIT_TEST(test_sa_equal);
++ CPPUNIT_TEST(test_sa_equal_addr);
++ CPPUNIT_TEST(test_sa_copy);
++ CPPUNIT_TEST(test_sa_copy_addr);
++
++ CPPUNIT_TEST(test_sa_from_v4mapped);
++ CPPUNIT_TEST(test_sa_to_v4mapped);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void setUp() {}
++ void tearDown() {}
++
++ void test_sa_is_any();
++ void test_sa_is_broadcast();
++
++ void test_make();
++
++ void test_sin_from_sa();
++ void test_sin6_from_sa();
++
++ void test_sa_equal();
++ void test_sa_equal_addr();
++ void test_sa_copy();
++ void test_sa_copy_addr();
++
++ void test_sa_from_v4mapped();
++ void test_sa_to_v4mapped();
++};
+diff --git a/test/torrent/net/test_socket_address_key.cc b/test/torrent/net/test_socket_address_key.cc
+deleted file mode 100644
+index 7892e730..00000000
+--- a/test/torrent/net/test_socket_address_key.cc
++++ /dev/null
+@@ -1,87 +0,0 @@
+-#include "config.h"
+-
+-#include lt_tr1_functional
+-#include <sys/types.h>
+-#include <sys/socket.h>
+-
+-#include "test_socket_address_key.h"
+-
+-#include "torrent/utils/net.h"
+-#include "torrent/net/socket_address_key.h"
+-
+-CPPUNIT_TEST_SUITE_REGISTRATION(test_socket_address_key);
+-
+-// TODO: Move into a test utilities header:
+-
+-typedef std::function<struct addrinfo* ()> addrinfo_ftor;
+-
+-static torrent::socket_address_key
+-test_create_valid(const char* hostname, addrinfo_ftor ftor) {
+- struct addrinfo* addr_info;
+-
+- try {
+- addr_info = ftor();
+- } catch (torrent::address_info_error& e) {
+- CPPUNIT_ASSERT_MESSAGE("Caught address_info_error for '" + std::string(hostname) + "'", false);
+- }
+-
+- CPPUNIT_ASSERT_MESSAGE("test_create_valid could not find '" + std::string(hostname) + "'",
+- addr_info != NULL);
+-
+- torrent::socket_address_key sock_key = torrent::socket_address_key::from_sockaddr(addr_info->ai_addr);
+-
+- CPPUNIT_ASSERT_MESSAGE("test_create_valid failed to create valid socket_address_key for '" + std::string(hostname) + "'",
+- sock_key.is_valid());
+-
+- return sock_key;
+-}
+-
+-static bool
+-test_create_throws(const char* hostname, addrinfo_ftor ftor) {
+- try {
+- ftor();
+-
+- return false;
+- } catch (torrent::address_info_error& e) {
+- return true;
+- }
+-}
+-
+-static torrent::socket_address_key
+-test_create_inet(const char* hostname) {
+- return test_create_valid(hostname, std::bind(&torrent::address_info_lookup, hostname, AF_INET, 0));
+-}
+-
+-static bool
+-test_create_inet_throws(const char* hostname) {
+- return test_create_throws(hostname, std::bind(&torrent::address_info_lookup, hostname, AF_INET, 0));
+-}
+-
+-static torrent::socket_address_key
+-test_create_inet6(const char* hostname) {
+- return test_create_valid(hostname, std::bind(&torrent::address_info_lookup, hostname, AF_INET6, 0));
+-}
+-
+-static bool
+-test_create_inet6_throws(const char* hostname) {
+- return test_create_throws(hostname, std::bind(&torrent::address_info_lookup, hostname, AF_INET6, 0));
+-}
+-
+-//
+-// Basic tests:
+-//
+-
+-void
+-test_socket_address_key::test_basic() {
+- CPPUNIT_ASSERT(test_create_inet("1.1.1.1").is_valid());
+- CPPUNIT_ASSERT(test_create_inet_throws("1.1.1.300"));
+-
+- CPPUNIT_ASSERT(test_create_inet6("ff01::1").is_valid());
+- CPPUNIT_ASSERT(test_create_inet6("2001:0db8:85a3:0000:0000:8a2e:0370:7334").is_valid());
+- CPPUNIT_ASSERT(test_create_inet6("2001:db8:a::123").is_valid());
+-
+- CPPUNIT_ASSERT(test_create_inet6_throws("2001:db8:a::22123"));
+-}
+-
+-
+-// Test lexical comparison:
+diff --git a/test/torrent/object_stream_test.cc b/test/torrent/object_stream_test.cc
+index c8a17049..5ad0c23e 100644
+--- a/test/torrent/object_stream_test.cc
++++ b/test/torrent/object_stream_test.cc
+@@ -1,10 +1,8 @@
+ #include "config.h"
+
+-#define __STDC_CONSTANT_MACROS
+-
+ #include <iostream>
+ #include <sstream>
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <torrent/object.h>
+
+ #include "object_stream_test.h"
+diff --git a/test/torrent/tracker_controller_features.cc b/test/torrent/tracker_controller_features.cc
+index 63f163f5..0a6a57d6 100644
+--- a/test/torrent/tracker_controller_features.cc
++++ b/test/torrent/tracker_controller_features.cc
+@@ -1,7 +1,7 @@
+ #include "config.h"
+
++#include <functional>
+ #include <iostream>
+-#include lt_tr1_functional
+
+ #include "rak/priority_queue_default.h"
+
+diff --git a/test/torrent/tracker_controller_requesting.cc b/test/torrent/tracker_controller_requesting.cc
+index 5bc25169..92e664b3 100644
+--- a/test/torrent/tracker_controller_requesting.cc
++++ b/test/torrent/tracker_controller_requesting.cc
+@@ -1,7 +1,7 @@
+ #include "config.h"
+
++#include <functional>
+ #include <iostream>
+-#include lt_tr1_functional
+
+ #include "rak/priority_queue_default.h"
+
+diff --git a/test/torrent/tracker_controller_test.cc b/test/torrent/tracker_controller_test.cc
+index 823a9d34..9406c99e 100644
+--- a/test/torrent/tracker_controller_test.cc
++++ b/test/torrent/tracker_controller_test.cc
+@@ -1,7 +1,7 @@
+ #include "config.h"
+
++#include <functional>
+ #include <iostream>
+-#include lt_tr1_functional
+
+ #include "rak/priority_queue_default.h"
+
+diff --git a/test/torrent/tracker_list_features_test.cc b/test/torrent/tracker_list_features_test.cc
+index 57c05f40..5257b1a7 100644
+--- a/test/torrent/tracker_list_features_test.cc
++++ b/test/torrent/tracker_list_features_test.cc
+@@ -1,6 +1,6 @@
+ #include "config.h"
+
+-#include lt_tr1_functional
++#include <functional>
+
+ #include "torrent/http.h"
+ #include "net/address_list.h"
+diff --git a/test/torrent/utils/directory_events_test.cc b/test/torrent/utils/directory_events_test.cc
+index 2cea5ab5..b97fd1d4 100644
+--- a/test/torrent/utils/directory_events_test.cc
++++ b/test/torrent/utils/directory_events_test.cc
+@@ -1,6 +1,6 @@
+ #include "config.h"
+
+-#include <tr1/functional>
++#include <functional>
+ #include <torrent/exceptions.h>
+ #include <torrent/utils/directory_events.h>
+
+@@ -8,8 +8,6 @@
+
+ CPPUNIT_TEST_SUITE_REGISTRATION(utils_directory_events_test);
+
+-namespace tr1 { using namespace std::tr1; }
+-
+ void
+ utils_directory_events_test::setUp() {
+ }
+diff --git a/test/torrent/utils/log_buffer_test.h b/test/torrent/utils/log_buffer_test.h
+deleted file mode 100644
+index f2824594..00000000
+--- a/test/torrent/utils/log_buffer_test.h
++++ /dev/null
+@@ -1,17 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "torrent/utils/log_buffer.h"
+-
+-class utils_log_buffer_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(utils_log_buffer_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST(test_timestamps);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp();
+- void tearDown();
+-
+- void test_basic();
+- void test_timestamps();
+-};
+diff --git a/test/torrent/utils/log_test.cc b/test/torrent/utils/log_test.cc
+index 9f975636..8cc00ef8 100644
+--- a/test/torrent/utils/log_test.cc
++++ b/test/torrent/utils/log_test.cc
+@@ -3,8 +3,9 @@
+ #include <algorithm>
+ #include <cstring>
+ #include <fstream>
++#include <functional>
+ #include <iostream>
+-#include lt_tr1_functional
++
+ #include <torrent/exceptions.h>
+ #include <torrent/utils/log.h>
+
+@@ -37,6 +38,7 @@ void
+ utils_log_test::setUp() {
+ // Don't initialize since this creates the group->child connections.
+ // torrent::log_initialize();
++ torrent::log_cleanup();
+ }
+
+ void
+diff --git a/test/torrent/utils/net_test.cc b/test/torrent/utils/net_test.cc
+deleted file mode 100644
+index d136e869..00000000
+--- a/test/torrent/utils/net_test.cc
++++ /dev/null
+@@ -1,32 +0,0 @@
+-#include "config.h"
+-
+-#include <torrent/exceptions.h>
+-#include <torrent/utils/net.h>
+-
+-#include "net_test.h"
+-
+-CPPUNIT_TEST_SUITE_REGISTRATION(utils_net_test);
+-
+-static void inc_value(int* value) { (*value)++; }
+-
+-#define LTUNIT_AI_CALL(lt_ai, lt_flags) { \
+- int test_value = 0; \
+- CPPUNIT_ASSERT(torrent::address_info_call(ai, 0, std::bind(&inc_value, &test_value))); \
+- CPPUNIT_ASSERT(test_value); } \
+-
+-void
+-utils_net_test::setUp() {
+-}
+-
+-void
+-utils_net_test::tearDown() {
+-}
+-
+-void
+-utils_net_test::test_basic() {
+- addrinfo* ai = torrent::address_info_lookup("localhost", AF_INET, SOCK_STREAM);
+-
+- LTUNIT_AI_CALL(ai, 0);
+-
+- torrent::address_info_free(ai);
+-}
+diff --git a/test/torrent/utils/net_test.h b/test/torrent/utils/net_test.h
+deleted file mode 100644
+index e538ab39..00000000
+--- a/test/torrent/utils/net_test.h
++++ /dev/null
+@@ -1,15 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "torrent/utils/net.h"
+-
+-class utils_net_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(utils_net_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp();
+- void tearDown();
+-
+- void test_basic();
+-};
+diff --git a/test/torrent/utils/option_strings_test.cc b/test/torrent/utils/option_strings_test.cc
+index c6302f98..a9bdcc89 100644
+--- a/test/torrent/utils/option_strings_test.cc
++++ b/test/torrent/utils/option_strings_test.cc
+@@ -1,8 +1,9 @@
+ #include "config.h"
+
+ #include <fstream>
++#include <functional>
+ #include <iostream>
+-#include lt_tr1_functional
++
+ #include <torrent/exceptions.h>
+ #include <torrent/utils/option_strings.h>
+
+diff --git a/test/torrent/utils/test_extents.cc b/test/torrent/utils/test_extents.cc
+index d6b8d11d..87424d62 100644
+--- a/test/torrent/utils/test_extents.cc
++++ b/test/torrent/utils/test_extents.cc
+@@ -2,7 +2,7 @@
+
+ #include "test_extents.h"
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <iostream>
+ #include <torrent/utils/extents.h>
+
+diff --git a/test/torrent/utils/log_buffer_test.cc b/test/torrent/utils/test_log_buffer.cc
+similarity index 86%
+rename from test/torrent/utils/log_buffer_test.cc
+rename to test/torrent/utils/test_log_buffer.cc
+index a0ede0a0..a56a5365 100644
+--- a/test/torrent/utils/log_buffer_test.cc
++++ b/test/torrent/utils/test_log_buffer.cc
+@@ -1,23 +1,23 @@
+ #include "config.h"
+
+-#include <torrent/utils/log_buffer.h>
++#include "test_log_buffer.h"
+
+ #include "globals.h"
+-#include "log_buffer_test.h"
++#include <torrent/utils/log_buffer.h>
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(utils_log_buffer_test);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_log_buffer, "torrent/utils");
+
+ void
+-utils_log_buffer_test::setUp() {
++test_log_buffer::setUp() {
+ torrent::cachedTime = rak::timer::from_seconds(1000);
+ }
+
+ void
+-utils_log_buffer_test::tearDown() {
++test_log_buffer::tearDown() {
+ }
+
+ void
+-utils_log_buffer_test::test_basic() {
++test_log_buffer::test_basic() {
+ torrent::log_buffer log;
+
+ log.lock();
+@@ -44,7 +44,7 @@ utils_log_buffer_test::test_basic() {
+ }
+
+ void
+-utils_log_buffer_test::test_timestamps() {
++test_log_buffer::test_timestamps() {
+ torrent::log_buffer log;
+
+ log.lock_and_push_log("foobar", 6, 0);
+diff --git a/test/torrent/utils/test_log_buffer.h b/test/torrent/utils/test_log_buffer.h
+new file mode 100644
+index 00000000..290df4c1
+--- /dev/null
++++ b/test/torrent/utils/test_log_buffer.h
+@@ -0,0 +1,17 @@
++#include "helpers/test_fixture.h"
++
++class test_log_buffer : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_log_buffer);
++
++ CPPUNIT_TEST(test_basic);
++ CPPUNIT_TEST(test_timestamps);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void setUp();
++ void tearDown();
++
++ void test_basic();
++ void test_timestamps();
++};
+diff --git a/test/torrent/utils/test_uri_parser.cc b/test/torrent/utils/test_uri_parser.cc
+index c3b46eef..1f4bebe8 100644
+--- a/test/torrent/utils/test_uri_parser.cc
++++ b/test/torrent/utils/test_uri_parser.cc
+@@ -2,7 +2,7 @@
+
+ #include "test_uri_parser.h"
+
+-#include <inttypes.h>
++#include <cinttypes>
+ #include <iostream>
+ #include <torrent/utils/uri_parser.h>
+
+diff --git a/test/torrent/utils/thread_base_test.cc b/test/torrent/utils/thread_base_test.cc
+index 5cb7553f..8366c9ba 100644
+--- a/test/torrent/utils/thread_base_test.cc
++++ b/test/torrent/utils/thread_base_test.cc
+@@ -1,7 +1,8 @@
+ #include "config.h"
+
++#include <functional>
+ #include <unistd.h>
+-#include lt_tr1_functional
++
+ #include <torrent/exceptions.h>
+ #include <torrent/poll_select.h>
+ #include <torrent/utils/thread_base.h>
new file mode 100644
@@ -0,0 +1,26 @@
+From b656a77864bd322d69522f1f9d922404066e5a7c Mon Sep 17 00:00:00 2001
+From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Date: Mon, 21 Oct 2019 09:32:15 +0200
+Subject: [PATCH] libtorrent.pc.in: add Libs.Private (#202)
+
+Add Libs.Private: -lz so applications that want to link statically with
+libtorrent (such as rtorrent) will know that they must link with -lz
+
+Fixes:
+ - http://autobuild.buildroot.org/results/075598e1699c2ac20a4dfbcb5695bbb7343f9a86
+
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+---
+ libtorrent.pc.in | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/libtorrent.pc.in b/libtorrent.pc.in
+index cf6612bc..6108f7e9 100644
+--- a/libtorrent.pc.in
++++ b/libtorrent.pc.in
+@@ -7,4 +7,5 @@ Name: libtorrent
+ Description: A BitTorrent library
+ Version: @VERSION@
+ Libs: -L${libdir} -ltorrent
++Libs.Private: -lz
+ Cflags: -I${includedir}
new file mode 100644
@@ -0,0 +1,27 @@
+From 7c80dc3e3a47dd996ca0554fce4f69d16761a9c9 Mon Sep 17 00:00:00 2001
+From: Tadej Obrstar <tadej.obrstar@gmail.com>
+Date: Sun, 1 Dec 2019 06:59:25 +0100
+Subject: [PATCH] Fix for inotify missing quickly renamed files (#203)
+
+---
+ src/torrent/utils/directory_events.cc | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/src/torrent/utils/directory_events.cc b/src/torrent/utils/directory_events.cc
+index 5d789f4e..8dcc2a61 100644
+--- a/src/torrent/utils/directory_events.cc
++++ b/src/torrent/utils/directory_events.cc
+@@ -154,8 +154,11 @@ directory_events::event_read() {
+ wd_list::const_iterator itr = std::find_if(m_wd_list.begin(), m_wd_list.end(),
+ std::bind(&watch_descriptor::compare_desc, std::placeholders::_1, event->wd));
+
+- if (itr != m_wd_list.end())
+- itr->slot(itr->path + event->name);
++ if (itr != m_wd_list.end()) {
++ std::string sname(event->name);
++ if((sname.substr(sname.find_last_of(".") ) == ".torrent"))
++ itr->slot(itr->path + event->name);
++ }
+
+ event = (struct inotify_event*)(next_event);
+ }
new file mode 100644
@@ -0,0 +1,470 @@
+From 81897862edea81e9620493c473f488d1820bcf93 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Tue, 3 Dec 2019 21:53:48 +0900
+Subject: [PATCH] Fix compiler warnings. (#204)
+
+---
+ configure.ac | 6 ++-
+ scripts/ax_execinfo.m4 | 67 +++++++++++++++++++++++++++++++
+ scripts/common.m4 | 15 -------
+ scripts/rak_execinfo.m4 | 11 +++++
+ src/torrent/exceptions.cc | 4 +-
+ test/Makefile.am | 15 ++-----
+ test/helpers/expect_fd.h | 8 ++--
+ test/helpers/mock_compare.h | 6 +--
+ test/helpers/progress_listener.cc | 3 +-
+ test/main.cc | 4 +-
+ test/net/Makefile.am | 40 ++++++++++++++++++
+ test/torrent/net/Makefile.am | 44 ++++++++++++++++++++
+ test/torrent/net/test_fd.cc | 48 +++++++++++-----------
+ 13 files changed, 207 insertions(+), 64 deletions(-)
+ create mode 100644 scripts/ax_execinfo.m4
+ create mode 100644 scripts/rak_execinfo.m4
+ create mode 100644 test/net/Makefile.am
+ create mode 100644 test/torrent/net/Makefile.am
+
+diff --git a/configure.ac b/configure.ac
+index 620ca552..b6708366 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -33,6 +33,7 @@ RAK_CHECK_CXXFLAGS
+ RAK_ENABLE_DEBUG
+ RAK_ENABLE_EXTRA_DEBUG
+ RAK_ENABLE_WERROR
++RAK_DISABLE_BACKTRACE
+
+ RAK_CHECK_CXX11
+
+@@ -57,8 +58,8 @@ TORRENT_WITH_INOTIFY
+
+ CC_ATTRIBUTE_VISIBILITY
+
+-AX_PTHREAD
+ AX_CHECK_ZLIB
++AX_PTHREAD
+
+ PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
+
+@@ -74,7 +75,6 @@ AC_CHECK_FUNCS(posix_memalign)
+ TORRENT_CHECK_MADVISE()
+ TORRENT_CHECK_CACHELINE()
+ TORRENT_CHECK_POPCOUNT()
+-TORRENT_CHECK_EXECINFO()
+ TORRENT_CHECK_PTHREAD_SETNAME_NP()
+ TORRENT_MINCORE()
+
+@@ -111,4 +111,6 @@ AC_OUTPUT([
+ src/tracker/Makefile
+ src/utils/Makefile
+ test/Makefile
++ test/torrent/net/Makefile
++ test/net/Makefile
+ ])
+diff --git a/scripts/ax_execinfo.m4 b/scripts/ax_execinfo.m4
+new file mode 100644
+index 00000000..0ff5fc0e
+--- /dev/null
++++ b/scripts/ax_execinfo.m4
+@@ -0,0 +1,67 @@
++# ===========================================================================
++# https://www.gnu.org/software/autoconf-archive/ax_execinfo.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++# AX_EXECINFO([ACTION-IF-EXECINFO-H-IS-FOUND], [ACTION-IF-EXECINFO-H-IS-NOT-FOUND], [ADDITIONAL-TYPES-LIST])
++#
++# DESCRIPTION
++#
++# Checks for execinfo.h header and if the len parameter/return type can be
++# found from a list, also define backtrace_size_t to that type.
++#
++# By default the list of types to try contains int and size_t, but should
++# some yet undiscovered system use e.g. unsigned, the 3rd argument can be
++# used for extensions. I'd like to hear of further suggestions.
++#
++# Executes ACTION-IF-EXECINFO-H-IS-FOUND when present and the execinfo.h
++# header is found or ACTION-IF-EXECINFO-H-IS-NOT-FOUND in case the header
++# seems unavailable.
++#
++# Also adds -lexecinfo to LIBS on BSD if needed.
++#
++# LICENSE
++#
++# Copyright (c) 2014 Thomas Jahns <jahns@dkrz.de>
++#
++# Copying and distribution of this file, with or without modification, are
++# permitted in any medium without royalty provided the copyright notice
++# and this notice are preserved. This file is offered as-is, without any
++# warranty.
++
++#serial 2
++
++AC_DEFUN([AX_EXECINFO],
++ [AC_CHECK_HEADERS([execinfo.h])
++ AS_IF([test x"$ac_cv_header_execinfo_h" = xyes],
++ [AC_CACHE_CHECK([size parameter type for backtrace()],
++ [ax_cv_proto_backtrace_type],
++ [AC_LANG_PUSH([C])
++ for ax_cv_proto_backtrace_type in size_t int m4_ifnblank([$3],[$3 ])none; do
++ AS_IF([test "${ax_cv_proto_backtrace_type}" = none],
++ [ax_cv_proto_backtrace_type= ; break])
++ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
++#include <execinfo.h>
++extern
++${ax_cv_proto_backtrace_type} backtrace(void **addrlist, ${ax_cv_proto_backtrace_type} len);
++char **backtrace_symbols(void *const *buffer, ${ax_cv_proto_backtrace_type} size);
++])],
++ [break])
++ done
++ AC_LANG_POP([C])])])
++ AS_IF([test x${ax_cv_proto_backtrace_type} != x],
++ [AC_DEFINE_UNQUOTED([backtrace_size_t], [$ax_cv_proto_backtrace_type],
++ [Defined to return type of backtrace().])])
++ AC_SEARCH_LIBS([backtrace],[execinfo])
++ AS_IF([test x"${ax_cv_proto_backtrace_type}" != x -a x"$ac_cv_header_execinfo_h" = xyes -a x"$ac_cv_search_backtrace" != xno],
++ [AC_DEFINE([HAVE_BACKTRACE],[1],
++ [Defined if backtrace() could be fully identified.])
++ ]m4_ifnblank([$1],[$1
++]),m4_ifnblank([$2],[$2
++]))])
++dnl
++dnl Local Variables:
++dnl mode: autoconf
++dnl End:
++dnl
+diff --git a/scripts/common.m4 b/scripts/common.m4
+index b6d051f5..55e8d66e 100644
+--- a/scripts/common.m4
++++ b/scripts/common.m4
+@@ -150,21 +150,6 @@ dnl Need to fix this so that it uses the stuff defined by the system.
+ ])
+ ])
+
+-AC_DEFUN([TORRENT_CHECK_EXECINFO], [
+- AC_MSG_CHECKING(for execinfo.h)
+-
+- AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+- #include <execinfo.h>
+- int main() { backtrace((void**)0, 0); backtrace_symbols((char**)0, 0); return 0;}
+- ])],
+- [
+- AC_MSG_RESULT(yes)
+- AC_DEFINE(USE_EXECINFO, 1, Use execinfo.h)
+- ], [
+- AC_MSG_RESULT(no)
+- ])
+-])
+-
+ AC_DEFUN([TORRENT_CHECK_ALIGNED], [
+ AC_MSG_CHECKING(the byte alignment)
+
+diff --git a/scripts/rak_execinfo.m4 b/scripts/rak_execinfo.m4
+new file mode 100644
+index 00000000..c1d9b2f8
+--- /dev/null
++++ b/scripts/rak_execinfo.m4
+@@ -0,0 +1,11 @@
++AC_DEFUN([RAK_DISABLE_BACKTRACE], [
++ AC_ARG_ENABLE(backtrace,
++ AC_HELP_STRING([--disable-backtrace], [disable backtrace information [[default=no]]]),
++ [
++ if test "$enableval" = "yes"; then
++ AX_EXECINFO
++ fi
++ ],[
++ AX_EXECINFO
++ ])
++])
+diff --git a/src/torrent/exceptions.cc b/src/torrent/exceptions.cc
+index 2aeca1d7..f834f9fa 100644
+--- a/src/torrent/exceptions.cc
++++ b/src/torrent/exceptions.cc
+@@ -42,7 +42,7 @@
+ #include <sstream>
+ #include <unistd.h>
+
+-#ifdef USE_EXECINFO
++#ifdef HAVE_BACKTRACE
+ #include <execinfo.h>
+ #endif
+
+@@ -75,7 +75,7 @@ internal_error::initialize(const std::string& msg) {
+
+ std::stringstream output;
+
+-#ifdef USE_EXECINFO
++#ifdef HAVE_BACKTRACE
+ void* stackPtrs[20];
+
+ // Print the stack and exit.
+diff --git a/test/Makefile.am b/test/Makefile.am
+index b60a86a6..23b260e4 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -1,3 +1,5 @@
++SUBDIRS = torrent/net net
++
+ TESTS = LibTorrentTest
+ AUTOMAKE_OPTIONS = subdir-objects
+
+@@ -43,19 +45,9 @@ LibTorrentTest_SOURCES = \
+ data/hash_queue_test.cc \
+ data/hash_queue_test.h \
+ \
+- net/test_socket_listen.cc \
+- net/test_socket_listen.h \
+- \
+ protocol/test_request_list.cc \
+ protocol/test_request_list.h \
+ \
+- torrent/net/test_address_info.cc \
+- torrent/net/test_address_info.h \
+- torrent/net/test_fd.cc \
+- torrent/net/test_fd.h \
+- torrent/net/test_socket_address.cc \
+- torrent/net/test_socket_address.h \
+- \
+ torrent/utils/log_test.cc \
+ torrent/utils/log_test.h \
+ torrent/utils/option_strings_test.cc \
+@@ -97,9 +89,10 @@ LibTorrentTest_SOURCES = \
+ torrent/tracker_timeout_test.h \
+ tracker/tracker_http_test.cc \
+ tracker/tracker_http_test.h \
++ \
+ main.cc
+
+ LibTorrentTest_CXXFLAGS = $(CPPUNIT_CFLAGS)
+-LibTorrentTest_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrentTest_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+
+ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src
+diff --git a/test/helpers/expect_fd.h b/test/helpers/expect_fd.h
+index 178cbabc..cc77c34a 100644
+--- a/test/helpers/expect_fd.h
++++ b/test/helpers/expect_fd.h
+@@ -18,15 +18,15 @@ sap_cache_copy_addr_c_ptr(sap_cache_type& sap_cache, const torrent::c_sa_unique_
+
+ inline void
+ expect_event_open_re(int idx) {
+- mock_expect(&torrent::poll_event_open, mock_compare_map<torrent::Event>::begin_pointer + idx);
+- mock_expect(&torrent::poll_event_insert_read, mock_compare_map<torrent::Event>::begin_pointer + idx);
+- mock_expect(&torrent::poll_event_insert_error, mock_compare_map<torrent::Event>::begin_pointer + idx);
++ mock_expect(&torrent::poll_event_open, mock_compare_map<torrent::Event>::begin_pointer() + idx);
++ mock_expect(&torrent::poll_event_insert_read, mock_compare_map<torrent::Event>::begin_pointer() + idx);
++ mock_expect(&torrent::poll_event_insert_error, mock_compare_map<torrent::Event>::begin_pointer() + idx);
+ }
+
+ inline void
+ expect_event_closed_fd(int idx, int fd) {
+ mock_expect(&torrent::fd__close, 0, fd);
+- mock_expect(&torrent::poll_event_closed, mock_compare_map<torrent::Event>::begin_pointer + idx);
++ mock_expect(&torrent::poll_event_closed, mock_compare_map<torrent::Event>::begin_pointer() + idx);
+ }
+
+ inline void
+diff --git a/test/helpers/mock_compare.h b/test/helpers/mock_compare.h
+index 3ea90305..3cc8d075 100644
+--- a/test/helpers/mock_compare.h
++++ b/test/helpers/mock_compare.h
+@@ -34,11 +34,11 @@ template <typename T>
+ struct mock_compare_map {
+ typedef std::map<const T*, const T*> values_type;
+
+- constexpr static T* begin_pointer = reinterpret_cast<T*>(0x1000);
+- constexpr static T* end_pointer = reinterpret_cast<T*>(0x2000);
++ static T* begin_pointer() { return reinterpret_cast<T*>(0x1000); }
++ static T* end_pointer() { return reinterpret_cast<T*>(0x2000); }
+
+ static bool is_key(const T* k) {
+- return k >= begin_pointer && k < end_pointer;
++ return k >= begin_pointer() && k < end_pointer();
+ }
+
+ static bool has_key(const T* k) {
+diff --git a/test/helpers/progress_listener.cc b/test/helpers/progress_listener.cc
+index 02803ffc..c2b60bcd 100644
+--- a/test/helpers/progress_listener.cc
++++ b/test/helpers/progress_listener.cc
+@@ -54,7 +54,8 @@ void
+ progress_listener::startSuite(CppUnit::Test *suite) {
+ m_test_path.push_back(suite);
+
+- std::cout << std::endl << get_test_path(m_test_path) << suite->getName() << ":" << std::endl;
++ if (suite->countTestCases() > 0)
++ std::cout << std::endl << get_test_path(m_test_path) << suite->getName() << ":" << std::endl;
+ }
+
+ void
+diff --git a/test/main.cc b/test/main.cc
+index da93fead..e8a00e1f 100644
+--- a/test/main.cc
++++ b/test/main.cc
+@@ -12,7 +12,7 @@
+ #include <cppunit/extensions/TestFactoryRegistry.h>
+ #include <cppunit/ui/text/TestRunner.h>
+
+-#ifdef USE_EXECINFO
++#ifdef HAVE_BACKTRACE
+ #include <execinfo.h>
+ #endif
+
+@@ -29,7 +29,7 @@ do_test_panic(int signum) {
+
+ std::cout << std::endl << std::endl << "Caught " << strsignal(signum) << ", dumping stack:" << std::endl << std::endl;
+
+-#ifdef USE_EXECINFO
++#ifdef HAVE_BACKTRACE
+ void* stackPtrs[20];
+
+ // Print the stack and exit.
+diff --git a/test/net/Makefile.am b/test/net/Makefile.am
+new file mode 100644
+index 00000000..bb951814
+--- /dev/null
++++ b/test/net/Makefile.am
+@@ -0,0 +1,40 @@
++TESTS = LibTorrentTestNet
++AUTOMAKE_OPTIONS = subdir-objects
++
++check_PROGRAMS = $(TESTS)
++LibTorrentTestNet_LDADD = \
++ ../../src/libtorrent.la \
++ ../../src/torrent/libsub_torrent.la \
++ ../../src/torrent/data/libsub_torrentdata.la \
++ ../../src/torrent/download/libsub_torrentdownload.la \
++ ../../src/torrent/peer/libsub_torrentpeer.la \
++ ../../src/data/libsub_data.la \
++ ../../src/dht/libsub_dht.la \
++ ../../src/net/libsub_net.la \
++ ../../src/protocol/libsub_protocol.la \
++ ../../src/download/libsub_download.la \
++ ../../src/tracker/libsub_tracker.la \
++ ../../src/utils/libsub_utils.la \
++ ../../src/torrent/utils/libsub_torrentutils.la
++
++LibTorrentTestNet_SOURCES = \
++ ../helpers/expect_fd.h \
++ ../helpers/expect_utils.h \
++ ../helpers/mock_compare.h \
++ ../helpers/mock_function.cc \
++ ../helpers/mock_function.h \
++ ../helpers/network.h \
++ ../helpers/progress_listener.cc \
++ ../helpers/progress_listener.h \
++ ../helpers/test_fixture.cc \
++ ../helpers/test_fixture.h \
++ \
++ test_socket_listen.cc \
++ test_socket_listen.h \
++ \
++ ../main.cc
++
++LibTorrentTestNet_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrentTestNet_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++
++AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/test
+diff --git a/test/torrent/net/Makefile.am b/test/torrent/net/Makefile.am
+new file mode 100644
+index 00000000..8a531cc1
+--- /dev/null
++++ b/test/torrent/net/Makefile.am
+@@ -0,0 +1,44 @@
++TESTS = LibTorrentTestTorrentNet
++AUTOMAKE_OPTIONS = subdir-objects
++
++check_PROGRAMS = $(TESTS)
++LibTorrentTestTorrentNet_LDADD = \
++ ../../../src/libtorrent.la \
++ ../../../src/torrent/libsub_torrent.la \
++ ../../../src/torrent/data/libsub_torrentdata.la \
++ ../../../src/torrent/download/libsub_torrentdownload.la \
++ ../../../src/torrent/peer/libsub_torrentpeer.la \
++ ../../../src/data/libsub_data.la \
++ ../../../src/dht/libsub_dht.la \
++ ../../../src/net/libsub_net.la \
++ ../../../src/protocol/libsub_protocol.la \
++ ../../../src/download/libsub_download.la \
++ ../../../src/tracker/libsub_tracker.la \
++ ../../../src/utils/libsub_utils.la \
++ ../../../src/torrent/utils/libsub_torrentutils.la
++
++LibTorrentTestTorrentNet_SOURCES = \
++ ../../helpers/expect_fd.h \
++ ../../helpers/expect_utils.h \
++ ../../helpers/mock_compare.h \
++ ../../helpers/mock_function.cc \
++ ../../helpers/mock_function.h \
++ ../../helpers/network.h \
++ ../../helpers/progress_listener.cc \
++ ../../helpers/progress_listener.h \
++ ../../helpers/test_fixture.cc \
++ ../../helpers/test_fixture.h \
++ \
++ test_address_info.cc \
++ test_address_info.h \
++ test_fd.cc \
++ test_fd.h \
++ test_socket_address.cc \
++ test_socket_address.h \
++ \
++ ../../main.cc
++
++LibTorrentTestTorrentNet_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrentTestTorrentNet_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++
++AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_srcdir)/test
+diff --git a/test/torrent/net/test_fd.cc b/test/torrent/net/test_fd.cc
+index 3cab0c5e..5e56f0f3 100644
+--- a/test/torrent/net/test_fd.cc
++++ b/test/torrent/net/test_fd.cc
+@@ -1,24 +1,24 @@
+-#include "config.h"
+-
+-#include "test_fd.h"
+-
+-#include <torrent/net/fd.h>
+-
+-CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_fd, "torrent/net");
+-
+-void
+-test_fd::test_valid_flags() {
+- CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream));
+- CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_nonblock));
+- CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_reuse_address));
+- CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only));
+- CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v6only));
+-
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_v4only | torrent::fd_flag_v6only));
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_v6only));
+-
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags()));
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(~torrent::fd_flag_all)));
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(torrent::fd_flag_stream | ~torrent::fd_flag_all)));
+- CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(0x3245132)));
+-}
++#include "config.h"
++
++#include "test_fd.h"
++
++#include <torrent/net/fd.h>
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_fd, "torrent/net");
++
++void
++test_fd::test_valid_flags() {
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_nonblock));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_reuse_address));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v6only));
++
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_v4only | torrent::fd_flag_v6only));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_v6only));
++
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags()));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(~torrent::fd_flag_all)));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(torrent::fd_flag_stream | ~torrent::fd_flag_all)));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(0x3245132)));
++}
new file mode 100644
@@ -0,0 +1,33 @@
+From e813c344b1e4aa89288febb2f59109972083f1bb Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Thu, 5 Dec 2019 01:55:53 -0800
+Subject: [PATCH] Fix log format so GCC can check it. (#205)
+
+---
+ src/torrent/utils/log.cc | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/src/torrent/utils/log.cc b/src/torrent/utils/log.cc
+index b855a2c6..24932996 100644
+--- a/src/torrent/utils/log.cc
++++ b/src/torrent/utils/log.cc
+@@ -16,6 +16,8 @@
+ #include <functional>
+ #include <memory>
+
++#define GROUPFMT (group >= LOG_NON_CASCADING) ? ("%" PRIi32 " ") : ("%" PRIi32 " %c ")
++
+ namespace torrent {
+
+ struct log_cache_entry {
+@@ -356,9 +358,7 @@ log_gz_file_write(std::shared_ptr<log_gz_output>& outfile, const char* data, siz
+
+ // Normal groups are nul-terminated strings.
+ if (group >= 0) {
+- const char* fmt = (group >= LOG_NON_CASCADING) ? ("%" PRIi32 " ") : ("%" PRIi32 " %c ");
+-
+- int buffer_length = snprintf(buffer, 64, fmt,
++ int buffer_length = snprintf(buffer, 64, GROUPFMT,
+ cachedTime.seconds(),
+ log_level_char[group % 6]);
+
new file mode 100644
@@ -0,0 +1,843 @@
+From 7b85e112ac2f59a39afa344148a946553c776142 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Fri, 6 Dec 2019 00:35:21 +0900
+Subject: [PATCH] Consolidate make script to optimize build. (#206)
+
+---
+ configure.ac | 14 +--
+ src/Makefile.am | 147 ++++++++++++++++++++++++-----
+ src/data/Makefile.am | 28 ------
+ src/dht/Makefile.am | 18 ----
+ src/download/Makefile.am | 19 ----
+ src/net/Makefile.am | 30 ------
+ src/protocol/Makefile.am | 28 ------
+ src/torrent/Makefile.am | 154 ++++++++++++++++++++++++++++---
+ src/torrent/download/Makefile.am | 22 -----
+ src/torrent/net/Makefile.am | 25 -----
+ src/torrent/peer/Makefile.am | 28 ------
+ src/torrent/utils/Makefile.am | 41 --------
+ src/tracker/Makefile.am | 11 ---
+ src/utils/Makefile.am | 14 ---
+ test/Makefile.am | 92 +++++++++++++-----
+ 15 files changed, 338 insertions(+), 333 deletions(-)
+ delete mode 100644 src/data/Makefile.am
+ delete mode 100644 src/dht/Makefile.am
+ delete mode 100644 src/download/Makefile.am
+ delete mode 100644 src/net/Makefile.am
+ delete mode 100644 src/protocol/Makefile.am
+ delete mode 100644 src/torrent/download/Makefile.am
+ delete mode 100644 src/torrent/net/Makefile.am
+ delete mode 100644 src/torrent/peer/Makefile.am
+ delete mode 100644 src/torrent/utils/Makefile.am
+ delete mode 100644 src/tracker/Makefile.am
+ delete mode 100644 src/utils/Makefile.am
+
+diff --git a/configure.ac b/configure.ac
+index b6708366..e83710cc 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -17,7 +17,7 @@ AC_SUBST(LIBTORRENT_CURRENT)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_INFO)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_NO)
+
+-AM_INIT_AUTOMAKE([serial-tests])
++AM_INIT_AUTOMAKE([serial-tests subdir-objects])
+ AC_CONFIG_HEADERS(config.h)
+
+ AC_PROG_CXX
+@@ -98,18 +98,6 @@ AC_OUTPUT([
+ Makefile
+ src/Makefile
+ src/torrent/Makefile
+- src/torrent/data/Makefile
+- src/torrent/download/Makefile
+- src/torrent/net/Makefile
+- src/torrent/peer/Makefile
+- src/torrent/utils/Makefile
+- src/data/Makefile
+- src/dht/Makefile
+- src/download/Makefile
+- src/net/Makefile
+- src/protocol/Makefile
+- src/tracker/Makefile
+- src/utils/Makefile
+ test/Makefile
+ test/torrent/net/Makefile
+ test/net/Makefile
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 99aaace0..e96bd74b 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -1,30 +1,12 @@
+-SUBDIRS = \
+- torrent \
+- data \
+- dht \
+- download \
+- net \
+- protocol \
+- tracker \
+- utils
++SUBDIRS = torrent
+
+ lib_LTLIBRARIES = libtorrent.la
++noinst_LTLIBRARIES = libtorrent_other.la
+
+ libtorrent_la_LDFLAGS = -version-info $(LIBTORRENT_INTERFACE_VERSION_INFO)
+ libtorrent_la_LIBADD = \
+- torrent/libsub_torrent.la \
+- torrent/data/libsub_torrentdata.la \
+- torrent/download/libsub_torrentdownload.la \
+- torrent/net/libsub_torrentnet.la \
+- torrent/peer/libsub_torrentpeer.la \
+- torrent/utils/libsub_torrentutils.la \
+- data/libsub_data.la \
+- dht/libsub_dht.la \
+- download/libsub_download.la \
+- net/libsub_net.la \
+- protocol/libsub_protocol.la \
+- tracker/libsub_tracker.la \
+- utils/libsub_utils.la
++ torrent/libtorrent_torrent.la \
++ libtorrent_other.la
+
+ libtorrent_la_SOURCES = \
+ globals.cc \
+@@ -36,4 +18,125 @@ libtorrent_la_SOURCES = \
+ thread_main.cc \
+ thread_main.h
+
++libtorrent_other_la_SOURCES = \
++ data/chunk.cc \
++ data/chunk.h \
++ data/chunk_handle.h \
++ data/chunk_iterator.h \
++ data/chunk_list.cc \
++ data/chunk_list.h \
++ data/chunk_list_node.h \
++ data/chunk_part.cc \
++ data/chunk_part.h \
++ data/hash_check_queue.cc \
++ data/hash_check_queue.h \
++ data/hash_chunk.cc \
++ data/hash_chunk.h \
++ data/hash_queue.cc \
++ data/hash_queue.h \
++ data/hash_queue_node.cc \
++ data/hash_queue_node.h \
++ data/hash_torrent.cc \
++ data/hash_torrent.h \
++ data/memory_chunk.cc \
++ data/memory_chunk.h \
++ data/socket_file.cc \
++ data/socket_file.h \
++ \
++ dht/dht_bucket.cc \
++ dht/dht_bucket.h \
++ dht/dht_hash_map.h \
++ dht/dht_node.cc \
++ dht/dht_node.h \
++ dht/dht_router.cc \
++ dht/dht_router.h \
++ dht/dht_server.cc \
++ dht/dht_server.h \
++ dht/dht_tracker.cc \
++ dht/dht_tracker.h \
++ dht/dht_transaction.cc \
++ dht/dht_transaction.h \
++ \
++ download/available_list.cc \
++ download/available_list.h \
++ download/chunk_selector.cc \
++ download/chunk_selector.h \
++ download/chunk_statistics.cc \
++ download/chunk_statistics.h \
++ download/delegator.cc \
++ download/delegator.h \
++ download/download_constructor.cc \
++ download/download_constructor.h \
++ download/download_main.cc \
++ download/download_main.h \
++ download/download_wrapper.cc \
++ download/download_wrapper.h \
++ \
++ net/address_list.cc \
++ net/address_list.h \
++ net/data_buffer.h \
++ net/local_addr.cc \
++ net/local_addr.h \
++ net/listen.cc \
++ net/listen.h \
++ net/protocol_buffer.h \
++ net/socket_base.cc \
++ net/socket_base.h \
++ net/socket_datagram.cc \
++ net/socket_datagram.h \
++ net/socket_fd.cc \
++ net/socket_fd.h \
++ net/socket_listen.cc \
++ net/socket_listen.h \
++ net/socket_set.cc \
++ net/socket_set.h \
++ net/socket_stream.cc \
++ net/socket_stream.h \
++ net/throttle_internal.cc \
++ net/throttle_internal.h \
++ net/throttle_list.cc \
++ net/throttle_list.h \
++ net/throttle_node.h \
++ \
++ protocol/encryption_info.h \
++ protocol/extensions.cc \
++ protocol/extensions.h \
++ protocol/handshake.cc \
++ protocol/handshake.h \
++ protocol/handshake_encryption.cc \
++ protocol/handshake_encryption.h \
++ protocol/handshake_manager.cc \
++ protocol/handshake_manager.h \
++ protocol/initial_seed.cc \
++ protocol/initial_seed.h \
++ protocol/peer_chunks.h \
++ protocol/peer_connection_base.cc \
++ protocol/peer_connection_base.h \
++ protocol/peer_connection_leech.cc \
++ protocol/peer_connection_leech.h \
++ protocol/peer_connection_metadata.cc \
++ protocol/peer_connection_metadata.h \
++ protocol/peer_factory.cc \
++ protocol/peer_factory.h \
++ protocol/protocol_base.h \
++ protocol/request_list.cc \
++ protocol/request_list.h \
++ \
++ tracker/tracker_dht.cc \
++ tracker/tracker_dht.h \
++ tracker/tracker_http.cc \
++ tracker/tracker_http.h \
++ tracker/tracker_udp.cc \
++ tracker/tracker_udp.h \
++ \
++ utils/diffie_hellman.cc \
++ utils/diffie_hellman.h \
++ utils/instrumentation.cc \
++ utils/instrumentation.h \
++ utils/rc4.h \
++ utils/sha1.h \
++ utils/sha_fast.cc \
++ utils/sha_fast.h \
++ utils/queue_buckets.h
++
+ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir)
+diff --git a/src/data/Makefile.am b/src/data/Makefile.am
+deleted file mode 100644
+index ef41c9bd..00000000
+--- a/src/data/Makefile.am
++++ /dev/null
+@@ -1,28 +0,0 @@
+-noinst_LTLIBRARIES = libsub_data.la
+-
+-libsub_data_la_SOURCES = \
+- chunk.cc \
+- chunk.h \
+- chunk_handle.h \
+- chunk_iterator.h \
+- chunk_list.cc \
+- chunk_list.h \
+- chunk_list_node.h \
+- chunk_part.cc \
+- chunk_part.h \
+- hash_check_queue.cc \
+- hash_check_queue.h \
+- hash_chunk.cc \
+- hash_chunk.h \
+- hash_queue.cc \
+- hash_queue.h \
+- hash_queue_node.cc \
+- hash_queue_node.h \
+- hash_torrent.cc \
+- hash_torrent.h \
+- memory_chunk.cc \
+- memory_chunk.h \
+- socket_file.cc \
+- socket_file.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/dht/Makefile.am b/src/dht/Makefile.am
+deleted file mode 100644
+index a87c57bc..00000000
+--- a/src/dht/Makefile.am
++++ /dev/null
+@@ -1,18 +0,0 @@
+-noinst_LTLIBRARIES = libsub_dht.la
+-
+-libsub_dht_la_SOURCES = \
+- dht_bucket.cc \
+- dht_bucket.h \
+- dht_hash_map.h \
+- dht_node.cc \
+- dht_node.h \
+- dht_router.cc \
+- dht_router.h \
+- dht_server.cc \
+- dht_server.h \
+- dht_tracker.cc \
+- dht_tracker.h \
+- dht_transaction.cc \
+- dht_transaction.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/download/Makefile.am b/src/download/Makefile.am
+deleted file mode 100644
+index 65ceaf97..00000000
+--- a/src/download/Makefile.am
++++ /dev/null
+@@ -1,19 +0,0 @@
+-noinst_LTLIBRARIES = libsub_download.la
+-
+-libsub_download_la_SOURCES = \
+- available_list.cc \
+- available_list.h \
+- chunk_selector.cc \
+- chunk_selector.h \
+- chunk_statistics.cc \
+- chunk_statistics.h \
+- delegator.cc \
+- delegator.h \
+- download_constructor.cc \
+- download_constructor.h \
+- download_main.cc \
+- download_main.h \
+- download_wrapper.cc \
+- download_wrapper.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/net/Makefile.am b/src/net/Makefile.am
+deleted file mode 100644
+index e3a8c7e1..00000000
+--- a/src/net/Makefile.am
++++ /dev/null
+@@ -1,30 +0,0 @@
+-noinst_LTLIBRARIES = libsub_net.la
+-
+-libsub_net_la_SOURCES = \
+- address_list.cc \
+- address_list.h \
+- data_buffer.h \
+- local_addr.cc \
+- local_addr.h \
+- listen.cc \
+- listen.h \
+- protocol_buffer.h \
+- socket_base.cc \
+- socket_base.h \
+- socket_datagram.cc \
+- socket_datagram.h \
+- socket_fd.cc \
+- socket_fd.h \
+- socket_listen.cc \
+- socket_listen.h \
+- socket_set.cc \
+- socket_set.h \
+- socket_stream.cc \
+- socket_stream.h \
+- throttle_internal.cc \
+- throttle_internal.h \
+- throttle_list.cc \
+- throttle_list.h \
+- throttle_node.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/protocol/Makefile.am b/src/protocol/Makefile.am
+deleted file mode 100644
+index 2e9aba7a..00000000
+--- a/src/protocol/Makefile.am
++++ /dev/null
+@@ -1,28 +0,0 @@
+-noinst_LTLIBRARIES = libsub_protocol.la
+-
+-libsub_protocol_la_SOURCES = \
+- encryption_info.h \
+- extensions.cc \
+- extensions.h \
+- handshake.cc \
+- handshake.h \
+- handshake_encryption.cc \
+- handshake_encryption.h \
+- handshake_manager.cc \
+- handshake_manager.h \
+- initial_seed.cc \
+- initial_seed.h \
+- peer_chunks.h \
+- peer_connection_base.cc \
+- peer_connection_base.h \
+- peer_connection_leech.cc \
+- peer_connection_leech.h \
+- peer_connection_metadata.cc \
+- peer_connection_metadata.h \
+- peer_factory.cc \
+- peer_factory.h \
+- protocol_base.h \
+- request_list.cc \
+- request_list.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am
+index 8cd26ce7..30157b95 100644
+--- a/src/torrent/Makefile.am
++++ b/src/torrent/Makefile.am
+@@ -1,13 +1,89 @@
+-SUBDIRS = \
+- data \
+- download \
+- net \
+- peer \
+- utils
++noinst_LTLIBRARIES = libtorrent_torrent.la
+
+-noinst_LTLIBRARIES = libsub_torrent.la
+-
+-libsub_torrent_la_SOURCES = \
++libtorrent_torrent_la_SOURCES = \
++ data/block.cc \
++ data/block.h \
++ data/block_failed.h \
++ data/block_list.cc \
++ data/block_list.h \
++ data/block_transfer.h \
++ data/chunk_utils.cc \
++ data/chunk_utils.h \
++ data/download_data.cc \
++ data/download_data.h \
++ data/file.cc \
++ data/file.h \
++ data/file_list.cc \
++ data/file_list.h \
++ data/file_list_iterator.cc \
++ data/file_list_iterator.h \
++ data/file_manager.cc \
++ data/file_manager.h \
++ data/file_utils.cc \
++ data/file_utils.h \
++ data/piece.h \
++ data/transfer_list.cc \
++ data/transfer_list.h \
++\
++ download/choke_group.cc \
++ download/choke_group.h \
++ download/choke_queue.cc \
++ download/choke_queue.h \
++ download/download_manager.cc \
++ download/download_manager.h \
++ download/group_entry.h \
++ download/resource_manager.cc \
++ download/resource_manager.h \
++\
++ net/address_info.cc \
++ net/address_info.h \
++ net/fd.cc \
++ net/fd.h \
++ net/socket_address.cc \
++ net/socket_address.h \
++ net/socket_address_key.cc \
++ net/socket_address_key.h \
++ net/socket_event.cc \
++ net/socket_event.h \
++ net/types.h \
++\
++ peer/choke_status.h \
++ peer/client_info.cc \
++ peer/client_info.h \
++ peer/client_list.cc \
++ peer/client_list.h \
++ peer/connection_list.cc \
++ peer/connection_list.h \
++ peer/peer.cc \
++ peer/peer.h \
++ peer/peer_info.cc \
++ peer/peer_info.h \
++ peer/peer_list.cc \
++ peer/peer_list.h \
++\
++ utils/directory_events.cc \
++ utils/directory_events.h \
++ utils/extents.h \
++ utils/log.cc \
++ utils/log.h \
++ utils/log_buffer.cc \
++ utils/log_buffer.h \
++ utils/option_strings.cc \
++ utils/option_strings.h \
++ utils/random.cc \
++ utils/random.h \
++ utils/ranges.h \
++ utils/resume.cc \
++ utils/resume.h \
++ utils/signal_bitfield.cc \
++ utils/signal_bitfield.h \
++ utils/thread_base.cc \
++ utils/thread_base.h \
++ utils/thread_interrupt.cc \
++ utils/thread_interrupt.h \
++ utils/uri_parser.cc \
++ utils/uri_parser.h \
++\
+ bitfield.cc \
+ bitfield.h \
+ chunk_manager.cc \
+@@ -61,8 +137,64 @@ libsub_torrent_la_SOURCES = \
+
+ AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+
+-libtorrentincludedir = $(includedir)/torrent
+-libtorrentinclude_HEADERS = \
++libtorrent_torrent_data_includedir = $(includedir)/torrent/data
++libtorrent_torrent_data_include_HEADERS = \
++ data/block.h \
++ data/block_list.h \
++ data/block_transfer.h \
++ data/chunk_utils.h \
++ data/download_data.h \
++ data/file.h \
++ data/file_list.h \
++ data/file_list_iterator.h \
++ data/file_manager.h \
++ data/file_utils.h \
++ data/piece.h \
++ data/transfer_list.h
++
++libtorrent_torrent_download_includedir = $(includedir)/torrent/download
++libtorrent_torrent_download_include_HEADERS = \
++ download/choke_group.h \
++ download/choke_queue.h \
++ download/download_manager.h \
++ download/group_entry.h \
++ download/resource_manager.h
++
++libtorrent_torrent_net_includedir = $(includedir)/torrent/net
++libtorrent_torrent_net_include_HEADERS = \
++ net/address_info.h \
++ net/fd.h \
++ net/socket_address.h \
++ net/socket_address_key.h \
++ net/socket_event.h \
++ net/types.h
++
++libtorrent_torrent_peer_includedir = $(includedir)/torrent/peer
++libtorrent_torrent_peer_include_HEADERS = \
++ peer/choke_status.h \
++ peer/client_info.h \
++ peer/client_list.h \
++ peer/connection_list.h \
++ peer/peer.h \
++ peer/peer_info.h \
++ peer/peer_list.h
++
++libtorrent_torrent_utils_includedir = $(includedir)/torrent/utils
++libtorrent_torrent_utils_include_HEADERS = \
++ utils/directory_events.h \
++ utils/extents.h \
++ utils/log.h \
++ utils/log_buffer.h \
++ utils/option_strings.h \
++ utils/ranges.h \
++ utils/resume.h \
++ utils/signal_bitfield.h \
++ utils/thread_base.h \
++ utils/thread_interrupt.h \
++ utils/uri_parser.h
++
++libtorrent_torrent_includedir = $(includedir)/torrent
++libtorrent_torrent_include_HEADERS = \
+ bitfield.h \
+ chunk_manager.h \
+ common.h \
+diff --git a/src/torrent/download/Makefile.am b/src/torrent/download/Makefile.am
+deleted file mode 100644
+index f92c7aa4..00000000
+--- a/src/torrent/download/Makefile.am
++++ /dev/null
+@@ -1,22 +0,0 @@
+-noinst_LTLIBRARIES = libsub_torrentdownload.la
+-
+-libsub_torrentdownload_la_SOURCES = \
+- choke_group.cc \
+- choke_group.h \
+- choke_queue.cc \
+- choke_queue.h \
+- download_manager.cc \
+- download_manager.h \
+- group_entry.h \
+- resource_manager.cc \
+- resource_manager.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+-
+-libtorrentincludedir = $(includedir)/torrent/download
+-libtorrentinclude_HEADERS = \
+- choke_group.h \
+- choke_queue.h \
+- download_manager.h \
+- group_entry.h \
+- resource_manager.h
+diff --git a/src/torrent/net/Makefile.am b/src/torrent/net/Makefile.am
+deleted file mode 100644
+index 35dd4774..00000000
+--- a/src/torrent/net/Makefile.am
++++ /dev/null
+@@ -1,25 +0,0 @@
+-noinst_LTLIBRARIES = libsub_torrentnet.la
+-
+-libsub_torrentnet_la_SOURCES = \
+- address_info.cc \
+- address_info.h \
+- fd.cc \
+- fd.h \
+- socket_address.cc \
+- socket_address.h \
+- socket_address_key.cc \
+- socket_address_key.h \
+- socket_event.cc \
+- socket_event.h \
+- types.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+-
+-libtorrentincludedir = $(includedir)/torrent/net
+-libtorrentinclude_HEADERS = \
+- address_info.h \
+- fd.h \
+- socket_address.h \
+- socket_address_key.h \
+- socket_event.h \
+- types.h
+diff --git a/src/torrent/peer/Makefile.am b/src/torrent/peer/Makefile.am
+deleted file mode 100644
+index 1324e88a..00000000
+--- a/src/torrent/peer/Makefile.am
++++ /dev/null
+@@ -1,28 +0,0 @@
+-noinst_LTLIBRARIES = libsub_torrentpeer.la
+-
+-libsub_torrentpeer_la_SOURCES = \
+- choke_status.h \
+- client_info.cc \
+- client_info.h \
+- client_list.cc \
+- client_list.h \
+- connection_list.cc \
+- connection_list.h \
+- peer.cc \
+- peer.h \
+- peer_info.cc \
+- peer_info.h \
+- peer_list.cc \
+- peer_list.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+-
+-libtorrentincludedir = $(includedir)/torrent/peer
+-libtorrentinclude_HEADERS = \
+- choke_status.h \
+- client_info.h \
+- client_list.h \
+- connection_list.h \
+- peer.h \
+- peer_info.h \
+- peer_list.h
+diff --git a/src/torrent/utils/Makefile.am b/src/torrent/utils/Makefile.am
+deleted file mode 100644
+index a48786c6..00000000
+--- a/src/torrent/utils/Makefile.am
++++ /dev/null
+@@ -1,41 +0,0 @@
+-noinst_LTLIBRARIES = libsub_torrentutils.la
+-
+-libsub_torrentutils_la_SOURCES = \
+- directory_events.cc \
+- directory_events.h \
+- extents.h \
+- log.cc \
+- log.h \
+- log_buffer.cc \
+- log_buffer.h \
+- option_strings.cc \
+- option_strings.h \
+- random.cc \
+- random.h \
+- ranges.h \
+- resume.cc \
+- resume.h \
+- signal_bitfield.cc \
+- signal_bitfield.h \
+- thread_base.cc \
+- thread_base.h \
+- thread_interrupt.cc \
+- thread_interrupt.h \
+- uri_parser.cc \
+- uri_parser.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+-
+-libtorrentincludedir = $(includedir)/torrent/utils
+-libtorrentinclude_HEADERS = \
+- directory_events.h \
+- extents.h \
+- log.h \
+- log_buffer.h \
+- option_strings.h \
+- ranges.h \
+- resume.h \
+- signal_bitfield.h \
+- thread_base.h \
+- thread_interrupt.h \
+- uri_parser.h
+diff --git a/src/tracker/Makefile.am b/src/tracker/Makefile.am
+deleted file mode 100644
+index 2f1ae5cf..00000000
+--- a/src/tracker/Makefile.am
++++ /dev/null
+@@ -1,11 +0,0 @@
+-noinst_LTLIBRARIES = libsub_tracker.la
+-
+-libsub_tracker_la_SOURCES = \
+- tracker_dht.cc \
+- tracker_dht.h \
+- tracker_http.cc \
+- tracker_http.h \
+- tracker_udp.cc \
+- tracker_udp.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
+deleted file mode 100644
+index 27ce359b..00000000
+--- a/src/utils/Makefile.am
++++ /dev/null
+@@ -1,14 +0,0 @@
+-noinst_LTLIBRARIES = libsub_utils.la
+-
+-libsub_utils_la_SOURCES = \
+- diffie_hellman.cc \
+- diffie_hellman.h \
+- instrumentation.cc \
+- instrumentation.h \
+- rc4.h \
+- sha1.h \
+- sha_fast.cc \
+- sha_fast.h \
+- queue_buckets.h
+-
+-AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(top_srcdir)
+diff --git a/test/Makefile.am b/test/Makefile.am
+index 23b260e4..8b0291bb 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -1,25 +1,68 @@
+-SUBDIRS = torrent/net net
+-
+-TESTS = LibTorrentTest
+-AUTOMAKE_OPTIONS = subdir-objects
++TESTS = \
++ LibTorrent_Test_Torrent_Net \
++ LibTorrent_Test_Net \
++ LibTorrent_Test
+
+ check_PROGRAMS = $(TESTS)
+-LibTorrentTest_LDADD = \
++
++LibTorrent_Test_LDADD = \
+ ../src/libtorrent.la \
+- ../src/torrent/libsub_torrent.la \
+- ../src/torrent/data/libsub_torrentdata.la \
+- ../src/torrent/download/libsub_torrentdownload.la \
+- ../src/torrent/peer/libsub_torrentpeer.la \
+- ../src/data/libsub_data.la \
+- ../src/dht/libsub_dht.la \
+- ../src/net/libsub_net.la \
+- ../src/protocol/libsub_protocol.la \
+- ../src/download/libsub_download.la \
+- ../src/tracker/libsub_tracker.la \
+- ../src/utils/libsub_utils.la \
+- ../src/torrent/utils/libsub_torrentutils.la
++ ../src/libtorrent_other.la \
++ ../src/torrent/libtorrent_torrent.la
++
++LibTorrent_Test_Net_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Torrent_Net_LDADD = $(LibTorrent_Test_LDADD)
++
++# LibTorrent_Test_SOURCES = \
++# helpers/expect_fd.h \
++# helpers/expect_utils.h \
++# helpers/mock_compare.h \
++# helpers/mock_function.cc \
++# helpers/mock_function.h \
++# helpers/network.h \
++# helpers/progress_listener.cc \
++# helpers/progress_listener.h \
++# helpers/test_fixture.cc \
++# helpers/test_fixture.h
++
++LibTorrent_Test_Torrent_Net_SOURCES = \
++ main.cc \
++ helpers/expect_fd.h \
++ helpers/expect_utils.h \
++ helpers/mock_compare.h \
++ helpers/mock_function.cc \
++ helpers/mock_function.h \
++ helpers/network.h \
++ helpers/progress_listener.cc \
++ helpers/progress_listener.h \
++ helpers/test_fixture.cc \
++ helpers/test_fixture.h \
++ \
++ torrent/net/test_address_info.cc \
++ torrent/net/test_address_info.h \
++ torrent/net/test_fd.cc \
++ torrent/net/test_fd.h \
++ torrent/net/test_socket_address.cc \
++ torrent/net/test_socket_address.h
+
+-LibTorrentTest_SOURCES = \
++LibTorrent_Test_Net_SOURCES = \
++ main.cc \
++ helpers/expect_fd.h \
++ helpers/expect_utils.h \
++ helpers/mock_compare.h \
++ helpers/mock_function.cc \
++ helpers/mock_function.h \
++ helpers/network.h \
++ helpers/progress_listener.cc \
++ helpers/progress_listener.h \
++ helpers/test_fixture.cc \
++ helpers/test_fixture.h \
++ \
++ net/test_socket_listen.cc \
++ net/test_socket_listen.h
++
++LibTorrent_Test_SOURCES = \
++ main.cc \
+ helpers/expect_fd.h \
+ helpers/expect_utils.h \
+ helpers/mock_compare.h \
+@@ -87,12 +130,15 @@ LibTorrentTest_SOURCES = \
+ torrent/tracker_list_features_test.h \
+ torrent/tracker_timeout_test.cc \
+ torrent/tracker_timeout_test.h \
+- tracker/tracker_http_test.cc \
+- tracker/tracker_http_test.h \
+ \
+- main.cc
++ tracker/tracker_http_test.cc \
++ tracker/tracker_http_test.h
+
+-LibTorrentTest_CXXFLAGS = $(CPPUNIT_CFLAGS)
+-LibTorrentTest_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Torrent_Net_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Torrent_Net_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Net_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Net_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+
+ AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/src
new file mode 100644
@@ -0,0 +1,2710 @@
+From 9cdb950c0db2bad1a4d85b48f06419e2920aa114 Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Fri, 20 Dec 2019 00:37:38 +0900
+Subject: [PATCH] Refactor make process. (#207)
+
+---
+ configure.ac | 3 +-
+ src/Makefile.am | 3 +-
+ src/manager.h | 36 ---
+ src/thread_disk.h | 51 +---
+ src/torrent/utils/thread_base.cc | 36 ---
+ src/torrent/utils/thread_base.h | 48 +---
+ test/Makefile.am | 140 +++++------
+ ...{chunk_list_test.cc => test_chunk_list.cc} | 16 +-
+ .../{chunk_list_test.h => test_chunk_list.h} | 13 +-
+ ...queue_test.cc => test_hash_check_queue.cc} | 33 ++-
+ ...k_queue_test.h => test_hash_check_queue.h} | 22 +-
+ ...{hash_queue_test.cc => test_hash_queue.cc} | 66 +++---
+ .../{hash_queue_test.h => test_hash_queue.h} | 11 +-
+ test/helpers/progress_listener.cc | 4 +-
+ test/helpers/test_fixture.h | 28 +--
+ test/helpers/test_thread.cc | 71 ++++++
+ test/helpers/test_thread.h | 59 +++++
+ test/helpers/test_utils.h | 16 ++
+ test/helpers/utils.h | 120 +++++-----
+ test/main.cc | 4 +-
+ test/torrent/{http_test.cc => test_http.cc} | 17 +-
+ test/torrent/{http_test.h => test_http.h} | 11 +-
+ test/torrent/utils/option_strings_test.h | 17 --
+ test/torrent/utils/signal_bitfield_test.h | 23 --
+ test/torrent/utils/test_extents.cc | 63 ++---
+ test/torrent/utils/test_extents.h | 9 +-
+ .../utils/{log_test.cc => test_log.cc} | 26 +-
+ test/torrent/utils/{log_test.h => test_log.h} | 8 +-
+ test/torrent/utils/test_log_buffer.cc | 11 +-
+ test/torrent/utils/test_log_buffer.h | 5 -
+ ...strings_test.cc => test_option_strings.cc} | 29 +--
+ test/torrent/utils/test_option_strings.h | 10 +
+ test/torrent/utils/test_queue_buckets.cc | 12 +-
+ test/torrent/utils/test_queue_buckets.h | 11 +-
+ ...tfield_test.cc => test_signal_bitfield.cc} | 31 ++-
+ test/torrent/utils/test_signal_bitfield.h | 22 ++
+ test/torrent/utils/test_thread_base.cc | 169 +++++++++++++
+ test/torrent/utils/test_thread_base.h | 25 ++
+ test/torrent/utils/test_uri_parser.cc | 31 +--
+ test/torrent/utils/test_uri_parser.h | 11 +-
+ test/torrent/utils/thread_base_test.cc | 224 ------------------
+ test/torrent/utils/thread_base_test.h | 86 -------
+ test/tracker/test_tracker_http.cc | 11 +
+ test/tracker/test_tracker_http.h | 12 +
+ test/tracker/tracker_http_test.cc | 17 --
+ test/tracker/tracker_http_test.h | 18 --
+ 46 files changed, 735 insertions(+), 954 deletions(-)
+ rename test/data/{chunk_list_test.cc => test_chunk_list.cc} (93%)
+ rename test/data/{chunk_list_test.h => test_chunk_list.h} (88%)
+ rename test/data/{hash_check_queue_test.cc => test_hash_check_queue.cc} (92%)
+ rename test/data/{hash_check_queue_test.h => test_hash_check_queue.h} (63%)
+ rename test/data/{hash_queue_test.cc => test_hash_queue.cc} (82%)
+ rename test/data/{hash_queue_test.h => test_hash_queue.h} (58%)
+ create mode 100755 test/helpers/test_thread.cc
+ create mode 100755 test/helpers/test_thread.h
+ create mode 100644 test/helpers/test_utils.h
+ rename test/torrent/{http_test.cc => test_http.cc} (94%)
+ rename test/torrent/{http_test.h => test_http.h} (63%)
+ delete mode 100644 test/torrent/utils/option_strings_test.h
+ delete mode 100644 test/torrent/utils/signal_bitfield_test.h
+ rename test/torrent/utils/{log_test.cc => test_log.cc} (92%)
+ rename test/torrent/utils/{log_test.h => test_log.h} (71%)
+ rename test/torrent/utils/{option_strings_test.cc => test_option_strings.cc} (65%)
+ create mode 100644 test/torrent/utils/test_option_strings.h
+ rename test/torrent/utils/{signal_bitfield_test.cc => test_signal_bitfield.cc} (85%)
+ create mode 100644 test/torrent/utils/test_signal_bitfield.h
+ create mode 100644 test/torrent/utils/test_thread_base.cc
+ create mode 100644 test/torrent/utils/test_thread_base.h
+ delete mode 100644 test/torrent/utils/thread_base_test.cc
+ delete mode 100644 test/torrent/utils/thread_base_test.h
+ create mode 100644 test/tracker/test_tracker_http.cc
+ create mode 100644 test/tracker/test_tracker_http.h
+ delete mode 100644 test/tracker/tracker_http_test.cc
+ delete mode 100644 test/tracker/tracker_http_test.h
+
+diff --git a/configure.ac b/configure.ac
+index e83710cc..88a46edd 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -21,6 +21,7 @@ AM_INIT_AUTOMAKE([serial-tests subdir-objects])
+ AC_CONFIG_HEADERS(config.h)
+
+ AC_PROG_CXX
++AC_SYS_LARGEFILE
+
+ AC_C_BIGENDIAN(
+ AC_DEFINE(IS_BIG_ENDIAN, 1, Big endian),
+@@ -37,8 +38,6 @@ RAK_DISABLE_BACKTRACE
+
+ RAK_CHECK_CXX11
+
+-AC_SYS_LARGEFILE
+-
+ TORRENT_ENABLE_ALIGNED
+ TORRENT_ENABLE_INTERRUPT_SOCKET
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index e96bd74b..95e6a7ae 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -1,7 +1,8 @@
+ SUBDIRS = torrent
+
+ lib_LTLIBRARIES = libtorrent.la
+-noinst_LTLIBRARIES = libtorrent_other.la
++noinst_LTLIBRARIES = \
++ libtorrent_other.la
+
+ libtorrent_la_LDFLAGS = -version-info $(LIBTORRENT_INTERFACE_VERSION_INFO)
+ libtorrent_la_LIBADD = \
+diff --git a/src/manager.h b/src/manager.h
+index 1db81e9b..1ada9567 100644
+--- a/src/manager.h
++++ b/src/manager.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_MANAGER_H
+ #define LIBTORRENT_MANAGER_H
+
+diff --git a/src/thread_disk.h b/src/thread_disk.h
+index fa1fcb7e..7b378915 100644
+--- a/src/thread_disk.h
++++ b/src/thread_disk.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_THREAD_DISK_H
+ #define LIBTORRENT_THREAD_DISK_H
+
+@@ -42,19 +6,18 @@
+
+ namespace torrent {
+
+-class thread_disk : public thread_base {
++class LIBTORRENT_EXPORT thread_disk : public thread_base {
+ public:
+- const char* name() const { return "rtorrent disk"; }
+-
+- virtual void init_thread();
++ const char* name() const { return "rtorrent disk"; }
++ HashCheckQueue* hash_queue() { return &m_hash_queue; }
+
+- HashCheckQueue* hash_queue() { return &m_hash_queue; }
++ virtual void init_thread();
+
+ protected:
+- virtual void call_events();
+- virtual int64_t next_timeout_usec();
++ virtual void call_events();
++ virtual int64_t next_timeout_usec();
+
+- HashCheckQueue m_hash_queue;
++ HashCheckQueue m_hash_queue;
+ };
+
+ }
+diff --git a/src/torrent/utils/thread_base.cc b/src/torrent/utils/thread_base.cc
+index 778e4c38..99d6355d 100644
+--- a/src/torrent/utils/thread_base.cc
++++ b/src/torrent/utils/thread_base.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <cstring>
+diff --git a/src/torrent/utils/thread_base.h b/src/torrent/utils/thread_base.h
+index b92a98ba..bead9659 100644
+--- a/src/torrent/utils/thread_base.h
++++ b/src/torrent/utils/thread_base.h
+@@ -1,48 +1,12 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_UTILS_THREAD_BASE_H
+ #define LIBTORRENT_UTILS_THREAD_BASE_H
+
+-#include <functional>
+-#include <pthread.h>
+-#include <sys/types.h>
++#import <functional>
++#import <pthread.h>
++#import <sys/types.h>
+
+-#include <torrent/common.h>
+-#include <torrent/utils/signal_bitfield.h>
++#import <torrent/common.h>
++#import <torrent/utils/signal_bitfield.h>
+
+ namespace torrent {
+
+@@ -54,7 +18,7 @@ public:
+ typedef void* (*pthread_func)(void*);
+ typedef std::function<void ()> slot_void;
+ typedef std::function<uint64_t ()> slot_timer;
+- typedef class signal_bitfield signal_type;
++ typedef class signal_bitfield signal_type;
+
+ enum state_type {
+ STATE_UNKNOWN,
+diff --git a/test/Makefile.am b/test/Makefile.am
+index 8b0291bb..cb00dce3 100644
+--- a/test/Makefile.am
++++ b/test/Makefile.am
+@@ -1,6 +1,10 @@
+ TESTS = \
+ LibTorrent_Test_Torrent_Net \
++ LibTorrent_Test_Torrent_Utils \
++ LibTorrent_Test_Torrent \
++ LibTorrent_Test_Data \
+ LibTorrent_Test_Net \
++ LibTorrent_Test_Tracker \
+ LibTorrent_Test
+
+ check_PROGRAMS = $(TESTS)
+@@ -10,22 +14,14 @@ LibTorrent_Test_LDADD = \
+ ../src/libtorrent_other.la \
+ ../src/torrent/libtorrent_torrent.la
+
+-LibTorrent_Test_Net_LDADD = $(LibTorrent_Test_LDADD)
+ LibTorrent_Test_Torrent_Net_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Torrent_Utils_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Torrent_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Data_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Net_LDADD = $(LibTorrent_Test_LDADD)
++LibTorrent_Test_Tracker_LDADD = $(LibTorrent_Test_LDADD)
+
+-# LibTorrent_Test_SOURCES = \
+-# helpers/expect_fd.h \
+-# helpers/expect_utils.h \
+-# helpers/mock_compare.h \
+-# helpers/mock_function.cc \
+-# helpers/mock_function.h \
+-# helpers/network.h \
+-# helpers/progress_listener.cc \
+-# helpers/progress_listener.h \
+-# helpers/test_fixture.cc \
+-# helpers/test_fixture.h
+-
+-LibTorrent_Test_Torrent_Net_SOURCES = \
++LibTorrent_Test_Common = \
+ main.cc \
+ helpers/expect_fd.h \
+ helpers/expect_utils.h \
+@@ -37,7 +33,10 @@ LibTorrent_Test_Torrent_Net_SOURCES = \
+ helpers/progress_listener.h \
+ helpers/test_fixture.cc \
+ helpers/test_fixture.h \
+- \
++ helpers/test_thread.cc \
++ helpers/test_thread.h
++
++LibTorrent_Test_Torrent_Net_SOURCES = $(LibTorrent_Test_Common) \
+ torrent/net/test_address_info.cc \
+ torrent/net/test_address_info.h \
+ torrent/net/test_fd.cc \
+@@ -45,71 +44,28 @@ LibTorrent_Test_Torrent_Net_SOURCES = \
+ torrent/net/test_socket_address.cc \
+ torrent/net/test_socket_address.h
+
+-LibTorrent_Test_Net_SOURCES = \
+- main.cc \
+- helpers/expect_fd.h \
+- helpers/expect_utils.h \
+- helpers/mock_compare.h \
+- helpers/mock_function.cc \
+- helpers/mock_function.h \
+- helpers/network.h \
+- helpers/progress_listener.cc \
+- helpers/progress_listener.h \
+- helpers/test_fixture.cc \
+- helpers/test_fixture.h \
+- \
+- net/test_socket_listen.cc \
+- net/test_socket_listen.h
+-
+-LibTorrent_Test_SOURCES = \
+- main.cc \
+- helpers/expect_fd.h \
+- helpers/expect_utils.h \
+- helpers/mock_compare.h \
+- helpers/mock_function.cc \
+- helpers/mock_function.h \
+- helpers/network.h \
+- helpers/progress_listener.cc \
+- helpers/progress_listener.h \
+- helpers/test_fixture.cc \
+- helpers/test_fixture.h \
+- \
+- ../src/thread_disk.cc \
+- ../src/thread_disk.h \
+- \
+- rak/allocators_test.cc \
+- rak/allocators_test.h \
+- rak/ranges_test.cc \
+- rak/ranges_test.h \
+- data/chunk_list_test.cc \
+- data/chunk_list_test.h \
+- data/hash_check_queue_test.cc \
+- data/hash_check_queue_test.h \
+- data/hash_queue_test.cc \
+- data/hash_queue_test.h \
+- \
+- protocol/test_request_list.cc \
+- protocol/test_request_list.h \
+- \
+- torrent/utils/log_test.cc \
+- torrent/utils/log_test.h \
+- torrent/utils/option_strings_test.cc \
+- torrent/utils/option_strings_test.h \
++LibTorrent_Test_Torrent_Utils_SOURCES = $(LibTorrent_Test_Common) \
+ torrent/utils/test_extents.cc \
+ torrent/utils/test_extents.h \
++ torrent/utils/test_log.cc \
++ torrent/utils/test_log.h \
+ torrent/utils/test_log_buffer.cc \
+ torrent/utils/test_log_buffer.h \
++ torrent/utils/test_option_strings.cc \
++ torrent/utils/test_option_strings.h \
+ torrent/utils/test_queue_buckets.cc \
+ torrent/utils/test_queue_buckets.h \
++ torrent/utils/test_signal_bitfield.cc \
++ torrent/utils/test_signal_bitfield.h \
++ torrent/utils/test_thread_base.cc \
++ torrent/utils/test_thread_base.h \
+ torrent/utils/test_uri_parser.cc \
+- torrent/utils/test_uri_parser.h \
+- torrent/utils/signal_bitfield_test.cc \
+- torrent/utils/signal_bitfield_test.h \
+- torrent/utils/thread_base_test.cc \
+- torrent/utils/thread_base_test.h \
++ torrent/utils/test_uri_parser.h
++
++LibTorrent_Test_Torrent_SOURCES = $(LibTorrent_Test_Common) \
++ torrent/test_http.cc \
++ torrent/test_http.h \
+ \
+- torrent/http_test.cc \
+- torrent/http_test.h \
+ torrent/object_test.cc \
+ torrent/object_test.h \
+ torrent/object_test_utils.cc \
+@@ -129,15 +85,49 @@ LibTorrent_Test_SOURCES = \
+ torrent/tracker_list_features_test.cc \
+ torrent/tracker_list_features_test.h \
+ torrent/tracker_timeout_test.cc \
+- torrent/tracker_timeout_test.h \
++ torrent/tracker_timeout_test.h
++
++LibTorrent_Test_Data_SOURCES = $(LibTorrent_Test_Common) \
++ data/test_chunk_list.cc \
++ data/test_chunk_list.h \
++ data/test_hash_check_queue.cc \
++ data/test_hash_check_queue.h \
++ data/test_hash_queue.cc \
++ data/test_hash_queue.h
++
++LibTorrent_Test_Net_SOURCES = $(LibTorrent_Test_Common) \
++ net/test_socket_listen.cc \
++ net/test_socket_listen.h
++
++LibTorrent_Test_Tracker_SOURCES = $(LibTorrent_Test_Common) \
++ tracker/test_tracker_http.cc \
++ tracker/test_tracker_http.h
++
++LibTorrent_Test_SOURCES = $(LibTorrent_Test_Common) \
++ \
++ ../src/thread_disk.cc \
++ ../src/thread_disk.h \
+ \
+- tracker/tracker_http_test.cc \
+- tracker/tracker_http_test.h
++ rak/allocators_test.cc \
++ rak/allocators_test.h \
++ rak/ranges_test.cc \
++ rak/ranges_test.h \
++ \
++ protocol/test_request_list.cc \
++ protocol/test_request_list.h
+
+ LibTorrent_Test_Torrent_Net_CXXFLAGS = $(CPPUNIT_CFLAGS)
+ LibTorrent_Test_Torrent_Net_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Torrent_Utils_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Torrent_Utils_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Torrent_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Torrent_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Data_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Data_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+ LibTorrent_Test_Net_CXXFLAGS = $(CPPUNIT_CFLAGS)
+ LibTorrent_Test_Net_LDFLAGS = $(CPPUNIT_LIBS) -ldl
++LibTorrent_Test_Tracker_CXXFLAGS = $(CPPUNIT_CFLAGS)
++LibTorrent_Test_Tracker_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+ LibTorrent_Test_CXXFLAGS = $(CPPUNIT_CFLAGS)
+ LibTorrent_Test_LDFLAGS = $(CPPUNIT_LIBS) -ldl
+
+diff --git a/test/data/chunk_list_test.cc b/test/data/test_chunk_list.cc
+similarity index 93%
+rename from test/data/chunk_list_test.cc
+rename to test/data/test_chunk_list.cc
+index 28647db2..18de597e 100644
+--- a/test/data/chunk_list_test.cc
++++ b/test/data/test_chunk_list.cc
+@@ -1,11 +1,11 @@
+-#include "config.h"
++#import "config.h"
+
+-#include "chunk_list_test.h"
++#import "test_chunk_list.h"
+
+-#include "torrent/chunk_manager.h"
+-#include "torrent/exceptions.h"
++#import "torrent/chunk_manager.h"
++#import "torrent/exceptions.h"
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(ChunkListTest);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_chunk_list, "data");
+
+ torrent::Chunk*
+ func_create_chunk(uint32_t index, int prot_flags) {
+@@ -36,7 +36,7 @@ func_storage_error(torrent::ChunkList* chunk_list, const std::string& message) {
+ }
+
+ void
+-ChunkListTest::test_basic() {
++test_chunk_list::test_basic() {
+ torrent::ChunkManager chunk_manager;
+ torrent::ChunkList chunk_list;
+
+@@ -55,7 +55,7 @@ ChunkListTest::test_basic() {
+ }
+
+ void
+-ChunkListTest::test_get_release() {
++test_chunk_list::test_get_release() {
+ SETUP_CHUNK_LIST();
+
+ CPPUNIT_ASSERT(!(*chunk_list)[0].is_valid());
+@@ -112,7 +112,7 @@ ChunkListTest::test_get_release() {
+
+ // Make sure we can't go into writable when blocking, etc.
+ void
+-ChunkListTest::test_blocking() {
++test_chunk_list::test_blocking() {
+ SETUP_CHUNK_LIST();
+
+ torrent::ChunkHandle handle_0_ro = chunk_list->get(0, torrent::ChunkList::get_blocking);
+diff --git a/test/data/chunk_list_test.h b/test/data/test_chunk_list.h
+similarity index 88%
+rename from test/data/chunk_list_test.h
+rename to test/data/test_chunk_list.h
+index 3979982f..85d1f77b 100644
+--- a/test/data/chunk_list_test.h
++++ b/test/data/test_chunk_list.h
+@@ -1,23 +1,22 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#import "helpers/test_fixture.h"
+
+-#include "data/chunk_list.h"
++class test_chunk_list : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_chunk_list);
+
+-class ChunkListTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(ChunkListTest);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_get_release);
+ CPPUNIT_TEST(test_blocking);
++
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp() {}
+- void tearDown() {}
+-
+ void test_basic();
+ void test_get_release();
+ void test_blocking();
+ };
+
++#include "data/chunk_list.h"
++
+ torrent::Chunk* func_create_chunk(uint32_t index, int prot_flags);
+ uint64_t func_free_diskspace(torrent::ChunkList* chunk_list);
+ void func_storage_error(torrent::ChunkList* chunk_list, const std::string& message);
+diff --git a/test/data/hash_check_queue_test.cc b/test/data/test_hash_check_queue.cc
+similarity index 92%
+rename from test/data/hash_check_queue_test.cc
+rename to test/data/test_hash_check_queue.cc
+index 4b15245e..65931273 100644
+--- a/test/data/hash_check_queue_test.cc
++++ b/test/data/test_hash_check_queue.cc
+@@ -1,20 +1,23 @@
+ #include "config.h"
+
++#include "test_hash_check_queue.h"
++
++#include "helpers/test_thread.h"
++#include "helpers/test_utils.h"
++
+ #include <functional>
+ #include <signal.h>
+
+-#include "data/hash_queue_node.h"
++#include "data/chunk_handle.h"
+ #include "utils/sha1.h"
+ #include "torrent/chunk_manager.h"
+ #include "torrent/exceptions.h"
+ #include "torrent/poll_select.h"
+-#include "torrent/utils/thread_base_test.h"
+ #include "thread_disk.h"
+
+-#include "chunk_list_test.h"
+-#include "hash_check_queue_test.h"
++#include "test_chunk_list.h"
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(HashCheckQueueTest);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_hash_check_queue, "data");
+
+ pthread_mutex_t done_chunks_lock = PTHREAD_MUTEX_INITIALIZER;
+
+@@ -68,22 +71,16 @@ static torrent::Poll* create_select_poll() { return torrent::PollSelect::create(
+ static void do_nothing() {}
+
+ void
+-HashCheckQueueTest::setUp() {
++test_hash_check_queue::setUp() {
++ test_fixture::setUp();
++
+ torrent::Poll::slot_create_poll() = std::bind(&create_select_poll);
+
+ signal(SIGUSR1, (sig_t)&do_nothing);
+ }
+
+ void
+-HashCheckQueueTest::tearDown() {
+-}
+-
+-void
+-HashCheckQueueTest::test_basic() {
+-}
+-
+-void
+-HashCheckQueueTest::test_single() {
++test_hash_check_queue::test_single() {
+ SETUP_CHUNK_LIST();
+ torrent::HashCheckQueue hash_queue;
+
+@@ -110,7 +107,7 @@ HashCheckQueueTest::test_single() {
+ }
+
+ void
+-HashCheckQueueTest::test_multiple() {
++test_hash_check_queue::test_multiple() {
+ SETUP_CHUNK_LIST();
+ torrent::HashCheckQueue hash_queue;
+
+@@ -143,7 +140,7 @@ HashCheckQueueTest::test_multiple() {
+ }
+
+ void
+-HashCheckQueueTest::test_erase() {
++test_hash_check_queue::test_erase() {
+ // SETUP_CHUNK_LIST();
+ // torrent::HashCheckQueue hash_queue;
+
+@@ -176,7 +173,7 @@ HashCheckQueueTest::test_erase() {
+ }
+
+ void
+-HashCheckQueueTest::test_thread() {
++test_hash_check_queue::test_thread() {
+ SETUP_CHUNK_LIST();
+ SETUP_THREAD();
+ thread_disk->start_thread();
+diff --git a/test/data/hash_check_queue_test.h b/test/data/test_hash_check_queue.h
+similarity index 63%
+rename from test/data/hash_check_queue_test.h
+rename to test/data/test_hash_check_queue.h
+index 5398a50d..d2d271bb 100644
+--- a/test/data/hash_check_queue_test.h
++++ b/test/data/test_hash_check_queue.h
+@@ -1,26 +1,19 @@
+-#include <map>
+-#include <vector>
+-#include <cppunit/extensions/HelperMacros.h>
++#import "helpers/test_fixture.h"
+
+-#include "data/hash_check_queue.h"
+-#include "torrent/hash_string.h"
++class test_hash_check_queue : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_hash_check_queue);
+
+-
+-class HashCheckQueueTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(HashCheckQueueTest);
+- CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_single);
+ CPPUNIT_TEST(test_multiple);
+ CPPUNIT_TEST(test_erase);
+
+ CPPUNIT_TEST(test_thread);
++
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp();
+- void tearDown();
+
+- void test_basic();
+ void test_single();
+ void test_multiple();
+ void test_erase();
+@@ -28,6 +21,13 @@ public:
+ void test_thread();
+ };
+
++#import <map>
++#import <vector>
++
++#import "data/hash_queue_node.h"
++#import "data/hash_check_queue.h"
++#import "torrent/hash_string.h"
++
+ typedef std::map<int, torrent::HashString> done_chunks_type;
+ typedef std::vector<torrent::ChunkHandle> handle_list;
+
+diff --git a/test/data/hash_queue_test.cc b/test/data/test_hash_queue.cc
+similarity index 82%
+rename from test/data/hash_queue_test.cc
+rename to test/data/test_hash_queue.cc
+index d7ce3ba8..d9a88c8d 100644
+--- a/test/data/hash_queue_test.cc
++++ b/test/data/test_hash_queue.cc
+@@ -1,22 +1,26 @@
+-#include "config.h"
++#import "config.h"
+
+-#include <functional>
+-#include <signal.h>
++#import "test_hash_queue.h"
+
+-#include "data/hash_queue_node.h"
+-#include "torrent/chunk_manager.h"
+-#include "torrent/exceptions.h"
+-#include "torrent/hash_string.h"
+-#include "torrent/poll_select.h"
+-#include "torrent/utils/thread_base_test.h"
+-#include "globals.h"
+-#include "thread_disk.h"
++#import "helpers/test_thread.h"
++#import "helpers/test_utils.h"
+
+-#include "chunk_list_test.h"
+-#include "hash_queue_test.h"
+-#include "hash_check_queue_test.h"
++#import <functional>
++#import <signal.h>
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(HashQueueTest);
++#import "data/hash_queue.h"
++#import "data/hash_queue_node.h"
++#import "torrent/chunk_manager.h"
++#import "torrent/exceptions.h"
++#import "torrent/hash_string.h"
++#import "torrent/poll_select.h"
++#import "globals.h"
++#import "thread_disk.h"
++
++#import "test_chunk_list.h"
++#import "test_hash_check_queue.h"
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_hash_queue, "data");
+
+ typedef std::map<int, torrent::HashString> done_chunks_type;
+
+@@ -39,7 +43,9 @@ static torrent::Poll* create_select_poll() { return torrent::PollSelect::create(
+ static void do_nothing() {}
+
+ void
+-HashQueueTest::setUp() {
++test_hash_queue::setUp() {
++ test_fixture::setUp();
++
+ CPPUNIT_ASSERT(torrent::taskScheduler.empty());
+
+ torrent::Poll::slot_create_poll() = std::bind(&create_select_poll);
+@@ -47,25 +53,9 @@ HashQueueTest::setUp() {
+ }
+
+ void
+-HashQueueTest::tearDown() {
++test_hash_queue::tearDown() {
+ torrent::taskScheduler.clear();
+-}
+-
+-void
+-HashQueueTest::test_basic() {
+- // SETUP_CHUNK_LIST();
+- // SETUP_THREAD();
+- // thread_disk->start_thread();
+-
+- // torrent::HashQueue* hash_queue = new torrent::HashQueue(thread_disk);
+-
+- // // Do stuff?
+-
+- // delete hash_queue;
+-
+- // thread_disk->stop_thread();
+- // CLEANUP_THREAD();
+- // CLEANUP_CHUNK_LIST();
++ test_fixture::tearDown();
+ }
+
+ static void
+@@ -73,7 +63,7 @@ fill_queue() {
+ }
+
+ void
+-HashQueueTest::test_single() {
++test_hash_queue::test_single() {
+ SETUP_CHUNK_LIST();
+ SETUP_THREAD();
+ thread_disk->start_thread();
+@@ -105,7 +95,7 @@ HashQueueTest::test_single() {
+ }
+
+ void
+-HashQueueTest::test_multiple() {
++test_hash_queue::test_multiple() {
+ SETUP_CHUNK_LIST();
+ SETUP_THREAD();
+ thread_disk->start_thread();
+@@ -137,7 +127,7 @@ HashQueueTest::test_multiple() {
+ }
+
+ void
+-HashQueueTest::test_erase() {
++test_hash_queue::test_erase() {
+ SETUP_CHUNK_LIST();
+ SETUP_THREAD();
+
+@@ -164,7 +154,7 @@ HashQueueTest::test_erase() {
+ }
+
+ void
+-HashQueueTest::test_erase_stress() {
++test_hash_queue::test_erase_stress() {
+ SETUP_CHUNK_LIST();
+ SETUP_THREAD();
+ thread_disk->start_thread();
+diff --git a/test/data/hash_queue_test.h b/test/data/test_hash_queue.h
+similarity index 58%
+rename from test/data/hash_queue_test.h
+rename to test/data/test_hash_queue.h
+index cb5b7282..79914484 100644
+--- a/test/data/hash_queue_test.h
++++ b/test/data/test_hash_queue.h
+@@ -1,10 +1,7 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#include "helpers/test_fixture.h"
+
+-#include "data/hash_queue.h"
+-
+-class HashQueueTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(HashQueueTest);
+- CPPUNIT_TEST(test_basic);
++class test_hash_queue : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_hash_queue);
+
+ CPPUNIT_TEST(test_single);
+ CPPUNIT_TEST(test_multiple);
+@@ -17,8 +14,6 @@ public:
+ void setUp();
+ void tearDown();
+
+- void test_basic();
+-
+ void test_single();
+ void test_multiple();
+ void test_erase();
+diff --git a/test/helpers/progress_listener.cc b/test/helpers/progress_listener.cc
+index c2b60bcd..7a6ed047 100644
+--- a/test/helpers/progress_listener.cc
++++ b/test/helpers/progress_listener.cc
+@@ -36,10 +36,10 @@ progress_listener::addFailure(const CppUnit::TestFailure &failure) {
+ if (m_current_log_buffer == nullptr)
+ return;
+
+- std::cout << " : " << (failure.isError() ? "error" : "assertion");
++ std::cout << " : " << (failure.isError() ? "error" : "assertion") << std::flush;
+
+ m_last_test_failed = true;
+- m_failures.push_back(std::move(failure_type{failure.failedTestName(), std::move(m_current_log_buffer)}));
++ m_failures.push_back(failure_type{ failure.failedTestName(), std::move(m_current_log_buffer) });
+ }
+
+ void
+diff --git a/test/helpers/test_fixture.h b/test/helpers/test_fixture.h
+index 312d5009..aa557a5e 100644
+--- a/test/helpers/test_fixture.h
++++ b/test/helpers/test_fixture.h
+@@ -1,14 +1,14 @@
+-#ifndef LIBTORRENT_HELPER_TEST_FIXTURE_H
+-#define LIBTORRENT_HELPER_TEST_FIXTURE_H
+-
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "helpers/mock_function.h"
+-
+-class test_fixture : public CppUnit::TestFixture {
+-public:
+- void setUp();
+- void tearDown();
+-};
+-
+-#endif
++#ifndef LIBTORRENT_HELPER_TEST_FIXTURE_H
++#define LIBTORRENT_HELPER_TEST_FIXTURE_H
++
++#include <cppunit/extensions/HelperMacros.h>
++
++#include "helpers/mock_function.h"
++
++class test_fixture : public CppUnit::TestFixture {
++public:
++ void setUp();
++ void tearDown();
++};
++
++#endif
+diff --git a/test/helpers/test_thread.cc b/test/helpers/test_thread.cc
+new file mode 100755
+index 00000000..4b3d4c95
+--- /dev/null
++++ b/test/helpers/test_thread.cc
+@@ -0,0 +1,71 @@
++#import "config.h"
++
++#import "test_thread.h"
++
++#import <unistd.h>
++#import <cppunit/extensions/HelperMacros.h>
++
++#import "thread_disk.h"
++#import "torrent/exceptions.h"
++#import "torrent/poll_select.h"
++
++const int test_thread::test_flag_pre_stop;
++const int test_thread::test_flag_long_timeout;
++
++const int test_thread::test_flag_acquire_global;
++const int test_thread::test_flag_has_global;
++
++const int test_thread::test_flag_do_work;
++const int test_thread::test_flag_pre_poke;
++const int test_thread::test_flag_post_poke;
++
++test_thread::test_thread() :
++ m_test_state(TEST_NONE),
++ m_test_flags(0) {
++}
++
++void
++test_thread::init_thread() {
++ m_state = STATE_INITIALIZED;
++ m_test_state = TEST_PRE_START;
++ m_poll = torrent::PollSelect::create(256);
++}
++
++void
++test_thread::call_events() {
++ if ((m_test_flags & test_flag_pre_stop) && m_test_state == TEST_PRE_START && m_state == STATE_ACTIVE)
++ __sync_lock_test_and_set(&m_test_state, TEST_PRE_STOP);
++
++ if ((m_test_flags & test_flag_acquire_global)) {
++ acquire_global_lock();
++ __sync_and_and_fetch(&m_test_flags, ~test_flag_acquire_global);
++ __sync_or_and_fetch(&m_test_flags, test_flag_has_global);
++ }
++
++ if ((m_flags & flag_do_shutdown)) {
++ if ((m_flags & flag_did_shutdown))
++ throw torrent::internal_error("Already trigged shutdown.");
++
++ __sync_or_and_fetch(&m_flags, flag_did_shutdown);
++ throw torrent::shutdown_exception();
++ }
++
++ if ((m_test_flags & test_flag_pre_poke)) {
++ }
++
++ if ((m_test_flags & test_flag_do_work)) {
++ usleep(10 * 1000); // TODO: Don't just sleep, as that give up core.
++ __sync_and_and_fetch(&m_test_flags, ~test_flag_do_work);
++ }
++
++ if ((m_test_flags & test_flag_post_poke)) {
++ }
++}
++
++thread_management_type::thread_management_type() {
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++}
++
++thread_management_type::~thread_management_type() {
++ torrent::thread_base::release_global_lock();
++}
+diff --git a/test/helpers/test_thread.h b/test/helpers/test_thread.h
+new file mode 100755
+index 00000000..52037036
+--- /dev/null
++++ b/test/helpers/test_thread.h
+@@ -0,0 +1,59 @@
++#import "torrent/utils/thread_base.h"
++
++class test_thread : public torrent::thread_base {
++public:
++ enum test_state {
++ TEST_NONE,
++ TEST_PRE_START,
++ TEST_PRE_STOP,
++ TEST_STOP
++ };
++
++ static const int test_flag_pre_stop = 0x1;
++ static const int test_flag_long_timeout = 0x2;
++
++ static const int test_flag_acquire_global = 0x10;
++ static const int test_flag_has_global = 0x20;
++
++ static const int test_flag_do_work = 0x100;
++ static const int test_flag_pre_poke = 0x200;
++ static const int test_flag_post_poke = 0x400;
++
++ test_thread();
++
++ int test_state() const { return m_test_state; }
++ bool is_state(int state) const { return m_state == state; }
++ bool is_test_state(int state) const { return m_test_state == state; }
++ bool is_test_flags(int flags) const { return (m_test_flags & flags) == flags; }
++ bool is_not_test_flags(int flags) const { return !(m_test_flags & flags); }
++
++ auto name() const -> const char* { return "test_thread"; }
++
++ void init_thread();
++
++ void set_pre_stop() { __sync_or_and_fetch(&m_test_flags, test_flag_pre_stop); }
++ void set_acquire_global() { __sync_or_and_fetch(&m_test_flags, test_flag_acquire_global); }
++
++ void set_test_flag(int flags) { __sync_or_and_fetch(&m_test_flags, flags); }
++
++private:
++ void call_events();
++ int64_t next_timeout_usec() { return (m_test_flags & test_flag_long_timeout) ? (10000 * 1000) : (100 * 1000); }
++
++ int m_test_state lt_cacheline_aligned;
++ int m_test_flags lt_cacheline_aligned;
++};
++
++struct thread_management_type {
++ thread_management_type();
++ ~thread_management_type();
++};
++
++#define SETUP_THREAD() \
++ thread_management_type thread_management; \
++ torrent::thread_disk* thread_disk = new torrent::thread_disk(); \
++ thread_disk->init_thread();
++
++#define CLEANUP_THREAD() \
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&torrent::thread_base::is_inactive, thread_disk))); \
++ delete thread_disk;
+diff --git a/test/helpers/test_utils.h b/test/helpers/test_utils.h
+new file mode 100644
+index 00000000..0c8c6b7a
+--- /dev/null
++++ b/test/helpers/test_utils.h
+@@ -0,0 +1,16 @@
++#include <functional>
++#include <unistd.h>
++
++inline bool
++wait_for_true(std::function<bool ()> test_function) {
++ int i = 100;
++
++ do {
++ if (test_function())
++ return true;
++
++ usleep(10 * 1000);
++ } while (--i);
++
++ return false;
++}
+diff --git a/test/helpers/utils.h b/test/helpers/utils.h
+index d18450c1..e81d22eb 100644
+--- a/test/helpers/utils.h
++++ b/test/helpers/utils.h
+@@ -1,60 +1,60 @@
+-#ifndef LIBTORRENT_HELPER_UTILS_H
+-#define LIBTORRENT_HELPER_UTILS_H
+-
+-#include <algorithm>
+-#include <iostream>
+-#include <cppunit/extensions/TestFactoryRegistry.h>
+-#include <torrent/utils/log.h>
+-
+-static void
+-dump_failure_log(const failure_type& failure) {
+- if (failure.log->empty())
+- return;
+-
+- std::cout << std::endl << failure.name << std::endl;
+-
+- // Doesn't print dump messages as log_buffer drops them.
+- std::for_each(failure.log->begin(), failure.log->end(), [](const torrent::log_entry& entry) {
+- std::cout << entry.timestamp << ' ' << entry.message << '\n';
+- });
+-
+- std::cout << std::flush;
+-}
+-
+-static void
+-dump_failures(const failure_list_type& failures) {
+- if (failures.empty())
+- return;
+-
+- std::cout << std::endl
+- << "=================" << std::endl
+- << "Failed Test Logs:" << std::endl
+- << "=================" << std::endl;
+-
+- std::for_each(failures.begin(), failures.end(), [](const failure_type& failure) {
+- dump_failure_log(failure);
+- });
+- std::cout << std::endl;
+-}
+-
+-static
+-void add_tests(CppUnit::TextUi::TestRunner& runner, const char* c_test_names) {
+- if (c_test_names == NULL || std::string(c_test_names).empty()) {
+- runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
+- return;
+- }
+-
+- const std::string& test_names(c_test_names);
+-
+- size_t pos = 0;
+- size_t next = 0;
+-
+- while ((next = test_names.find(',', pos)) < test_names.size()) {
+- runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos, next - pos)).makeTest());
+- pos = next + 1;
+- }
+-
+- runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos)).makeTest());
+-}
+-
+-#endif
++#ifndef LIBTORRENT_HELPER_UTILS_H
++#define LIBTORRENT_HELPER_UTILS_H
++
++#include <algorithm>
++#include <iostream>
++#include <cppunit/extensions/TestFactoryRegistry.h>
++#include <torrent/utils/log.h>
++
++static void
++dump_failure_log(const failure_type& failure) {
++ if (failure.log->empty())
++ return;
++
++ std::cout << std::endl << failure.name << std::endl;
++
++ // Doesn't print dump messages as log_buffer drops them.
++ std::for_each(failure.log->begin(), failure.log->end(), [](const torrent::log_entry& entry) {
++ std::cout << entry.timestamp << ' ' << entry.message << '\n';
++ });
++
++ std::cout << std::flush;
++}
++
++static void
++dump_failures(const failure_list_type& failures) {
++ if (failures.empty())
++ return;
++
++ std::cout << std::endl
++ << "=================" << std::endl
++ << "Failed Test Logs:" << std::endl
++ << "=================" << std::endl;
++
++ std::for_each(failures.begin(), failures.end(), [](const failure_type& failure) {
++ dump_failure_log(failure);
++ });
++ std::cout << std::endl;
++}
++
++static
++void add_tests(CppUnit::TextUi::TestRunner& runner, const char* c_test_names) {
++ if (c_test_names == NULL || std::string(c_test_names).empty()) {
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
++ return;
++ }
++
++ const std::string& test_names(c_test_names);
++
++ size_t pos = 0;
++ size_t next = 0;
++
++ while ((next = test_names.find(',', pos)) < test_names.size()) {
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos, next - pos)).makeTest());
++ pos = next + 1;
++ }
++
++ runner.addTest(CppUnit::TestFactoryRegistry::getRegistry(test_names.substr(pos)).makeTest());
++}
++
++#endif
+diff --git a/test/main.cc b/test/main.cc
+index e8a00e1f..57ae31a2 100644
+--- a/test/main.cc
++++ b/test/main.cc
+@@ -19,9 +19,11 @@
+ #include "helpers/progress_listener.h"
+ #include "helpers/utils.h"
+
+-CPPUNIT_REGISTRY_ADD_TO_DEFAULT("net");
+ CPPUNIT_REGISTRY_ADD_TO_DEFAULT("torrent/net");
+ CPPUNIT_REGISTRY_ADD_TO_DEFAULT("torrent/utils");
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("torrent");
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("net");
++CPPUNIT_REGISTRY_ADD_TO_DEFAULT("tracker");
+
+ void
+ do_test_panic(int signum) {
+diff --git a/test/torrent/http_test.cc b/test/torrent/test_http.cc
+similarity index 94%
+rename from test/torrent/http_test.cc
+rename to test/torrent/test_http.cc
+index 27e04552..24ec97b5 100644
+--- a/test/torrent/http_test.cc
++++ b/test/torrent/test_http.cc
+@@ -1,10 +1,11 @@
+ #include "config.h"
+
+-#include <sstream>
++#include "test_http.h"
+
+-#include "http_test.h"
++#include <sstream>
++#include "torrent/http.h"
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(HttpTest);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_http, "torrent");
+
+ #define HTTP_SETUP() \
+ bool http_destroyed = false; \
+@@ -72,7 +73,7 @@ TestHttp* create_test_http() { return new TestHttp; }
+ static void increment_value(int* value) { (*value)++; }
+
+ void
+-HttpTest::test_basic() {
++test_http::test_basic() {
+ torrent::Http::slot_factory() = std::bind(&create_test_http);
+
+ torrent::Http* http = torrent::Http::slot_factory()();
+@@ -94,7 +95,7 @@ HttpTest::test_basic() {
+ }
+
+ void
+-HttpTest::test_done() {
++test_http::test_done() {
+ HTTP_SETUP();
+ http->start();
+
+@@ -106,7 +107,7 @@ HttpTest::test_done() {
+ }
+
+ void
+-HttpTest::test_failure() {
++test_http::test_failure() {
+ HTTP_SETUP();
+ http->start();
+
+@@ -118,7 +119,7 @@ HttpTest::test_failure() {
+ }
+
+ void
+-HttpTest::test_delete_on_done() {
++test_http::test_delete_on_done() {
+ HTTP_SETUP();
+ http->start();
+ http->set_delete_stream();
+@@ -145,7 +146,7 @@ HttpTest::test_delete_on_done() {
+ }
+
+ void
+-HttpTest::test_delete_on_failure() {
++test_http::test_delete_on_failure() {
+ HTTP_SETUP();
+ http->start();
+ http->set_delete_stream();
+diff --git a/test/torrent/http_test.h b/test/torrent/test_http.h
+similarity index 63%
+rename from test/torrent/http_test.h
+rename to test/torrent/test_http.h
+index c6c97d08..f4334646 100644
+--- a/test/torrent/http_test.h
++++ b/test/torrent/test_http.h
+@@ -1,21 +1,18 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#import "helpers/test_fixture.h"
+
+-#include "torrent/http.h"
++class test_http : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_http);
+
+-class HttpTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(HttpTest);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_done);
+ CPPUNIT_TEST(test_failure);
+
+ CPPUNIT_TEST(test_delete_on_done);
+ CPPUNIT_TEST(test_delete_on_failure);
++
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp() {}
+- void tearDown() {}
+-
+ void test_basic();
+ void test_done();
+ void test_failure();
+diff --git a/test/torrent/utils/option_strings_test.h b/test/torrent/utils/option_strings_test.h
+deleted file mode 100644
+index 55df4f19..00000000
+--- a/test/torrent/utils/option_strings_test.h
++++ /dev/null
+@@ -1,17 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "torrent/utils/option_strings.h"
+-
+-class option_strings_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(option_strings_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST(test_entries);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp() {}
+- void tearDown() {}
+-
+- void test_basic();
+- void test_entries();
+-};
+diff --git a/test/torrent/utils/signal_bitfield_test.h b/test/torrent/utils/signal_bitfield_test.h
+deleted file mode 100644
+index 4590de41..00000000
+--- a/test/torrent/utils/signal_bitfield_test.h
++++ /dev/null
+@@ -1,23 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "torrent/utils/signal_bitfield.h"
+-
+-class utils_signal_bitfield_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(utils_signal_bitfield_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST(test_single);
+- CPPUNIT_TEST(test_multiple);
+-
+- CPPUNIT_TEST(test_thread);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp();
+- void tearDown();
+-
+- void test_basic();
+- void test_single();
+- void test_multiple();
+-
+- void test_thread();
+-};
+diff --git a/test/torrent/utils/test_extents.cc b/test/torrent/utils/test_extents.cc
+index 87424d62..8e614e10 100644
+--- a/test/torrent/utils/test_extents.cc
++++ b/test/torrent/utils/test_extents.cc
+@@ -2,34 +2,25 @@
+
+ #include "test_extents.h"
+
+-#include <cinttypes>
+-#include <iostream>
+ #include <torrent/utils/extents.h>
++#include <torrent/utils/log.h>
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(ExtentsTest);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_extents, "torrent/utils");
+
+-void
+-ExtentsTest::setUp() {
+-}
++#define TEST_EXTENT_BEGIN(name) \
++ lt_log_print(torrent::LOG_MOCK_CALLS, "extent: %s", name);
+
+-void
+-ExtentsTest::tearDown() {
+-}
+-
+-//typedef torrent::extents<uint32_t, int, 8, 16, 4> extent_type_1;
+ typedef torrent::extents<uint32_t, int> extent_type_1;
+
+-// typedef torrent::extents<uint32_t, int, 0, 256, 16> extent_type_3;
+-/*
+ template <typename Extent>
+ bool
+ verify_extent_data(Extent& extent, const uint32_t* idx, const int* val) {
+ while (*idx != *(idx + 1)) {
+- if (!extent.is_equal_range(*idx, *(idx + 1) - 1, *val)) {
+- // std::cout << *idx << ' ' << *(idx + 1) << ' ' << *val << std::endl;
+- // std::cout << extent.at(*idx) << std::endl;
+- // std::cout << extent.at(*(idx + 1)) << std::endl;
+- return false;
++ for (auto i = *idx; i != *(idx + 1); i++) {
++ lt_log_print(torrent::LOG_MOCK_CALLS, "extent: at %u", i);
++
++ if (extent.at(i) != *val)
++ return false;
+ }
+
+ idx++;
+@@ -40,34 +31,32 @@ verify_extent_data(Extent& extent, const uint32_t* idx, const int* val) {
+ }
+
+ static const uint32_t idx_empty[] = {0, 256, 256};
+-static const int val_empty[] = {0, 1};
++static const int val_empty[] = {0};
+
+ static const uint32_t idx_basic_1[] = {0, 1, 255, 256, 256};
+ static const int val_basic_1[] = {1, 0, 1};
+
+-// static const uint32_t idx_basic_2[] = {0, 1, 16, 255, 256, 256};
+-// static const int val_basic_2[] = {1, 0, 2, 1};
+-*/
+ void
+-ExtentsTest::test_basic() {
++test_extents::test_basic() {
+ extent_type_1 extent_1;
+-/*
+- // Test empty.
+- CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_empty, val_empty));
+-
+- CPPUNIT_ASSERT(extent_1.at(0) == int());
+- CPPUNIT_ASSERT(extent_1.at(255) == int());
++ extent_1.insert(0, 255, int());
+
+- extent_1.insert(0, 0, 1);
+- extent_1.insert(255, 0, 1);
++ { TEST_EXTENT_BEGIN("empty");
++ CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_empty, val_empty));
+
+- CPPUNIT_ASSERT(extent_1.at(0) == 1);
+- CPPUNIT_ASSERT(extent_1.at(255) == 1);
++ CPPUNIT_ASSERT(extent_1.at(0) == int());
++ CPPUNIT_ASSERT(extent_1.at(255) == int());
++ };
++ { TEST_EXTENT_BEGIN("borders");
+
+- CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_1, val_basic_1));
++ extent_1.insert(0, 0, 1);
++ extent_1.insert(255, 255, 1);
++ // This step shouldn't be needed.
++ extent_1.insert(1, 254, int());
+
+- // extent_1.insert(38, 3, 2);
++ CPPUNIT_ASSERT(extent_1.at(0) == 1);
++ CPPUNIT_ASSERT(extent_1.at(255) == 1);
+
+- // CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_2, val_basic_2));
+-*/
++ CPPUNIT_ASSERT(verify_extent_data(extent_1, idx_basic_1, val_basic_1));
++ };
+ }
+diff --git a/test/torrent/utils/test_extents.h b/test/torrent/utils/test_extents.h
+index fd790cf8..e187f6a7 100644
+--- a/test/torrent/utils/test_extents.h
++++ b/test/torrent/utils/test_extents.h
+@@ -1,13 +1,10 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#include "helpers/test_fixture.h"
+
+-class ExtentsTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(ExtentsTest);
++class test_extents : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_extents);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp();
+- void tearDown();
+-
+ void test_basic();
+ };
+diff --git a/test/torrent/utils/log_test.cc b/test/torrent/utils/test_log.cc
+similarity index 92%
+rename from test/torrent/utils/log_test.cc
+rename to test/torrent/utils/test_log.cc
+index 8cc00ef8..fec7e505 100644
+--- a/test/torrent/utils/log_test.cc
++++ b/test/torrent/utils/test_log.cc
+@@ -1,5 +1,7 @@
+ #include "config.h"
+
++#include "test_log.h"
++
+ #include <algorithm>
+ #include <cstring>
+ #include <fstream>
+@@ -9,15 +11,13 @@
+ #include <torrent/exceptions.h>
+ #include <torrent/utils/log.h>
+
+-#include "log_test.h"
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_log, "torrent/utils");
+
+ namespace torrent {
+ typedef std::vector<std::pair<std::string, log_slot> > log_output_list;
+ extern log_output_list log_outputs;
+ }
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(utils_log_test);
+-
+ const char* expected_output = NULL;
+ unsigned int output_mask;
+
+@@ -35,19 +35,19 @@ test_output(const char* output, unsigned int length, unsigned int mask) {
+ CPPUNIT_ASSERT(output_mask == (mask));
+
+ void
+-utils_log_test::setUp() {
++test_log::setUp() {
+ // Don't initialize since this creates the group->child connections.
+ // torrent::log_initialize();
+ torrent::log_cleanup();
+ }
+
+ void
+-utils_log_test::tearDown() {
++test_log::tearDown() {
+ torrent::log_cleanup();
+ }
+
+ void
+-utils_log_test::test_basic() {
++test_log::test_basic() {
+ CPPUNIT_ASSERT(!torrent::log_groups.empty());
+ CPPUNIT_ASSERT(torrent::log_groups.size() == torrent::LOG_GROUP_MAX_SIZE);
+
+@@ -61,7 +61,7 @@ open_output(const char* name, int mask = 0) {
+ }
+
+ void
+-utils_log_test::test_output_open() {
++test_log::test_output_open() {
+ CPPUNIT_ASSERT(torrent::log_groups[0].size_outputs() == 0);
+
+ // Add test for unknown output names.
+@@ -92,7 +92,7 @@ utils_log_test::test_output_open() {
+ // on unused log items.
+
+ void
+-utils_log_test::test_print() {
++test_log::test_print() {
+ open_output("test_print_1", 0x1);
+ open_output("test_print_2", 0x2);
+ torrent::log_add_group_output(0, "test_print_1");
+@@ -113,7 +113,7 @@ enum {
+ };
+
+ void
+-utils_log_test::test_children() {
++test_log::test_children() {
+ open_output("test_children_1", 0x1);
+ open_output("test_children_2", 0x2);
+ torrent::log_add_group_output(GROUP_PARENT_1, "test_children_1");
+@@ -136,8 +136,8 @@ utils_log_test::test_children() {
+ }
+
+ void
+-utils_log_test::test_file_output() {
+- std::string filename = "utils_log_test.XXXXXX";
++test_log::test_file_output() {
++ std::string filename = "test_log.XXXXXX";
+
+ mktemp(&*filename.begin());
+
+@@ -159,8 +159,8 @@ utils_log_test::test_file_output() {
+ }
+
+ void
+-utils_log_test::test_file_output_append() {
+- std::string filename = "utils_log_test.XXXXXX";
++test_log::test_file_output_append() {
++ std::string filename = "test_log.XXXXXX";
+
+ mktemp(&*filename.begin());
+
+diff --git a/test/torrent/utils/log_test.h b/test/torrent/utils/test_log.h
+similarity index 71%
+rename from test/torrent/utils/log_test.h
+rename to test/torrent/utils/test_log.h
+index d4cb3bc6..a06c95ae 100644
+--- a/test/torrent/utils/log_test.h
++++ b/test/torrent/utils/test_log.h
+@@ -1,9 +1,7 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#include "helpers/test_fixture.h"
+
+-#include "torrent/utils/log.h"
+-
+-class utils_log_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(utils_log_test);
++class test_log : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_log);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_output_open);
+
+diff --git a/test/torrent/utils/test_log_buffer.cc b/test/torrent/utils/test_log_buffer.cc
+index a56a5365..58412750 100644
+--- a/test/torrent/utils/test_log_buffer.cc
++++ b/test/torrent/utils/test_log_buffer.cc
+@@ -7,18 +7,10 @@
+
+ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_log_buffer, "torrent/utils");
+
+-void
+-test_log_buffer::setUp() {
+- torrent::cachedTime = rak::timer::from_seconds(1000);
+-}
+-
+-void
+-test_log_buffer::tearDown() {
+-}
+-
+ void
+ test_log_buffer::test_basic() {
+ torrent::log_buffer log;
++ torrent::cachedTime = rak::timer::from_seconds(1000);
+
+ log.lock();
+ CPPUNIT_ASSERT(log.empty());
+@@ -46,6 +38,7 @@ test_log_buffer::test_basic() {
+ void
+ test_log_buffer::test_timestamps() {
+ torrent::log_buffer log;
++ torrent::cachedTime = rak::timer::from_seconds(1000);
+
+ log.lock_and_push_log("foobar", 6, 0);
+ CPPUNIT_ASSERT(log.back().timestamp == 1000);
+diff --git a/test/torrent/utils/test_log_buffer.h b/test/torrent/utils/test_log_buffer.h
+index 290df4c1..39c6b879 100644
+--- a/test/torrent/utils/test_log_buffer.h
++++ b/test/torrent/utils/test_log_buffer.h
+@@ -2,16 +2,11 @@
+
+ class test_log_buffer : public test_fixture {
+ CPPUNIT_TEST_SUITE(test_log_buffer);
+-
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_timestamps);
+-
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp();
+- void tearDown();
+-
+ void test_basic();
+ void test_timestamps();
+ };
+diff --git a/test/torrent/utils/option_strings_test.cc b/test/torrent/utils/test_option_strings.cc
+similarity index 65%
+rename from test/torrent/utils/option_strings_test.cc
+rename to test/torrent/utils/test_option_strings.cc
+index a9bdcc89..68da1d2b 100644
+--- a/test/torrent/utils/option_strings_test.cc
++++ b/test/torrent/utils/test_option_strings.cc
+@@ -1,35 +1,22 @@
+ #include "config.h"
+
+-#include <fstream>
+-#include <functional>
+-#include <iostream>
++#include "test_option_strings.h"
+
+-#include <torrent/exceptions.h>
+-#include <torrent/utils/option_strings.h>
+-
+-#include <torrent/connection_manager.h>
+-#include <torrent/object.h>
+ #include <torrent/download.h>
+-#include <torrent/download/choke_group.h>
+-#include <torrent/download/choke_queue.h>
++#include <torrent/utils/option_strings.h>
+ #include <torrent/utils/log.h>
+
+-#include "option_strings_test.h"
+-
+-CPPUNIT_TEST_SUITE_REGISTRATION(option_strings_test);
+-
+-void
+-option_strings_test::test_basic() {
+-
+-}
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_option_strings, "torrent/utils");
+
+ #define TEST_ENTRY(group, name, value) \
+- { std::string result(torrent::option_as_string(torrent::group, value)); \
++ { lt_log_print(torrent::LOG_MOCK_CALLS, "option_string: %s", name); \
++ std::string result(torrent::option_as_string(torrent::group, value)); \
+ CPPUNIT_ASSERT_MESSAGE("Not found '" + result + "'", result == name); \
+- CPPUNIT_ASSERT(torrent::option_find_string(torrent::group, name) == value); }
++ CPPUNIT_ASSERT(torrent::option_find_string(torrent::group, name) == value); \
++ }
+
+ void
+-option_strings_test::test_entries() {
++test_option_strings::test_entries() {
+ TEST_ENTRY(OPTION_CONNECTION_TYPE, "leech", torrent::Download::CONNECTION_LEECH);
+ TEST_ENTRY(OPTION_CONNECTION_TYPE, "seed", torrent::Download::CONNECTION_SEED);
+ TEST_ENTRY(OPTION_CONNECTION_TYPE, "initial_seed", torrent::Download::CONNECTION_INITIAL_SEED);
+diff --git a/test/torrent/utils/test_option_strings.h b/test/torrent/utils/test_option_strings.h
+new file mode 100644
+index 00000000..dc86e735
+--- /dev/null
++++ b/test/torrent/utils/test_option_strings.h
+@@ -0,0 +1,10 @@
++#include "helpers/test_fixture.h"
++
++class test_option_strings : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_option_strings);
++ CPPUNIT_TEST(test_entries);
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void test_entries();
++};
+diff --git a/test/torrent/utils/test_queue_buckets.cc b/test/torrent/utils/test_queue_buckets.cc
+index a32d17e7..49d1bae6 100644
+--- a/test/torrent/utils/test_queue_buckets.cc
++++ b/test/torrent/utils/test_queue_buckets.cc
+@@ -5,7 +5,7 @@
+ #include "utils/instrumentation.h"
+ #include "utils/queue_buckets.h"
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(TestQueueBuckets);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_queue_buckets, "torrent/utils");
+
+ struct test_constants {
+ static const int bucket_count = 2;
+@@ -87,7 +87,7 @@ struct test_queue_bucket_compare {
+ //
+
+ void
+-TestQueueBuckets::test_basic() {
++test_queue_buckets::test_basic() {
+ torrent::instrumentation_initialize();
+
+ buckets_type buckets;
+@@ -129,7 +129,7 @@ TestQueueBuckets::test_basic() {
+ }
+
+ void
+-TestQueueBuckets::test_erase() {
++test_queue_buckets::test_erase() {
+ items_destroyed = 0;
+ torrent::instrumentation_initialize();
+
+@@ -162,7 +162,7 @@ bucket_queue_find_in_any(const buckets_type& buckets, int value) {
+ }
+
+ void
+-TestQueueBuckets::test_find() {
++test_queue_buckets::test_find() {
+ items_destroyed = 0;
+ torrent::instrumentation_initialize();
+
+@@ -183,7 +183,7 @@ TestQueueBuckets::test_find() {
+ }
+
+ void
+-TestQueueBuckets::test_destroy_range() {
++test_queue_buckets::test_destroy_range() {
+ items_destroyed = 0;
+ torrent::instrumentation_initialize();
+
+@@ -206,7 +206,7 @@ TestQueueBuckets::test_destroy_range() {
+ }
+
+ void
+-TestQueueBuckets::test_move_range() {
++test_queue_buckets::test_move_range() {
+ items_destroyed = 0;
+ torrent::instrumentation_initialize();
+
+diff --git a/test/torrent/utils/test_queue_buckets.h b/test/torrent/utils/test_queue_buckets.h
+index 94624573..a7f1c30a 100644
+--- a/test/torrent/utils/test_queue_buckets.h
++++ b/test/torrent/utils/test_queue_buckets.h
+@@ -1,21 +1,18 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#include "helpers/test_fixture.h"
+
+-#include "protocol/request_list.h"
++class test_queue_buckets : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_queue_buckets);
+
+-class TestQueueBuckets : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(TestQueueBuckets);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_erase);
+ CPPUNIT_TEST(test_find);
+
+ CPPUNIT_TEST(test_destroy_range);
+ CPPUNIT_TEST(test_move_range);
++
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp() {}
+- void tearDown() {}
+-
+ void test_basic();
+ void test_erase();
+ void test_find();
+diff --git a/test/torrent/utils/signal_bitfield_test.cc b/test/torrent/utils/test_signal_bitfield.cc
+similarity index 85%
+rename from test/torrent/utils/signal_bitfield_test.cc
+rename to test/torrent/utils/test_signal_bitfield.cc
+index 34b622b3..4ecd18c0 100644
+--- a/test/torrent/utils/signal_bitfield_test.cc
++++ b/test/torrent/utils/test_signal_bitfield.cc
+@@ -1,13 +1,15 @@
+ #include "config.h"
+
++#include "test_signal_bitfield.h"
++
++#include "helpers/test_thread.h"
++#include "helpers/test_utils.h"
++
+ #include <torrent/exceptions.h>
+ #include <torrent/utils/signal_bitfield.h>
+ #include <torrent/utils/thread_base.h>
+
+-#include "signal_bitfield_test.h"
+-#include "thread_base_test.h"
+-
+-CPPUNIT_TEST_SUITE_REGISTRATION(utils_signal_bitfield_test);
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_signal_bitfield, "torrent/utils");
+
+ static void
+ mark_index(uint32_t* bitfield, unsigned int index) {
+@@ -20,13 +22,10 @@ check_index(uint32_t* bitfield, unsigned int index) {
+ }
+
+ void
+-utils_signal_bitfield_test::setUp() {
+-}
+-
+-void
+-utils_signal_bitfield_test::tearDown() {
++test_signal_bitfield::tearDown() {
+ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+ torrent::thread_base::release_global_lock();
++ test_fixture::tearDown();
+ }
+
+ static bool
+@@ -54,7 +53,7 @@ verify_did_internal_error(std::function<unsigned int ()> func, bool should_throw
+ did_throw));
+
+ void
+-utils_signal_bitfield_test::test_basic() {
++test_signal_bitfield::test_basic() {
+ SETUP_SIGNAL_BITFIELD();
+
+ CPPUNIT_ASSERT(torrent::signal_bitfield::max_size == sizeof(torrent::signal_bitfield::bitfield_type) * 8);
+@@ -68,7 +67,7 @@ utils_signal_bitfield_test::test_basic() {
+ }
+
+ void
+-utils_signal_bitfield_test::test_single() {
++test_signal_bitfield::test_single() {
+ SETUP_SIGNAL_BITFIELD();
+
+ CPPUNIT_ASSERT(signal_bitfield.add_signal(std::bind(&mark_index, &marked_bitfield, 0)) == 0);
+@@ -86,7 +85,7 @@ utils_signal_bitfield_test::test_single() {
+ }
+
+ void
+-utils_signal_bitfield_test::test_multiple() {
++test_signal_bitfield::test_multiple() {
+ SETUP_SIGNAL_BITFIELD();
+
+ for (unsigned int i = 0; i < torrent::signal_bitfield::max_size; i++)
+@@ -106,10 +105,10 @@ utils_signal_bitfield_test::test_multiple() {
+ }
+
+ void
+-utils_signal_bitfield_test::test_thread() {
++test_signal_bitfield::test_threaded() {
+ uint32_t marked_bitfield = 0;
+- thread_test* thread = new thread_test;
+- // thread->set_test_flag(thread_test::test_flag_long_timeout);
++ test_thread* thread = new test_thread;
++ // thread->set_test_flag(test_thread::test_flag_long_timeout);
+
+ for (unsigned int i = 0; i < torrent::signal_bitfield::max_size; i++)
+ CPPUNIT_ASSERT(thread->signal_bitfield()->add_signal(std::bind(&mark_index, &marked_bitfield, i)) == i);
+@@ -131,7 +130,7 @@ utils_signal_bitfield_test::test_thread() {
+ }
+
+ thread->stop_thread();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE)));
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_state, thread, test_thread::STATE_INACTIVE)));
+
+ delete thread;
+ }
+diff --git a/test/torrent/utils/test_signal_bitfield.h b/test/torrent/utils/test_signal_bitfield.h
+new file mode 100644
+index 00000000..2d24d955
+--- /dev/null
++++ b/test/torrent/utils/test_signal_bitfield.h
+@@ -0,0 +1,22 @@
++#include "helpers/test_fixture.h"
++
++class test_signal_bitfield : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_signal_bitfield);
++
++ CPPUNIT_TEST(test_basic);
++ CPPUNIT_TEST(test_single);
++ CPPUNIT_TEST(test_multiple);
++
++ CPPUNIT_TEST(test_threaded);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void tearDown();
++
++ void test_basic();
++ void test_single();
++ void test_multiple();
++
++ void test_threaded();
++};
+diff --git a/test/torrent/utils/test_thread_base.cc b/test/torrent/utils/test_thread_base.cc
+new file mode 100644
+index 00000000..33519b7c
+--- /dev/null
++++ b/test/torrent/utils/test_thread_base.cc
+@@ -0,0 +1,169 @@
++#include "config.h"
++
++#include "test_thread_base.h"
++
++#include "helpers/test_thread.h"
++#include "helpers/test_utils.h"
++
++#include <functional>
++#include <unistd.h>
++
++#include "torrent/exceptions.h"
++#include "torrent/poll_select.h"
++#include "torrent/utils/log.h"
++#include "torrent/utils/thread_base.h"
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_thread_base, "torrent/utils");
++
++#define TEST_BEGIN(name) \
++ lt_log_print(torrent::LOG_MOCK_CALLS, "thread_base: %s", name); \
++
++void throw_shutdown_exception() { throw torrent::shutdown_exception(); }
++
++void
++test_thread_base::tearDown() {
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++ torrent::thread_base::release_global_lock();
++ test_fixture::tearDown();
++}
++
++void
++test_thread_base::test_basic() {
++ test_thread* thread = new test_thread;
++
++ CPPUNIT_ASSERT(thread->flags() == 0);
++
++ CPPUNIT_ASSERT(!thread->is_main_polling());
++ CPPUNIT_ASSERT(!thread->is_active());
++ CPPUNIT_ASSERT(thread->global_queue_size() == 0);
++ CPPUNIT_ASSERT(thread->poll() == NULL);
++
++ // Check active...
++}
++
++void
++test_thread_base::test_lifecycle() {
++ test_thread* thread = new test_thread;
++
++ CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_UNKNOWN);
++ CPPUNIT_ASSERT(thread->test_state() == test_thread::TEST_NONE);
++
++ thread->init_thread();
++ CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_INITIALIZED);
++ CPPUNIT_ASSERT(thread->is_initialized());
++ CPPUNIT_ASSERT(thread->test_state() == test_thread::TEST_PRE_START);
++
++ thread->set_pre_stop();
++ CPPUNIT_ASSERT(!wait_for_true(std::bind(&test_thread::is_test_state, thread, test_thread::TEST_PRE_STOP)));
++
++ thread->start_thread();
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_state, thread, test_thread::STATE_ACTIVE)));
++ CPPUNIT_ASSERT(thread->is_active());
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_test_state, thread, test_thread::TEST_PRE_STOP)));
++
++ thread->stop_thread();
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_state, thread, test_thread::STATE_INACTIVE)));
++ CPPUNIT_ASSERT(thread->is_inactive());
++
++ delete thread;
++}
++
++void
++test_thread_base::test_global_lock_basic() {
++ test_thread* thread = new test_thread;
++
++ thread->init_thread();
++ thread->start_thread();
++
++ CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0);
++
++ // Acquire main thread...
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++ CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
++
++ torrent::thread_base::release_global_lock();
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++ CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
++
++ torrent::thread_base::release_global_lock();
++ torrent::thread_base::acquire_global_lock();
++ CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
++
++ thread->set_acquire_global();
++ CPPUNIT_ASSERT(!wait_for_true(std::bind(&test_thread::is_test_flags, thread, test_thread::test_flag_has_global)));
++
++ torrent::thread_base::release_global_lock();
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_test_flags, thread, test_thread::test_flag_has_global)));
++
++ CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
++ torrent::thread_base::release_global_lock();
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++
++ // Test waive (loop).
++
++ CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0);
++
++ torrent::thread_base::release_global_lock();
++ thread->stop_thread();
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_state, thread, test_thread::STATE_INACTIVE)));
++
++ delete thread;
++}
++
++void
++test_thread_base::test_interrupt() {
++ test_thread* thread = new test_thread;
++ thread->set_test_flag(test_thread::test_flag_long_timeout);
++
++ thread->init_thread();
++ thread->start_thread();
++
++ // Vary the various timeouts.
++
++ for (int i = 0; i < 100; i++) {
++ thread->interrupt();
++ usleep(0);
++
++ thread->set_test_flag(test_thread::test_flag_do_work);
++ thread->interrupt();
++
++ // Wait for flag to clear.
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_not_test_flags, thread, test_thread::test_flag_do_work)));
++ }
++
++ thread->stop_thread();
++ CPPUNIT_ASSERT(wait_for_true(std::bind(&test_thread::is_state, thread, test_thread::STATE_INACTIVE)));
++
++ delete thread;
++}
++
++void
++test_thread_base::test_stop() {
++ { TEST_BEGIN("trylock global lock");
++ CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
++ // torrent::thread_base::acquire_global_lock();
++ };
++
++ for (int i = 0; i < 20; i++) {
++ CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
++
++ test_thread* thread = new test_thread;
++ thread->set_test_flag(test_thread::test_flag_do_work);
++
++ { TEST_BEGIN("init and start thread");
++ thread->init_thread();
++ thread->start_thread();
++ };
++
++ { TEST_BEGIN("stop and delete thread");
++ thread->stop_thread_wait();
++ CPPUNIT_ASSERT(thread->is_inactive());
++
++ delete thread;
++ }
++ }
++
++ { TEST_BEGIN("release global lock");
++ torrent::thread_base::release_global_lock();
++ };
++}
+diff --git a/test/torrent/utils/test_thread_base.h b/test/torrent/utils/test_thread_base.h
+new file mode 100644
+index 00000000..7b2a3432
+--- /dev/null
++++ b/test/torrent/utils/test_thread_base.h
+@@ -0,0 +1,25 @@
++#include "helpers/test_fixture.h"
++
++class test_thread_base : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_thread_base);
++
++ CPPUNIT_TEST(test_basic);
++ CPPUNIT_TEST(test_lifecycle);
++
++ CPPUNIT_TEST(test_global_lock_basic);
++ CPPUNIT_TEST(test_interrupt);
++ CPPUNIT_TEST(test_stop);
++
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void tearDown();
++
++ void test_basic();
++ void test_lifecycle();
++
++ void test_global_lock_basic();
++ void test_interrupt();
++ void test_interrupt_legacy();
++ void test_stop();
++};
+diff --git a/test/torrent/utils/test_uri_parser.cc b/test/torrent/utils/test_uri_parser.cc
+index 1f4bebe8..66d6cda5 100644
+--- a/test/torrent/utils/test_uri_parser.cc
++++ b/test/torrent/utils/test_uri_parser.cc
+@@ -2,31 +2,22 @@
+
+ #include "test_uri_parser.h"
+
+-#include <cinttypes>
+-#include <iostream>
++#include <torrent/utils/log.h>
+ #include <torrent/utils/uri_parser.h>
+
+-CPPUNIT_TEST_SUITE_REGISTRATION(UriParserTest);
+-
+-void
+-UriParserTest::setUp() {
+-}
+-
+-void
+-UriParserTest::tearDown() {
+-}
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_uri_parser, "torrent/utils");
+
+ void
+ test_print_uri_state(torrent::utils::uri_state state) {
+- std::cerr << "state.uri: " << state.uri << std::endl;
+- std::cerr << "state.scheme: " << state.scheme << std::endl;
+- std::cerr << "state.resource: " << state.resource << std::endl;
+- std::cerr << "state.query: " << state.query << std::endl;
+- std::cerr << "state.fragment: " << state.fragment << std::endl;
++ lt_log_print(torrent::LOG_MOCK_CALLS, "state.uri: %s", state.uri.c_str());
++ lt_log_print(torrent::LOG_MOCK_CALLS, "state.scheme: %s", state.scheme.c_str());
++ lt_log_print(torrent::LOG_MOCK_CALLS, "state.resource: %s", state.resource.c_str());
++ lt_log_print(torrent::LOG_MOCK_CALLS, "state.query: %s", state.query.c_str());
++ lt_log_print(torrent::LOG_MOCK_CALLS, "state.fragment: %s", state.fragment.c_str());
+ }
+
+ void
+-UriParserTest::test_basic() {
++test_uri_parser::test_basic() {
+ torrent::utils::uri_state state;
+
+ CPPUNIT_ASSERT(state.state == torrent::utils::uri_state::state_empty);
+@@ -37,7 +28,7 @@ UriParserTest::test_basic() {
+ #define MAGNET_BASIC "magnet:?xt=urn:sha1:YNCKHTQCWBTRNJIV4WNAE52SJUQCZO5C"
+
+ void
+-UriParserTest::test_basic_magnet() {
++test_uri_parser::test_basic_magnet() {
+ torrent::utils::uri_state state;
+
+ uri_parse_str(MAGNET_BASIC, state);
+@@ -63,7 +54,7 @@ UriParserTest::test_basic_magnet() {
+ #define QUERY_MAGNET "magnet:?" QUERY_MAGNET_QUERY
+
+ void
+-UriParserTest::test_query_magnet() {
++test_uri_parser::test_query_magnet() {
+ torrent::utils::uri_state state;
+ torrent::utils::uri_query_state query_state;
+
+@@ -82,7 +73,7 @@ UriParserTest::test_query_magnet() {
+ uri_parse_query_str(state.query, query_state);
+
+ for (auto element : query_state.elements)
+- std::cerr << "query_element: " << element << std::endl;
++ lt_log_print(torrent::LOG_MOCK_CALLS, "query_element: %s", element.c_str());
+
+ CPPUNIT_ASSERT(query_state.state == torrent::utils::uri_query_state::state_valid);
+
+diff --git a/test/torrent/utils/test_uri_parser.h b/test/torrent/utils/test_uri_parser.h
+index 4f1c2586..f978c8ad 100644
+--- a/test/torrent/utils/test_uri_parser.h
++++ b/test/torrent/utils/test_uri_parser.h
+@@ -1,16 +1,15 @@
+-#include <cppunit/extensions/HelperMacros.h>
++#include "helpers/test_fixture.h"
++
++class test_uri_parser : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_uri_parser);
+
+-class UriParserTest : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(UriParserTest);
+ CPPUNIT_TEST(test_basic);
+ CPPUNIT_TEST(test_basic_magnet);
+ CPPUNIT_TEST(test_query_magnet);
++
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+- void setUp();
+- void tearDown();
+-
+ void test_basic();
+ void test_basic_magnet();
+ void test_query_magnet();
+diff --git a/test/torrent/utils/thread_base_test.cc b/test/torrent/utils/thread_base_test.cc
+deleted file mode 100644
+index 8366c9ba..00000000
+--- a/test/torrent/utils/thread_base_test.cc
++++ /dev/null
+@@ -1,224 +0,0 @@
+-#include "config.h"
+-
+-#include <functional>
+-#include <unistd.h>
+-
+-#include <torrent/exceptions.h>
+-#include <torrent/poll_select.h>
+-#include <torrent/utils/thread_base.h>
+-
+-#include "thread_base_test.h"
+-
+-CPPUNIT_TEST_SUITE_REGISTRATION(utils_thread_base_test);
+-
+-const int thread_test::test_flag_pre_stop;
+-const int thread_test::test_flag_long_timeout;
+-
+-const int thread_test::test_flag_acquire_global;
+-const int thread_test::test_flag_has_global;
+-
+-const int thread_test::test_flag_do_work;
+-const int thread_test::test_flag_pre_poke;
+-const int thread_test::test_flag_post_poke;
+-
+-void throw_shutdown_exception() { throw torrent::shutdown_exception(); }
+-
+-thread_test::thread_test() :
+- m_test_state(TEST_NONE),
+- m_test_flags(0) {
+-}
+-
+-void
+-thread_test::init_thread() {
+- m_state = STATE_INITIALIZED;
+- m_test_state = TEST_PRE_START;
+- m_poll = torrent::PollSelect::create(256);
+-}
+-
+-void
+-thread_test::call_events() {
+- if ((m_test_flags & test_flag_pre_stop) && m_test_state == TEST_PRE_START && m_state == STATE_ACTIVE)
+- __sync_lock_test_and_set(&m_test_state, TEST_PRE_STOP);
+-
+- if ((m_test_flags & test_flag_acquire_global)) {
+- acquire_global_lock();
+- __sync_and_and_fetch(&m_test_flags, ~test_flag_acquire_global);
+- __sync_or_and_fetch(&m_test_flags, test_flag_has_global);
+- }
+-
+- if ((m_flags & flag_do_shutdown)) {
+- if ((m_flags & flag_did_shutdown))
+- throw torrent::internal_error("Already trigged shutdown.");
+-
+- __sync_or_and_fetch(&m_flags, flag_did_shutdown);
+- throw torrent::shutdown_exception();
+- }
+-
+- if ((m_test_flags & test_flag_pre_poke)) {
+- }
+-
+- if ((m_test_flags & test_flag_do_work)) {
+- usleep(10 * 1000); // TODO: Don't just sleep, as that give up core.
+- __sync_and_and_fetch(&m_test_flags, ~test_flag_do_work);
+- }
+-
+- if ((m_test_flags & test_flag_post_poke)) {
+- }
+-}
+-
+-bool
+-wait_for_true(std::function<bool ()> test_function) {
+- int i = 100;
+-
+- do {
+- if (test_function())
+- return true;
+-
+- usleep(10 * 1000);
+- } while (--i);
+-
+- return false;
+-}
+-
+-void
+-utils_thread_base_test::setUp() {
+-}
+-
+-void
+-utils_thread_base_test::tearDown() {
+- CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+- torrent::thread_base::release_global_lock();
+-}
+-
+-void
+-utils_thread_base_test::test_basic() {
+- thread_test* thread = new thread_test;
+-
+- CPPUNIT_ASSERT(thread->flags() == 0);
+-
+- CPPUNIT_ASSERT(!thread->is_main_polling());
+- CPPUNIT_ASSERT(!thread->is_active());
+- CPPUNIT_ASSERT(thread->global_queue_size() == 0);
+- CPPUNIT_ASSERT(thread->poll() == NULL);
+-
+- // Check active...
+-}
+-
+-void
+-utils_thread_base_test::test_lifecycle() {
+- thread_test* thread = new thread_test;
+-
+- CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_UNKNOWN);
+- CPPUNIT_ASSERT(thread->test_state() == thread_test::TEST_NONE);
+-
+- thread->init_thread();
+- CPPUNIT_ASSERT(thread->state() == torrent::thread_base::STATE_INITIALIZED);
+- CPPUNIT_ASSERT(thread->is_initialized());
+- CPPUNIT_ASSERT(thread->test_state() == thread_test::TEST_PRE_START);
+-
+- thread->set_pre_stop();
+- CPPUNIT_ASSERT(!wait_for_true(std::bind(&thread_test::is_test_state, thread, thread_test::TEST_PRE_STOP)));
+-
+- thread->start_thread();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_state, thread, thread_test::STATE_ACTIVE)));
+- CPPUNIT_ASSERT(thread->is_active());
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_test_state, thread, thread_test::TEST_PRE_STOP)));
+-
+- thread->stop_thread();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE)));
+- CPPUNIT_ASSERT(thread->is_inactive());
+-
+- delete thread;
+-}
+-
+-void
+-utils_thread_base_test::test_global_lock_basic() {
+- thread_test* thread = new thread_test;
+-
+- thread->init_thread();
+- thread->start_thread();
+-
+- CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0);
+-
+- // Acquire main thread...
+- CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+- CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
+-
+- torrent::thread_base::release_global_lock();
+- CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+- CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
+-
+- torrent::thread_base::release_global_lock();
+- torrent::thread_base::acquire_global_lock();
+- CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
+-
+- thread->set_acquire_global();
+- CPPUNIT_ASSERT(!wait_for_true(std::bind(&thread_test::is_test_flags, thread, thread_test::test_flag_has_global)));
+-
+- torrent::thread_base::release_global_lock();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_test_flags, thread, thread_test::test_flag_has_global)));
+-
+- CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
+- torrent::thread_base::release_global_lock();
+- CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+-
+- // Test waive (loop).
+-
+- CPPUNIT_ASSERT(torrent::thread_base::global_queue_size() == 0);
+-
+- torrent::thread_base::release_global_lock();
+- thread->stop_thread();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE)));
+-
+- delete thread;
+-}
+-
+-void
+-utils_thread_base_test::test_interrupt() {
+- thread_test* thread = new thread_test;
+- thread->set_test_flag(thread_test::test_flag_long_timeout);
+-
+- thread->init_thread();
+- thread->start_thread();
+-
+- // Vary the various timeouts.
+-
+- for (int i = 0; i < 100; i++) {
+- thread->interrupt();
+- usleep(0);
+-
+- thread->set_test_flag(thread_test::test_flag_do_work);
+- thread->interrupt();
+-
+- // Wait for flag to clear.
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_not_test_flags, thread, thread_test::test_flag_do_work)));
+- }
+-
+- thread->stop_thread();
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&thread_test::is_state, thread, thread_test::STATE_INACTIVE)));
+-
+- delete thread;
+-}
+-
+-void
+-utils_thread_base_test::test_stop() {
+- CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock());
+- // torrent::thread_base::acquire_global_lock();
+-
+- for (int i = 0; i < 20; i++) {
+- CPPUNIT_ASSERT(!torrent::thread_base::trylock_global_lock());
+-
+- thread_test* thread = new thread_test;
+- thread->set_test_flag(thread_test::test_flag_do_work);
+-
+- thread->init_thread();
+- thread->start_thread();
+-
+- thread->stop_thread_wait();
+- CPPUNIT_ASSERT(thread->is_inactive());
+-
+- delete thread;
+- }
+-
+- torrent::thread_base::release_global_lock();
+-}
+diff --git a/test/torrent/utils/thread_base_test.h b/test/torrent/utils/thread_base_test.h
+deleted file mode 100644
+index 22eb99dc..00000000
+--- a/test/torrent/utils/thread_base_test.h
++++ /dev/null
+@@ -1,86 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "torrent/utils/thread_base.h"
+-
+-class utils_thread_base_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(utils_thread_base_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST(test_lifecycle);
+-
+- CPPUNIT_TEST(test_global_lock_basic);
+- CPPUNIT_TEST(test_interrupt);
+- CPPUNIT_TEST(test_stop);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp();
+- void tearDown();
+-
+- void test_basic();
+- void test_lifecycle();
+-
+- void test_global_lock_basic();
+- void test_interrupt();
+- void test_interrupt_legacy();
+- void test_stop();
+-};
+-
+-struct thread_management_type {
+- thread_management_type() { CPPUNIT_ASSERT(torrent::thread_base::trylock_global_lock()); }
+- ~thread_management_type() { torrent::thread_base::release_global_lock(); }
+-};
+-
+-#define SETUP_THREAD() \
+- thread_management_type thread_management; \
+- torrent::thread_disk* thread_disk = new torrent::thread_disk(); \
+- thread_disk->init_thread();
+-
+-#define CLEANUP_THREAD() \
+- CPPUNIT_ASSERT(wait_for_true(std::bind(&torrent::thread_base::is_inactive, thread_disk))); \
+- delete thread_disk;
+-
+-bool wait_for_true(std::function<bool ()> test_function);
+-
+-class thread_test : public torrent::thread_base {
+-public:
+- enum test_state {
+- TEST_NONE,
+- TEST_PRE_START,
+- TEST_PRE_STOP,
+- TEST_STOP
+- };
+-
+- static const int test_flag_pre_stop = 0x1;
+- static const int test_flag_long_timeout = 0x2;
+-
+- static const int test_flag_acquire_global = 0x10;
+- static const int test_flag_has_global = 0x20;
+-
+- static const int test_flag_do_work = 0x100;
+- static const int test_flag_pre_poke = 0x200;
+- static const int test_flag_post_poke = 0x400;
+-
+- thread_test();
+-
+- int test_state() const { return m_test_state; }
+- bool is_state(int state) const { return m_state == state; }
+- bool is_test_state(int state) const { return m_test_state == state; }
+- bool is_test_flags(int flags) const { return (m_test_flags & flags) == flags; }
+- bool is_not_test_flags(int flags) const { return !(m_test_flags & flags); }
+-
+- const char* name() const { return "test_thread"; }
+-
+- void init_thread();
+-
+- void set_pre_stop() { __sync_or_and_fetch(&m_test_flags, test_flag_pre_stop); }
+- void set_acquire_global() { __sync_or_and_fetch(&m_test_flags, test_flag_acquire_global); }
+-
+- void set_test_flag(int flags) { __sync_or_and_fetch(&m_test_flags, flags); }
+-
+-private:
+- void call_events();
+- int64_t next_timeout_usec() { return (m_test_flags & test_flag_long_timeout) ? (10000 * 1000) : (100 * 1000); }
+-
+- int m_test_state lt_cacheline_aligned;
+- int m_test_flags lt_cacheline_aligned;
+-};
+diff --git a/test/tracker/test_tracker_http.cc b/test/tracker/test_tracker_http.cc
+new file mode 100644
+index 00000000..399d00d5
+--- /dev/null
++++ b/test/tracker/test_tracker_http.cc
+@@ -0,0 +1,11 @@
++#include "config.h"
++
++#include "test_tracker_http.h"
++
++#include "tracker/tracker_http.h"
++
++CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_tracker_http, "tracker");
++
++void
++test_tracker_http::test_basic() {
++}
+diff --git a/test/tracker/test_tracker_http.h b/test/tracker/test_tracker_http.h
+new file mode 100644
+index 00000000..ab11a8f7
+--- /dev/null
++++ b/test/tracker/test_tracker_http.h
+@@ -0,0 +1,12 @@
++#include "helpers/test_fixture.h"
++
++#include "torrent/utils/thread_base.h"
++
++class test_tracker_http : public test_fixture {
++ CPPUNIT_TEST_SUITE(test_tracker_http);
++ CPPUNIT_TEST(test_basic);
++ CPPUNIT_TEST_SUITE_END();
++
++public:
++ void test_basic();
++};
+diff --git a/test/tracker/tracker_http_test.cc b/test/tracker/tracker_http_test.cc
+deleted file mode 100644
+index deda4382..00000000
+--- a/test/tracker/tracker_http_test.cc
++++ /dev/null
+@@ -1,17 +0,0 @@
+-#include "config.h"
+-
+-#include "tracker_http_test.h"
+-
+-#include "tracker/tracker_http.h"
+-
+-void
+-tracker_http_test::setUp() {
+-}
+-
+-void
+-tracker_http_test::tearDown() {
+-}
+-
+-void
+-tracker_http_test::test_basic() {
+-}
+diff --git a/test/tracker/tracker_http_test.h b/test/tracker/tracker_http_test.h
+deleted file mode 100644
+index 11ff8246..00000000
+--- a/test/tracker/tracker_http_test.h
++++ /dev/null
+@@ -1,18 +0,0 @@
+-#include <cppunit/extensions/HelperMacros.h>
+-
+-#include "tracker/tracker_http.h"
+-
+-class tracker_http_test : public CppUnit::TestFixture {
+- CPPUNIT_TEST_SUITE(tracker_http_test);
+- CPPUNIT_TEST(test_basic);
+- CPPUNIT_TEST(test_scrape);
+- CPPUNIT_TEST_SUITE_END();
+-
+-public:
+- void setUp();
+- void tearDown();
+-
+- void test_basic();
+-
+- void test_scrape();
+-};
new file mode 100644
@@ -0,0 +1,22 @@
+From 03e1c95987917bf98534e50fdd718a948540ffb2 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Thu, 25 Feb 2021 00:03:27 +0900
+Subject: [PATCH] Changes automake required files.
+
+---
+ autogen.sh | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/autogen.sh b/autogen.sh
+index 79afab8e..6def96dd 100755
+--- a/autogen.sh
++++ b/autogen.sh
+@@ -36,7 +36,7 @@ echo automake...
+ exit 1
+ }
+
+-automake --add-missing --copy --gnu || exit 1
++automake --add-missing --copy --foreign || exit 1
+
+ echo autoconf...
+ (autoconf --version) < /dev/null > /dev/null 2>&1 || {
new file mode 100644
@@ -0,0 +1,48 @@
+From b4a3888bd891d804a83ae1cee623592725975895 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Sat, 27 Feb 2021 22:11:55 +0900
+Subject: [PATCH] Replaced custom execinfo autoconf test.
+
+---
+ configure.ac | 2 +-
+ scripts/rak_execinfo.m4 | 11 -----------
+ 2 files changed, 1 insertion(+), 12 deletions(-)
+ delete mode 100644 scripts/rak_execinfo.m4
+
+diff --git a/configure.ac b/configure.ac
+index 88a46edd..197bbc94 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -34,7 +34,6 @@ RAK_CHECK_CXXFLAGS
+ RAK_ENABLE_DEBUG
+ RAK_ENABLE_EXTRA_DEBUG
+ RAK_ENABLE_WERROR
+-RAK_DISABLE_BACKTRACE
+
+ RAK_CHECK_CXX11
+
+@@ -58,6 +57,7 @@ TORRENT_WITH_INOTIFY
+ CC_ATTRIBUTE_VISIBILITY
+
+ AX_CHECK_ZLIB
++AX_EXECINFO
+ AX_PTHREAD
+
+ PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
+diff --git a/scripts/rak_execinfo.m4 b/scripts/rak_execinfo.m4
+deleted file mode 100644
+index c1d9b2f8..00000000
+--- a/scripts/rak_execinfo.m4
++++ /dev/null
+@@ -1,11 +0,0 @@
+-AC_DEFUN([RAK_DISABLE_BACKTRACE], [
+- AC_ARG_ENABLE(backtrace,
+- AC_HELP_STRING([--disable-backtrace], [disable backtrace information [[default=no]]]),
+- [
+- if test "$enableval" = "yes"; then
+- AX_EXECINFO
+- fi
+- ],[
+- AX_EXECINFO
+- ])
+-])
new file mode 100644
@@ -0,0 +1,135 @@
+From e5ed6301e0d07adeaab10e9924a8c9a2e327cdc5 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Thu, 29 Apr 2021 19:33:04 +0900
+Subject: [PATCH] Added option to disable pthread_setname_np.
+
+---
+ configure.ac | 30 +++++++++++++++++++-----------
+ scripts/checks.m4 | 18 ++++++++++++++++++
+ src/torrent/utils/thread_base.cc | 16 ++++++++++++++--
+ 3 files changed, 51 insertions(+), 13 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index 197bbc94..73caf712 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -24,9 +24,9 @@ AC_PROG_CXX
+ AC_SYS_LARGEFILE
+
+ AC_C_BIGENDIAN(
+- AC_DEFINE(IS_BIG_ENDIAN, 1, Big endian),
+- AC_DEFINE(IS_LITTLE_ENDIAN, 1, Little endian),
+- AC_MSG_ERROR([Could not determine endianness])
++ AC_DEFINE(IS_BIG_ENDIAN, 1, Big endian),
++ AC_DEFINE(IS_LITTLE_ENDIAN, 1, Little endian),
++ AC_MSG_ERROR([Could not determine endianness])
+ )
+
+ RAK_CHECK_CFLAGS
+@@ -54,7 +54,15 @@ TORRENT_WITHOUT_STATVFS
+ TORRENT_WITHOUT_STATFS
+ TORRENT_WITH_INOTIFY
+
+-CC_ATTRIBUTE_VISIBILITY
++AC_ARG_ENABLE(attribute-visibility,
++ AC_HELP_STRING([--disable-attribute-visibility], [disable symbol visibility attribute [[default=enable]]]),
++ [
++ if test "$enableval" = "yes"; then
++ CC_ATTRIBUTE_VISIBILITY
++ fi
++ ],[
++ CC_ATTRIBUTE_VISIBILITY
++ ])
+
+ AX_CHECK_ZLIB
+ AX_EXECINFO
+@@ -71,11 +79,11 @@ TORRENT_ARG_CYRUS_RC4
+
+ AC_CHECK_FUNCS(posix_memalign)
+
+-TORRENT_CHECK_MADVISE()
+-TORRENT_CHECK_CACHELINE()
+-TORRENT_CHECK_POPCOUNT()
+-TORRENT_CHECK_PTHREAD_SETNAME_NP()
+-TORRENT_MINCORE()
++TORRENT_CHECK_MADVISE
++TORRENT_CHECK_CACHELINE
++TORRENT_CHECK_POPCOUNT
++TORRENT_DISABLE_PTHREAD_SETNAME_NP
++TORRENT_MINCORE
+
+ TORRENT_DISABLE_INSTRUMENTATION
+
+@@ -88,8 +96,8 @@ AC_SUBST(LIBTORRENT_CFLAGS)
+ AC_DEFINE(HAVE_CONFIG_H, 1, true if config.h was included)
+
+ CC_ATTRIBUTE_UNUSED(
+- AC_DEFINE([__UNUSED], [__attribute__((unused))], [Wrapper around unused attribute]),
+- AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported])
++ AC_DEFINE([__UNUSED], [__attribute__((unused))], [Wrapper around unused attribute]),
++ AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported])
+ )
+
+ AC_OUTPUT([
+diff --git a/scripts/checks.m4 b/scripts/checks.m4
+index 98ef17f8..915a5011 100644
+--- a/scripts/checks.m4
++++ b/scripts/checks.m4
+@@ -490,3 +490,21 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
+ ])
+ ])
+ ])
++
++AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
++ AC_MSG_CHECKING([for pthread_setname_no])
++
++ AC_ARG_ENABLE(pthread-setname-np,
++ AC_HELP_STRING([--disable-pthread-setname-np], [disable pthread_setname_np]),
++ [
++ if test "$enableval" = "no"; then
++ AC_MSG_RESULT(disabled)
++ else
++ AC_MSG_RESULT(checking)
++ TORRENT_CHECK_PTHREAD_SETNAME_NP
++ fi
++ ], [
++ TORRENT_CHECK_PTHREAD_SETNAME_NP
++ ]
++ )
++])
+diff --git a/src/torrent/utils/thread_base.cc b/src/torrent/utils/thread_base.cc
+index 99d6355d..ec0619f3 100644
+--- a/src/torrent/utils/thread_base.cc
++++ b/src/torrent/utils/thread_base.cc
+@@ -41,10 +41,16 @@ thread_base::~thread_base() {
+
+ void
+ thread_base::start_thread() {
+- if (m_poll == NULL)
++ if (this == nullptr)
++ throw internal_error("Called thread_base::start_thread on a nullptr.");
++
++ if (m_poll == nullptr)
+ throw internal_error("No poll object for thread defined.");
+
+- if (!is_initialized() || pthread_create(&m_thread, NULL, (pthread_func)&thread_base::event_loop, this))
++ if (!is_initialized())
++ throw internal_error("Called thread_base::start_thread on an uninitialized object.");
++
++ if (pthread_create(&m_thread, NULL, (pthread_func)&thread_base::event_loop, this))
+ throw internal_error("Failed to create thread.");
+ }
+
+@@ -82,6 +88,12 @@ thread_base::should_handle_sigusr1() {
+
+ void*
+ thread_base::event_loop(thread_base* thread) {
++ if (thread == nullptr)
++ throw internal_error("thread_base::event_loop called with a null pointer thread");
++
++ if (!thread->is_initialized())
++ throw internal_error("thread_base::event_loop call on an uninitialized object");
++
+ __sync_lock_test_and_set(&thread->m_state, STATE_ACTIVE);
+
+ #if defined(HAS_PTHREAD_SETNAME_NP_DARWIN)
new file mode 100644
@@ -0,0 +1,594 @@
+From f978e68f9d907e25207d0a7d247d2b10935e5d76 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Thu, 29 Apr 2021 19:34:35 +0900
+Subject: [PATCH] Improved backtrace error checking.
+
+---
+ src/download/download_main.cc | 36 -------------
+ src/download/download_main.h | 36 -------------
+ src/download/download_wrapper.cc | 43 ++--------------
+ src/torrent/data/download_data.h | 36 -------------
+ src/torrent/download/choke_queue.cc | 50 +++++-------------
+ src/torrent/download/resource_manager.cc | 53 +++++--------------
+ src/torrent/exceptions.cc | 65 +++++++-----------------
+ src/torrent/torrent.cc | 1 +
+ src/torrent/utils/extents.h | 36 -------------
+ src/torrent/utils/signal_bitfield.cc | 36 -------------
+ 10 files changed, 48 insertions(+), 344 deletions(-)
+
+diff --git a/src/download/download_main.cc b/src/download/download_main.cc
+index 9a3f9df2..e075038a 100644
+--- a/src/download/download_main.cc
++++ b/src/download/download_main.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <cstring>
+diff --git a/src/download/download_main.h b/src/download/download_main.h
+index da3cf182..4783e863 100644
+--- a/src/download/download_main.h
++++ b/src/download/download_main.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_DOWNLOAD_MAIN_H
+ #define LIBTORRENT_DOWNLOAD_MAIN_H
+
+diff --git a/src/download/download_wrapper.cc b/src/download/download_wrapper.cc
+index 59e81781..304bddce 100644
+--- a/src/download/download_wrapper.cc
++++ b/src/download/download_wrapper.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <iterator>
+@@ -62,6 +26,8 @@
+
+ #include "download_wrapper.h"
+
++#define LT_LOG_THIS(log_fmt, ...) \
++ lt_log_print_info(LOG_TORRENT_INFO, this->info(), "download", log_fmt, __VA_ARGS__);
+ #define LT_LOG_STORAGE_ERRORS(log_fmt, ...) \
+ lt_log_print_info(LOG_PROTOCOL_STORAGE_ERRORS, this->info(), "storage_errors", log_fmt, __VA_ARGS__);
+
+@@ -325,8 +291,8 @@ DownloadWrapper::receive_tick(uint32_t ticks) {
+
+ void
+ DownloadWrapper::receive_update_priorities() {
+- if (m_main->chunk_selector()->empty())
+- return;
++ LT_LOG_THIS("update priorities: chunks_selected:%" PRIu32 " wanted_chunks:%" PRIu32,
++ m_main->chunk_selector()->size(), data()->wanted_chunks());
+
+ data()->mutable_high_priority()->clear();
+ data()->mutable_normal_priority()->clear();
+@@ -359,7 +325,6 @@ DownloadWrapper::receive_update_priorities() {
+ }
+
+ bool was_partial = data()->wanted_chunks() != 0;
+-
+ data()->update_wanted_chunks();
+
+ m_main->chunk_selector()->update_priorities();
+diff --git a/src/torrent/data/download_data.h b/src/torrent/data/download_data.h
+index fc212047..c701cb2f 100644
+--- a/src/torrent/data/download_data.h
++++ b/src/torrent/data/download_data.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_DATA_DOWNLOAD_DATA_H
+ #define LIBTORRENT_DATA_DOWNLOAD_DATA_H
+
+diff --git a/src/torrent/download/choke_queue.cc b/src/torrent/download/choke_queue.cc
+index 7c00b686..edf47795 100644
+--- a/src/torrent/download/choke_queue.cc
++++ b/src/torrent/download/choke_queue.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <algorithm>
+@@ -50,6 +14,10 @@
+
+ #include "choke_queue.h"
+
++// TODO: Add a different logging category.
++#define LT_LOG_THIS(log_fmt, ...) \
++ lt_log_print_subsystem(LOG_TORRENT_INFO, "choke_queue", log_fmt, __VA_ARGS__);
++
+ namespace torrent {
+
+ struct choke_manager_less {
+@@ -193,6 +161,9 @@ choke_queue::rebuild_containers(container_type* queued, container_type* unchoked
+
+ void
+ choke_queue::balance() {
++ LT_LOG_THIS("balancing queue: heuristics:%i currently_unchoked:%" PRIu32 " max_unchoked:%" PRIu32,
++ m_heuristics, m_currently_unchoked, m_maxUnchoked)
++
+ // Return if no balancing is needed. Don't return if is_unlimited()
+ // as we might have just changed the value and have interested that
+ // can be unchoked.
+@@ -216,6 +187,9 @@ choke_queue::balance() {
+
+ // If we have more unchoked than max global slots allow for,
+ // 'can_unchoke' will be negative.
++ //
++ // Throws std::bad_function_call if 'set_slot_can_unchoke' is not
++ // set.
+ int can_unchoke = m_slotCanUnchoke();
+ int max_unchoked = std::min(m_maxUnchoked, (uint32_t)(1 << 20));
+
+@@ -240,8 +214,8 @@ choke_queue::balance() {
+ if (result != 0)
+ m_slotUnchoke(result);
+
+- lt_log_print(LOG_PEER_DEBUG, "Called balance; adjust:%i can_unchoke:%i queued:%u unchoked:%u result:%i.",
+- adjust, can_unchoke, (unsigned)queued.size(), (unsigned)unchoked.size(), result);
++ LT_LOG_THIS("balanced queue: adjust:%i can_unchoke:%i queued:%" PRIu32 " unchoked:%" PRIu32 " result:%i",
++ adjust, can_unchoke, queued.size(), unchoked.size(), result);
+ }
+
+ void
+diff --git a/src/torrent/download/resource_manager.cc b/src/torrent/download/resource_manager.cc
+index 51434c91..8ca7b02e 100644
+--- a/src/torrent/download/resource_manager.cc
++++ b/src/torrent/download/resource_manager.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <algorithm>
+@@ -51,6 +15,11 @@
+ #include "choke_queue.h"
+ #include "resource_manager.h"
+
++#define LT_LOG_THIS(log_fmt, ...) \
++ lt_log_print_subsystem(LOG_TORRENT_INFO, "resource_manager", log_fmt, __VA_ARGS__);
++#define LT_LOG_ITR(log_fmt, ...) \
++ lt_log_print_info(LOG_TORRENT_INFO, itr->download()->info(), "resource_manager", log_fmt, __VA_ARGS__);
++
+ namespace torrent {
+
+ const Rate* resource_manager_entry::up_rate() const { return m_download->info()->up_rate(); }
+@@ -226,6 +195,8 @@ ResourceManager::group_index_of(const std::string& name) {
+
+ void
+ ResourceManager::set_priority(iterator itr, uint16_t pri) {
++ LT_LOG_ITR("set priority: %" PRIu16, 0)
++
+ itr->set_priority(pri);
+ }
+
+@@ -283,7 +254,7 @@ ResourceManager::set_max_download_unchoked(unsigned int m) {
+ // possibly multiple calls of this function.
+ void
+ ResourceManager::receive_upload_unchoke(int num) {
+- lt_log_print(LOG_PEER_INFO, "Upload unchoked slots adjust; currently:%u adjust:%i", m_currentlyUploadUnchoked, num);
++ LT_LOG_THIS("adjusting upload unchoked slots; current:%u adjusted:%i", m_currentlyUploadUnchoked, num);
+
+ if ((int)m_currentlyUploadUnchoked + num < 0)
+ throw internal_error("ResourceManager::receive_upload_unchoke(...) received an invalid value.");
+@@ -293,7 +264,7 @@ ResourceManager::receive_upload_unchoke(int num) {
+
+ void
+ ResourceManager::receive_download_unchoke(int num) {
+- lt_log_print(LOG_PEER_INFO, "Download unchoked slots adjust; currently:%u adjust:%i", m_currentlyDownloadUnchoked, num);
++ LT_LOG_THIS("adjusting download unchoked slots; current:%u adjusted:%i", m_currentlyDownloadUnchoked, num);
+
+ if ((int)m_currentlyDownloadUnchoked + num < 0)
+ throw internal_error("ResourceManager::receive_download_unchoke(...) received an invalid value.");
+@@ -387,12 +358,14 @@ ResourceManager::balance_unchoked(unsigned int weight, unsigned int max_unchoked
+ std::sort(group_first, group_last, std::bind(std::less<uint32_t>(),
+ std::bind(&choke_group::up_requested, std::placeholders::_1),
+ std::bind(&choke_group::up_requested, std::placeholders::_2)));
+- lt_log_print(LOG_PEER_DEBUG, "Upload unchoked slots cycle; currently:%u adjusted:%i max_unchoked:%u", m_currentlyUploadUnchoked, change, max_unchoked);
++
++ LT_LOG_THIS("balancing upload unchoked slots; current_unchoked:%u change:%i max_unchoked:%u", m_currentlyUploadUnchoked, change, max_unchoked);
+ } else {
+ std::sort(group_first, group_last, std::bind(std::less<uint32_t>(),
+ std::bind(&choke_group::down_requested, std::placeholders::_1),
+ std::bind(&choke_group::down_requested, std::placeholders::_2)));
+- lt_log_print(LOG_PEER_DEBUG, "Download unchoked slots cycle; currently:%u adjusted:%i max_unchoked:%u", m_currentlyDownloadUnchoked, change, max_unchoked);
++
++ LT_LOG_THIS("balancing download unchoked slots; current_unchoked:%u change:%i max_unchoked:%u", m_currentlyDownloadUnchoked, change, max_unchoked);
+ }
+
+ while (group_first != group_last) {
+diff --git a/src/torrent/exceptions.cc b/src/torrent/exceptions.cc
+index f834f9fa..7375ed8e 100644
+--- a/src/torrent/exceptions.cc
++++ b/src/torrent/exceptions.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <cerrno>
+@@ -73,23 +37,30 @@ void
+ internal_error::initialize(const std::string& msg) {
+ m_msg = msg;
+
+- std::stringstream output;
+-
+ #ifdef HAVE_BACKTRACE
+- void* stackPtrs[20];
++ void* stack_ptrs[20];
++ int stack_size = ::backtrace(stack_ptrs, 20);
++ char** stack_symbol_names = ::backtrace_symbols(stack_ptrs, stack_size);
+
+- // Print the stack and exit.
+- int stackSize = ::backtrace(stackPtrs, 20);
+- char** stackStrings = backtrace_symbols(stackPtrs, stackSize);
++ if (stack_symbol_names == nullptr) {
++ m_backtrace = "backtrace_symbols failed";
++ return;
++ }
+
+- for (int i = 0; i < stackSize; ++i)
+- output << stackStrings[i] << std::endl;
++ std::stringstream output;
+
+-#else
+- output << "Stack dump not enabled." << std::endl;
+-#endif
++ for (int i = 0; i < stack_size; ++i) {
++ if (stack_symbol_names[i] != nullptr && stack_symbol_names[i] > (void*)0x1000)
++ output << stack_symbol_names[i] << std::endl;
++ else
++ output << "stack_symbol: nullptr" << std::endl;
++ }
+
+ m_backtrace = output.str();
++
++#else
++ m_backtrace = "stack dump not enabled";
++#endif
+ }
+
+ }
+diff --git a/src/torrent/torrent.cc b/src/torrent/torrent.cc
+index fb70d247..67de4387 100644
+--- a/src/torrent/torrent.cc
++++ b/src/torrent/torrent.cc
+@@ -203,6 +203,7 @@ download_priority(Download d) {
+ return itr->priority();
+ }
+
++// TODO: Remove this.
+ void
+ download_set_priority(Download d, uint32_t pri) {
+ ResourceManager::iterator itr = manager->resource_manager()->find(d.ptr()->main());
+diff --git a/src/torrent/utils/extents.h b/src/torrent/utils/extents.h
+index c2b887b1..64605d4a 100644
+--- a/src/torrent/utils/extents.h
++++ b/src/torrent/utils/extents.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_UTILS_EXTENTS_H
+ #define LIBTORRENT_UTILS_EXTENTS_H
+
+diff --git a/src/torrent/utils/signal_bitfield.cc b/src/torrent/utils/signal_bitfield.cc
+index 82f81e7c..dfc3d1fe 100644
+--- a/src/torrent/utils/signal_bitfield.cc
++++ b/src/torrent/utils/signal_bitfield.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include "torrent/exceptions.h"
new file mode 100644
@@ -0,0 +1,199 @@
+From cabc557fdf6f12fee7029081de2cf5de88464c21 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Thu, 29 Apr 2021 21:27:50 +0900
+Subject: [PATCH] Fixed issue with multiple connections from NAT not working.
+
+---
+ src/protocol/handshake.cc | 36 -------------------
+ src/torrent/peer/peer_list.cc | 68 +++++++++++------------------------
+ src/torrent/peer/peer_list.h | 36 -------------------
+ 3 files changed, 20 insertions(+), 120 deletions(-)
+
+diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc
+index 1b877c7a..d6f48e59 100644
+--- a/src/protocol/handshake.cc
++++ b/src/protocol/handshake.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <stdio.h>
+diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc
+index 080a7f13..6ce630f7 100644
+--- a/src/torrent/peer/peer_list.cc
++++ b/src/torrent/peer/peer_list.cc
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #include "config.h"
+
+ #include <algorithm>
+@@ -262,8 +226,11 @@ PeerList::available_list_size() const {
+ return m_available_list->size();
+ }
+
++// TODO: Rename connecting:
+ PeerInfo*
+ PeerList::connected(const sockaddr* sa, int flags) {
++ // TODO: Rewrite to use new socket address api after fixing bug.
++
+ const rak::socket_address* address = rak::socket_address::cast_from(sa);
+ socket_address_key sock_key = socket_address_key::from_sockaddr(sa);
+
+@@ -281,13 +248,7 @@ PeerList::connected(const sockaddr* sa, int flags) {
+ // We should also remove any PeerInfo objects already for this
+ // address.
+ if ((filter_value & PeerInfo::flag_unwanted)) {
+- char ipv4_str[INET_ADDRSTRLEN];
+- uint32_t net_order_addr = htonl(host_byte_order_ipv4_addr);
+-
+- inet_ntop(AF_INET, &net_order_addr, ipv4_str, INET_ADDRSTRLEN);
+-
+- lt_log_print(LOG_PEER_INFO, "Peer %s is unwanted: preventing connection", ipv4_str);
+-
++ LT_LOG_EVENTS("connecting peer rejected, flagged as unwanted: " LT_LOG_SA_FMT, address->address_str().c_str(), address->port());
+ return NULL;
+ }
+
+@@ -313,12 +274,23 @@ PeerList::connected(const sockaddr* sa, int flags) {
+ //
+ // This also ensure we can connect to peers running on the same
+ // host as the tracker.
+- if (flags & connect_keep_handshakes &&
+- range.first->second->is_handshake() &&
+- rak::socket_address::cast_from(range.first->second->socket_address())->port() != address->port())
+- m_available_list->buffer()->push_back(*address);
++ // if (flags & connect_keep_handshakes &&
++ // range.first->second->is_handshake() &&
++ // rak::socket_address::cast_from(range.first->second->socket_address())->port() != address->port())
++ // m_available_list->buffer()->push_back(*address);
+
+- return NULL;
++ LT_LOG_EVENTS("connecting peer rejected, already connected (buggy, fixme): " LT_LOG_SA_FMT, address->address_str().c_str(), address->port());
++
++ // TODO: Verify this works properly, possibly add a check/flag
++ // that allows the handshake manager to notify peer list if the
++ // incoming connection was a duplicate peer hash.
++
++ //return NULL;
++
++ peerInfo = new PeerInfo(sa);
++ peerInfo->set_flags(filter_value & PeerInfo::mask_ip_table);
++
++ base_type::insert(range.second, value_type(sock_key, peerInfo));
+ }
+
+ if (flags & connect_filter_recent &&
+diff --git a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h
+index 4c2f707d..a9d31a54 100644
+--- a/src/torrent/peer/peer_list.h
++++ b/src/torrent/peer/peer_list.h
+@@ -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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+ #ifndef LIBTORRENT_PEER_LIST_H
+ #define LIBTORRENT_PEER_LIST_H
+
new file mode 100644
@@ -0,0 +1,32 @@
+From 532d3e54b3f012dc81530ebb80ded8b26434fdd9 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Wed, 16 Jun 2021 23:28:28 +0900
+Subject: [PATCH] Added '--disable-execinfo' option to configure.
+
+---
+ configure.ac | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/configure.ac b/configure.ac
+index 73caf712..a4f051e4 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -64,8 +64,17 @@ AC_ARG_ENABLE(attribute-visibility,
+ CC_ATTRIBUTE_VISIBILITY
+ ])
+
++AC_ARG_ENABLE(execinfo,
++ AC_HELP_STRING([--disable-execinfo], [disable libexecinfo [[default=enable]]]),
++ [
++ if test "$enableval" = "yes"; then
++ AX_EXECINFO
++ fi
++ ],[
++ AX_EXECINFO
++ ])
++
+ AX_CHECK_ZLIB
+-AX_EXECINFO
+ AX_PTHREAD
+
+ PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"])
new file mode 100644
@@ -0,0 +1,457 @@
+From 92781533fc4afab67447e8e6d47a649383179c44 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Sun, 20 Jun 2021 18:10:52 +0900
+Subject: [PATCH] Detect ip address.
+
+---
+ src/net/local_addr.cc | 6 +-
+ src/torrent/Makefile.am | 5 +-
+ src/torrent/net/fd.cc | 3 +
+ src/torrent/net/fd.h | 14 ++--
+ src/torrent/net/socket_address.cc | 2 +-
+ src/torrent/net/socket_address.h | 2 +-
+ src/torrent/net/utils.cc | 104 ++++++++++++++++++++++++
+ src/torrent/net/utils.h | 9 ++
+ src/tracker/tracker_http.cc | 104 ++++++++----------------
+ test/helpers/network.h | 2 +
+ test/torrent/net/test_fd.cc | 9 ++
+ test/torrent/net/test_socket_address.cc | 2 +-
+ 12 files changed, 181 insertions(+), 81 deletions(-)
+ create mode 100755 src/torrent/net/utils.cc
+ create mode 100755 src/torrent/net/utils.h
+
+diff --git a/src/net/local_addr.cc b/src/net/local_addr.cc
+index 24413265..0c7116cb 100644
+--- a/src/net/local_addr.cc
++++ b/src/net/local_addr.cc
+@@ -34,6 +34,8 @@
+ // Skomakerveien 33
+ // 3185 Skoppum, NORWAY
+
++// TODO: Remove.
++
+ #include "config.h"
+
+ #include <stdio.h>
+@@ -241,7 +243,7 @@ bool get_local_address(sa_family_t family, rak::socket_address *address) {
+ int plen = IFA_PAYLOAD(nlmsg);
+ for (const rtattr *rta = IFA_RTA(ifa);
+ RTA_OK(rta, plen);
+- rta = RTA_NEXT(rta, plen)) {
++ rta = RTA_NEXT(rta, plen)) {
+ if (rta->rta_type != IFA_LOCAL &&
+ rta->rta_type != IFA_ADDRESS) {
+ continue;
+@@ -303,7 +305,7 @@ get_local_address(sa_family_t family, rak::socket_address *address) {
+ dummy_dest.set_address_c_str("4.0.0.0");
+ break;
+ case rak::socket_address::af_inet6:
+- dummy_dest.set_address_c_str("2001:700::");
++ dummy_dest.set_address_c_str("2001:1::");
+ break;
+ default:
+ throw internal_error("Unknown address family");
+diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am
+index 30157b95..5de7e8ae 100644
+--- a/src/torrent/Makefile.am
++++ b/src/torrent/Makefile.am
+@@ -46,6 +46,8 @@ libtorrent_torrent_la_SOURCES = \
+ net/socket_event.cc \
+ net/socket_event.h \
+ net/types.h \
++ net/utils.cc \
++ net/utils.h \
+ \
+ peer/choke_status.h \
+ peer/client_info.cc \
+@@ -167,7 +169,8 @@ libtorrent_torrent_net_include_HEADERS = \
+ net/socket_address.h \
+ net/socket_address_key.h \
+ net/socket_event.h \
+- net/types.h
++ net/types.h \
++ net/utils.h
+
+ libtorrent_torrent_peer_includedir = $(includedir)/torrent/peer
+ libtorrent_torrent_peer_include_HEADERS = \
+diff --git a/src/torrent/net/fd.cc b/src/torrent/net/fd.cc
+index 07c91779..6d228181 100644
+--- a/src/torrent/net/fd.cc
++++ b/src/torrent/net/fd.cc
+@@ -64,6 +64,9 @@ fd_open(fd_flags flags) {
+ if ((flags & fd_flag_stream)) {
+ domain = SOCK_STREAM;
+ protocol = IPPROTO_TCP;
++ } else if ((flags & fd_flag_datagram)) {
++ domain = SOCK_DGRAM;
++ protocol = IPPROTO_UDP;
+ } else {
+ LT_LOG_FLAG("fd_open missing socket type");
+ errno = EINVAL;
+diff --git a/src/torrent/net/fd.h b/src/torrent/net/fd.h
+index a7094646..6ab3302d 100644
+--- a/src/torrent/net/fd.h
++++ b/src/torrent/net/fd.h
+@@ -9,11 +9,12 @@ namespace torrent {
+
+ enum fd_flags : int {
+ fd_flag_stream = 0x1,
+- fd_flag_nonblock = 0x10,
+- fd_flag_reuse_address = 0x20,
+- fd_flag_v4only = 0x40,
+- fd_flag_v6only = 0x80,
+- fd_flag_all = 0xff,
++ fd_flag_datagram = 0x10,
++ fd_flag_nonblock = 0x20,
++ fd_flag_reuse_address = 0x40,
++ fd_flag_v4only = 0x80,
++ fd_flag_v6only = 0x100,
++ fd_flag_all = 0x1ff,
+ };
+
+ constexpr bool fd_valid_flags(fd_flags flags);
+@@ -53,7 +54,8 @@ operator |=(fd_flags& lhs, fd_flags rhs) {
+ constexpr bool
+ fd_valid_flags(fd_flags flags) {
+ return
+- (flags & fd_flag_stream) &&
++ ((flags & fd_flag_stream) || (flags & fd_flag_datagram)) &&
++ !((flags & fd_flag_stream) && (flags & fd_flag_datagram)) &&
+ !((flags & fd_flag_v4only) && (flags & fd_flag_v6only)) &&
+ !(flags & ~(fd_flag_all));
+ }
+diff --git a/src/torrent/net/socket_address.cc b/src/torrent/net/socket_address.cc
+index c36ba0ae..078bee25 100644
+--- a/src/torrent/net/socket_address.cc
++++ b/src/torrent/net/socket_address.cc
+@@ -135,7 +135,7 @@ sa_unique_ptr
+ sa_make_unspec() {
+ sa_unique_ptr sa(new sockaddr);
+
+- std::memset(sa.get(), 0, sizeof(sa));
++ std::memset(sa.get(), 0, sizeof(sockaddr));
+ sa.get()->sa_family = AF_UNSPEC;
+
+ return sa;
+diff --git a/src/torrent/net/socket_address.h b/src/torrent/net/socket_address.h
+index f64aee68..b9586ca1 100644
+--- a/src/torrent/net/socket_address.h
++++ b/src/torrent/net/socket_address.h
+@@ -102,8 +102,8 @@ bool fd_sap_equal(const fd_sap_tuple& lhs, const fd_sap_tuple& rhs) LIBTORRENT_E
+
+ inline bool sap_is_unspec(const sa_unique_ptr& sap) { return sa_is_unspec(sap.get()); }
+ inline bool sap_is_unspec(const c_sa_unique_ptr& sap) { return sa_is_unspec(sap.get()); }
+-inline bool sap_is_inet(const c_sa_unique_ptr& sap) { return sa_is_inet(sap.get()); }
+ inline bool sap_is_inet(const sa_unique_ptr& sap) { return sa_is_inet(sap.get()); }
++inline bool sap_is_inet(const c_sa_unique_ptr& sap) { return sa_is_inet(sap.get()); }
+ inline bool sap_is_inet6(const sa_unique_ptr& sap) { return sa_is_inet6(sap.get()); }
+ inline bool sap_is_inet6(const c_sa_unique_ptr& sap) { return sa_is_inet6(sap.get()); }
+ inline bool sap_is_inet_inet6(const sa_unique_ptr& sap) { return sa_is_inet_inet6(sap.get()); }
+diff --git a/src/torrent/net/utils.cc b/src/torrent/net/utils.cc
+new file mode 100755
+index 00000000..4cb85924
+--- /dev/null
++++ b/src/torrent/net/utils.cc
+@@ -0,0 +1,104 @@
++#import <torrent/net/utils.h>
++
++#import <cerrno>
++#import <cstring>
++#import <torrent/net/fd.h>
++#import <torrent/net/socket_address.h>
++#import <torrent/utils/log.h>
++
++#define LT_LOG_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd: " log_fmt " (errno:%i message:'%s')", \
++ errno, std::strerror(errno));
++#define LT_LOG_FD(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt, fd);
++#define LT_LOG_FD_ERROR(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (errno:%i message:'%s')", \
++ fd, errno, std::strerror(errno));
++#define LT_LOG_FD_SIN(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (address:%s)", \
++ fd, sin_pretty_str(sa.get()).c_str());
++#define LT_LOG_FD_SIN6(log_fmt) \
++ lt_log_print(LOG_CONNECTION_FD, "fd->%i: " log_fmt " (address:%s)", \
++ fd, sin6_pretty_str(sa.get()).c_str());
++
++namespace torrent {
++
++auto detect_local_sin_addr() -> sin_unique_ptr {
++ int fd = fd_open(fd_flag_v4only | fd_flag_datagram);
++ if (fd == -1) {
++ LT_LOG_ERROR("detect_local_sin_addr: open failed");
++ return sin_unique_ptr();
++ }
++
++ // TODO: Check if unique_ptr works.
++ std::shared_ptr<void> _fd(nullptr, [fd](...){ fd_close(fd); });
++
++ auto connectAddress = sin_make();
++ connectAddress.get()->sin_addr.s_addr = htonl(0x04000001);
++ connectAddress.get()->sin_port = 80;
++
++ if (!fd_connect(fd, reinterpret_cast<sockaddr*>(connectAddress.get())) && errno != EINPROGRESS) {
++ LT_LOG_FD_ERROR("detect_local_sin_addr: connect failed");
++ return sin_unique_ptr();
++ }
++
++ // TODO: Make sa function.
++ socklen_t socklen = sizeof(sockaddr_in);
++
++ auto sa = sin_make();
++
++ if (::getsockname(fd, reinterpret_cast<sockaddr*>(sa.get()), &socklen) != 0) {
++ LT_LOG_FD_ERROR("detect_local_sin_addr: getsockname failed");
++ return sin_unique_ptr();
++ }
++ if (socklen != sizeof(sockaddr_in)) {
++ LT_LOG_FD("detect_local_sin_addr: getsockname failed, invalid socklen");
++ return sin_unique_ptr();
++ }
++
++ LT_LOG_FD_SIN("detect_local_sin_addr: success");
++
++ return sa;
++}
++
++auto detect_local_sin6_addr() -> sin6_unique_ptr {
++ int fd = fd_open(fd_flag_v6only | fd_flag_datagram);
++ if (fd == -1) {
++ LT_LOG_ERROR("detect_local_sin6_addr: open failed");
++ return sin6_unique_ptr();
++ }
++
++ // TODO: Check if unique_ptr works.
++ std::shared_ptr<void> _fd(nullptr, [fd](...){ fd_close(fd); });
++
++ auto connectAddress = sin6_make();
++ connectAddress.get()->sin6_addr.s6_addr[0] = 0x20;
++ connectAddress.get()->sin6_addr.s6_addr[1] = 0x01;
++ connectAddress.get()->sin6_addr.s6_addr[15] = 0x01;
++ connectAddress.get()->sin6_port = 80;
++
++ if (!fd_connect(fd, reinterpret_cast<sockaddr*>(connectAddress.get())) && errno != EINPROGRESS) {
++ LT_LOG_FD_ERROR("detect_local_sin6_addr: connect failed");
++ return sin6_unique_ptr();
++ }
++
++ // TODO: Make sa function.
++ socklen_t socklen = sizeof(sockaddr_in6);
++
++ auto sa = sin6_make();
++
++ if (::getsockname(fd, reinterpret_cast<sockaddr*>(sa.get()), &socklen) != 0) {
++ LT_LOG_FD_ERROR("detect_local_sin6_addr: getsockname failed");
++ return sin6_unique_ptr();
++ }
++ if (socklen != sizeof(sockaddr_in6)) {
++ LT_LOG_FD("detect_local_sin6_addr: getsockname failed, invalid socklen");
++ return sin6_unique_ptr();
++ }
++
++ LT_LOG_FD_SIN6("detect_local_sin6_addr: success");
++
++ return sa;
++}
++
++}
+diff --git a/src/torrent/net/utils.h b/src/torrent/net/utils.h
+new file mode 100755
+index 00000000..1d550c51
+--- /dev/null
++++ b/src/torrent/net/utils.h
+@@ -0,0 +1,9 @@
++#import <torrent/common.h>
++#import <torrent/net/socket_address.h>
++
++namespace torrent {
++
++auto detect_local_sin_addr() -> sin_unique_ptr;
++auto detect_local_sin6_addr() -> sin6_unique_ptr;
++
++}
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index 1bf94107..de3a39ab 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -1,63 +1,29 @@
+-// 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-#include "config.h"
++#import "config.h"
+
+ #define __STDC_FORMAT_MACROS
+
+-#include <iomanip>
+-#include <sstream>
+-#include <rak/functional.h>
+-#include <rak/string_manip.h>
+-
+-#include "net/address_list.h"
+-#include "net/local_addr.h"
+-#include "torrent/connection_manager.h"
+-#include "torrent/download_info.h"
+-#include "torrent/exceptions.h"
+-#include "torrent/http.h"
+-#include "torrent/object_stream.h"
+-#include "torrent/tracker_list.h"
+-#include "torrent/utils/log.h"
+-#include "torrent/utils/option_strings.h"
+-
+-#include "tracker_http.h"
+-
+-#include "globals.h"
+-#include "manager.h"
++#import <iomanip>
++#import <sstream>
++#import <rak/functional.h>
++#import <rak/string_manip.h>
++
++#import "net/address_list.h"
++#import "net/local_addr.h"
++#import "torrent/connection_manager.h"
++#import "torrent/download_info.h"
++#import "torrent/exceptions.h"
++#import "torrent/http.h"
++#import "torrent/net/utils.h"
++#import "torrent/net/socket_address.h"
++#import "torrent/object_stream.h"
++#import "torrent/tracker_list.h"
++#import "torrent/utils/log.h"
++#import "torrent/utils/option_strings.h"
++
++#import "tracker_http.h"
++
++#import "globals.h"
++#import "manager.h"
+
+ #define LT_LOG_TRACKER(log_level, log_fmt, ...) \
+ lt_log_print_info(LOG_TRACKER_##log_level, m_parent->info(), "tracker", "[%u] " log_fmt, group(), __VA_ARGS__);
+@@ -142,19 +108,19 @@ TrackerHttp::send_state(int state) {
+
+ const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address());
+
+- if (!localAddress->is_address_any())
+- s << "&ip=" << localAddress->address_str();
+-
+- if (localAddress->is_address_any() && localAddress->family() == rak::socket_address::pf_inet) {
+- rak::socket_address local_v6;
+- if (get_local_address(rak::socket_address::af_inet6, &local_v6))
+- s << "&ipv6=" << rak::copy_escape_html(local_v6.address_str());
+- }
++ if (localAddress->is_address_any()) {
++ auto ipv4_address = detect_local_sin_addr();
++ auto ipv6_address = detect_local_sin6_addr();
+
+- if (localAddress->is_address_any() && localAddress->family() == rak::socket_address::pf_inet6) {
+- rak::socket_address local_v4;
+- if (get_local_address(rak::socket_address::af_inet, &local_v4))
+- s << "&ipv4=" << local_v4.address_str();
++ if (ipv4_address != nullptr) {
++ s << "&ipv4=" << sin_addr_str(ipv4_address.get());
++ }
++ if (ipv6_address != nullptr) {
++ s << "&ipv6=" << sin6_addr_str(ipv6_address.get());
++ }
++
++ } else {
++ s << "&ip=" << localAddress->address_str();
+ }
+
+ if (info->is_compact())
+diff --git a/test/helpers/network.h b/test/helpers/network.h
+index 6cf2f870..eb188426 100644
+--- a/test/helpers/network.h
++++ b/test/helpers/network.h
+@@ -112,6 +112,7 @@ wrap_ai_get_first_sa(const char* nodename, const char* servname = nullptr, const
+
+ CPPUNIT_ASSERT_MESSAGE(("wrap_ai_get_first_sa: nodename:'" + std::string(nodename) + "'").c_str(),
+ sa != nullptr);
++
+ return sa;
+ }
+
+@@ -121,6 +122,7 @@ wrap_ai_get_first_c_sa(const char* nodename, const char* servname = nullptr, con
+
+ CPPUNIT_ASSERT_MESSAGE(("wrap_ai_get_first_sa: nodename:'" + std::string(nodename) + "'").c_str(),
+ sa != nullptr);
++
+ return torrent::c_sa_unique_ptr(sa.release());
+ }
+
+diff --git a/test/torrent/net/test_fd.cc b/test/torrent/net/test_fd.cc
+index 5e56f0f3..0a00ccd4 100644
+--- a/test/torrent/net/test_fd.cc
++++ b/test/torrent/net/test_fd.cc
+@@ -9,13 +9,22 @@ CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_fd, "torrent/net");
+ void
+ test_fd::test_valid_flags() {
+ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_datagram));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_datagram));
++
+ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_nonblock));
+ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_reuse_address));
+ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only));
+ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v6only));
+
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_datagram | torrent::fd_flag_nonblock));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_datagram | torrent::fd_flag_reuse_address));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_datagram | torrent::fd_flag_v4only));
++ CPPUNIT_ASSERT(torrent::fd_valid_flags(torrent::fd_flag_datagram | torrent::fd_flag_v6only));
++
+ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_v4only | torrent::fd_flag_v6only));
+ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_stream | torrent::fd_flag_v4only | torrent::fd_flag_v6only));
++ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flag_datagram | torrent::fd_flag_v4only | torrent::fd_flag_v6only));
+
+ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags()));
+ CPPUNIT_ASSERT(!torrent::fd_valid_flags(torrent::fd_flags(~torrent::fd_flag_all)));
+diff --git a/test/torrent/net/test_socket_address.cc b/test/torrent/net/test_socket_address.cc
+index 8a1b0c8a..a27b38bc 100644
+--- a/test/torrent/net/test_socket_address.cc
++++ b/test/torrent/net/test_socket_address.cc
+@@ -83,7 +83,7 @@ test_socket_address::test_make() {
+ CPPUNIT_ASSERT(sin6_inet6->sin6_family == AF_INET6);
+ CPPUNIT_ASSERT(sin6_inet6->sin6_port == 0);
+ CPPUNIT_ASSERT(sin6_inet6->sin6_flowinfo == 0);
+- CPPUNIT_ASSERT(compare_sin6_addr(sin6_inet6->sin6_addr, in6_addr{0}));
++ CPPUNIT_ASSERT(compare_sin6_addr(sin6_inet6->sin6_addr, (in6_addr{0})));
+ CPPUNIT_ASSERT(sin6_inet6->sin6_scope_id == 0);
+
+ torrent::sa_unique_ptr sa_unix = torrent::sa_make_unix("");
new file mode 100644
@@ -0,0 +1,85 @@
+From e646ed5427b690b75208510d328457af66b208e8 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Mon, 21 Jun 2021 21:12:56 +0900
+Subject: [PATCH] Added ipv6 options.
+
+---
+ src/torrent/connection_manager.cc | 6 +++++-
+ src/torrent/connection_manager.h | 13 +++++++++++++
+ src/tracker/tracker_http.cc | 13 +++++--------
+ 3 files changed, 23 insertions(+), 9 deletions(-)
+
+diff --git a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc
+index 972dcbfc..ea5efc58 100644
+--- a/src/torrent/connection_manager.cc
++++ b/src/torrent/connection_manager.cc
+@@ -89,7 +89,11 @@ ConnectionManager::ConnectionManager() :
+
+ m_listen(new Listen),
+ m_listen_port(0),
+- m_listen_backlog(SOMAXCONN) {
++ m_listen_backlog(SOMAXCONN),
++
++ m_block_ipv4(false),
++ m_block_ipv6(false),
++ m_prefer_ipv6(false) {
+
+ m_bindAddress = (new rak::socket_address())->c_sockaddr();
+ m_localAddress = (new rak::socket_address())->c_sockaddr();
+diff --git a/src/torrent/connection_manager.h b/src/torrent/connection_manager.h
+index cf43b0bf..09ccdd28 100644
+--- a/src/torrent/connection_manager.h
++++ b/src/torrent/connection_manager.h
+@@ -167,6 +167,15 @@ public:
+ // For internal usage.
+ Listen* listen() { return m_listen; }
+
++ bool is_block_ipv4() const { return m_block_ipv4; }
++ void set_block_ipv4(bool v) { m_block_ipv4 = v; }
++
++ bool is_block_ipv6() const { return m_block_ipv6; }
++ void set_block_ipv6(bool v) { m_block_ipv6 = v; }
++
++ bool is_prefer_ipv6() const { return m_prefer_ipv6; }
++ void set_prefer_ipv6(bool v) { m_prefer_ipv6 = v; }
++
+ private:
+ ConnectionManager(const ConnectionManager&);
+ void operator = (const ConnectionManager&);
+@@ -190,6 +199,10 @@ private:
+ slot_filter_type m_slot_filter;
+ slot_resolver_type m_slot_resolver;
+ slot_throttle_type m_slot_address_throttle;
++
++ bool m_block_ipv4;
++ bool m_block_ipv6;
++ bool m_prefer_ipv6;
+ };
+
+ }
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index de3a39ab..fdbbd58a 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -109,16 +109,13 @@ TrackerHttp::send_state(int state) {
+ const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address());
+
+ if (localAddress->is_address_any()) {
+- auto ipv4_address = detect_local_sin_addr();
+- auto ipv6_address = detect_local_sin6_addr();
++ if (manager->connection_manager()->is_prefer_ipv6()) {
++ auto ipv6_address = detect_local_sin6_addr();
+
+- if (ipv4_address != nullptr) {
+- s << "&ipv4=" << sin_addr_str(ipv4_address.get());
++ if (ipv6_address != nullptr) {
++ s << "&ip=" << sin6_addr_str(ipv6_address.get());
++ }
+ }
+- if (ipv6_address != nullptr) {
+- s << "&ipv6=" << sin6_addr_str(ipv6_address.get());
+- }
+-
+ } else {
+ s << "&ip=" << localAddress->address_str();
+ }
new file mode 100644
@@ -0,0 +1,444 @@
+From 54caef85baca975e0be30b4f3f6de01db19c8e19 Mon Sep 17 00:00:00 2001
+From: rakshasa <sundell.software@gmail.com>
+Date: Mon, 21 Jun 2021 21:28:02 +0900
+Subject: [PATCH] Removed obsolete files.
+
+---
+ src/Makefile.am | 2 -
+ src/net/local_addr.cc | 329 ------------------------------------
+ src/net/local_addr.h | 64 -------
+ src/tracker/tracker_http.cc | 1 -
+ 4 files changed, 396 deletions(-)
+ delete mode 100644 src/net/local_addr.cc
+ delete mode 100644 src/net/local_addr.h
+
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 95e6a7ae..925e7e15 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -76,8 +76,6 @@ libtorrent_other_la_SOURCES = \
+ net/address_list.cc \
+ net/address_list.h \
+ net/data_buffer.h \
+- net/local_addr.cc \
+- net/local_addr.h \
+ net/listen.cc \
+ net/listen.h \
+ net/protocol_buffer.h \
+diff --git a/src/net/local_addr.cc b/src/net/local_addr.cc
+deleted file mode 100644
+index 0c7116cb..00000000
+--- a/src/net/local_addr.cc
++++ /dev/null
+@@ -1,329 +0,0 @@
+-// libTorrent - BitTorrent library
+-// Copyright (C) 2005-2007, 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-// TODO: Remove.
+-
+-#include "config.h"
+-
+-#include <stdio.h>
+-#include <rak/socket_address.h>
+-#include <sys/types.h>
+-#include <errno.h>
+-
+-#ifdef __linux__
+-#include <linux/netlink.h>
+-#include <linux/rtnetlink.h>
+-#endif
+-
+-#include "torrent/exceptions.h"
+-#include "socket_fd.h"
+-#include "local_addr.h"
+-
+-namespace torrent {
+-
+-#ifdef __linux__
+-
+-namespace {
+-
+-// IPv4 priority, from highest to lowest:
+-//
+-// 1. Everything else (global address)
+-// 2. Private address space (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
+-// 3. Empty/INADDR_ANY (0.0.0.0)
+-// 4. Link-local address (169.254.0.0/16)
+-// 5. Localhost (127.0.0.0/8)
+-int
+-get_priority_ipv4(const in_addr& addr) {
+- if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x7f000000U)) {
+- return 5;
+- }
+- if ((addr.s_addr & htonl(0xffff0000U)) == htonl(0xa9fe0000U)) {
+- return 4;
+- }
+- if (addr.s_addr == htonl(0)) {
+- return 3;
+- }
+- if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x0a000000U) ||
+- (addr.s_addr & htonl(0xfff00000U)) == htonl(0xac100000U) ||
+- (addr.s_addr & htonl(0xffff0000U)) == htonl(0xc0a80000U)) {
+- return 2;
+- }
+- return 1;
+-}
+-
+-// IPv6 priority, from highest to lowest:
+-//
+-// 1. Global address (2000::/16 not in 6to4 or Teredo)
+-// 2. 6to4 (2002::/16)
+-// 3. Teredo (2001::/32)
+-// 4. Empty/INADDR_ANY (::)
+-// 5. Everything else (link-local, ULA, etc.)
+-int
+-get_priority_ipv6(const in6_addr& addr) {
+- const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(addr.s6_addr);
+- if (addr32[0] == htonl(0) &&
+- addr32[1] == htonl(0) &&
+- addr32[2] == htonl(0) &&
+- addr32[3] == htonl(0)) {
+- return 4;
+- }
+- if (addr32[0] == htonl(0x20010000)) {
+- return 3;
+- }
+- if ((addr32[0] & htonl(0xffff0000)) == htonl(0x20020000)) {
+- return 2;
+- }
+- if ((addr32[0] & htonl(0xe0000000)) == htonl(0x20000000)) {
+- return 1;
+- }
+- return 5;
+-}
+-
+-int
+-get_priority(const rak::socket_address& addr) {
+- switch (addr.family()) {
+- case AF_INET:
+- return get_priority_ipv4(addr.c_sockaddr_inet()->sin_addr);
+- case AF_INET6:
+- return get_priority_ipv6(addr.c_sockaddr_inet6()->sin6_addr);
+- default:
+- throw torrent::internal_error("Unknown address family given to compare");
+- }
+-}
+-
+-}
+-
+-// Linux-specific implementation that understands how to filter away
+-// understands how to filter away secondary addresses.
+-bool get_local_address(sa_family_t family, rak::socket_address *address) {
+- rak::socket_address best_addr;
+- switch (family) {
+- case AF_INET:
+- best_addr.sa_inet()->clear();
+- break;
+- case AF_INET6:
+- best_addr.sa_inet6()->clear();
+- break;
+- default:
+- throw torrent::internal_error("Unknown address family given to get_local_address");
+- }
+-
+- // The bottom bit of the priority is used to hold if the address is
+- // a secondary address (e.g. with IPv6 privacy extensions) or not;
+- // secondary addresses have lower priority (higher number).
+- int best_addr_pri = get_priority(best_addr) * 2;
+-
+- // Get all the addresses via Linux' netlink interface.
+- int fd = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+- if (fd == -1) {
+- return false;
+- }
+-
+- struct sockaddr_nl nladdr;
+- memset(&nladdr, 0, sizeof(nladdr));
+- nladdr.nl_family = AF_NETLINK;
+- if (::bind(fd, (sockaddr *)&nladdr, sizeof(nladdr))) {
+- ::close(fd);
+- return false;
+- }
+-
+- const int seq_no = 1;
+- struct {
+- nlmsghdr nh;
+- rtgenmsg g;
+- } req;
+- memset(&req, 0, sizeof(req));
+-
+- req.nh.nlmsg_len = sizeof(req);
+- req.nh.nlmsg_type = RTM_GETADDR;
+- req.nh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+- req.nh.nlmsg_pid = getpid();
+- req.nh.nlmsg_seq = seq_no;
+- req.g.rtgen_family = AF_UNSPEC;
+-
+- int ret;
+- do {
+- ret = ::sendto(fd, &req, sizeof(req), 0, (sockaddr *)&nladdr, sizeof(nladdr));
+- } while (ret == -1 && errno == EINTR);
+-
+- if (ret == -1) {
+- ::close(fd);
+- return false;
+- }
+-
+- bool done = false;
+- do {
+- char buf[4096];
+- socklen_t len = sizeof(nladdr);
+- do {
+- ret = ::recvfrom(fd, buf, sizeof(buf), 0, (sockaddr *)&nladdr, &len);
+- } while (ret == -1 && errno == EINTR);
+-
+- if (ret < 0) {
+- ::close(fd);
+- return false;
+- }
+-
+- for (const nlmsghdr *nlmsg = (const nlmsghdr *)buf;
+- NLMSG_OK(nlmsg, ret);
+- nlmsg = NLMSG_NEXT(nlmsg, ret)) {
+- if (nlmsg->nlmsg_seq != seq_no)
+- continue;
+- if (nlmsg->nlmsg_type == NLMSG_DONE) {
+- done = true;
+- break;
+- }
+- if (nlmsg->nlmsg_type == NLMSG_ERROR) {
+- ::close(fd);
+- return false;
+- }
+- if (nlmsg->nlmsg_type != RTM_NEWADDR)
+- continue;
+-
+- const ifaddrmsg *ifa = (const ifaddrmsg *)NLMSG_DATA(nlmsg);
+-
+- if (ifa->ifa_family != family)
+- continue;
+-
+-#ifdef IFA_F_OPTIMISTIC
+- if ((ifa->ifa_flags & IFA_F_OPTIMISTIC) != 0)
+- continue;
+-#endif
+-#ifdef IFA_F_DADFAILED
+- if ((ifa->ifa_flags & IFA_F_DADFAILED) != 0)
+- continue;
+-#endif
+-#ifdef IFA_F_DEPRECATED
+- if ((ifa->ifa_flags & IFA_F_DEPRECATED) != 0)
+- continue;
+-#endif
+-#ifdef IFA_F_TENTATIVE
+- if ((ifa->ifa_flags & IFA_F_TENTATIVE) != 0)
+- continue;
+-#endif
+-
+- // Since there can be point-to-point links on the machine, we need to keep
+- // track of the addresses we've seen for this interface; if we see both
+- // IFA_LOCAL and IFA_ADDRESS for an interface, keep only the IFA_LOCAL.
+- rak::socket_address this_addr;
+- bool seen_addr = false;
+- int plen = IFA_PAYLOAD(nlmsg);
+- for (const rtattr *rta = IFA_RTA(ifa);
+- RTA_OK(rta, plen);
+- rta = RTA_NEXT(rta, plen)) {
+- if (rta->rta_type != IFA_LOCAL &&
+- rta->rta_type != IFA_ADDRESS) {
+- continue;
+- }
+- if (rta->rta_type == IFA_ADDRESS && seen_addr) {
+- continue;
+- }
+- seen_addr = true;
+- switch (ifa->ifa_family) {
+- case AF_INET:
+- this_addr.sa_inet()->clear();
+- this_addr.sa_inet()->set_address(*(const in_addr *)RTA_DATA(rta));
+- break;
+- case AF_INET6:
+- this_addr.sa_inet6()->clear();
+- this_addr.sa_inet6()->set_address(*(const in6_addr *)RTA_DATA(rta));
+- break;
+- }
+- }
+- if (!seen_addr)
+- continue;
+-
+- int this_addr_pri = get_priority(this_addr) * 2;
+- if ((ifa->ifa_flags & IFA_F_SECONDARY) == IFA_F_SECONDARY) {
+- ++this_addr_pri;
+- }
+-
+- if (this_addr_pri < best_addr_pri) {
+- best_addr = this_addr;
+- best_addr_pri = this_addr_pri;
+- }
+- }
+- } while (!done);
+-
+- ::close(fd);
+- if (!best_addr.is_address_any()) {
+- *address = best_addr;
+- return true;
+- } else {
+- return false;
+- }
+-}
+-
+-#else
+-
+-// Generic POSIX variant.
+-bool
+-get_local_address(sa_family_t family, rak::socket_address *address) {
+- SocketFd sock;
+- if (!sock.open_datagram()) {
+- return false;
+- }
+-
+- rak::socket_address dummy_dest;
+- dummy_dest.clear();
+-
+- switch (family) {
+- case rak::socket_address::af_inet:
+- dummy_dest.set_address_c_str("4.0.0.0");
+- break;
+- case rak::socket_address::af_inet6:
+- dummy_dest.set_address_c_str("2001:1::");
+- break;
+- default:
+- throw internal_error("Unknown address family");
+- }
+-
+- dummy_dest.set_port(80);
+-
+- if (!sock.connect(dummy_dest)) {
+- sock.close();
+- return false;
+- }
+-
+- bool ret = sock.getsockname(address);
+- sock.close();
+-
+- return ret;
+-}
+-
+-#endif
+-
+-}
+diff --git a/src/net/local_addr.h b/src/net/local_addr.h
+deleted file mode 100644
+index 43bc8206..00000000
+--- a/src/net/local_addr.h
++++ /dev/null
+@@ -1,64 +0,0 @@
+-// libTorrent - BitTorrent library
+-// Copyright (C) 2005-2007, 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 <jaris@ifi.uio.no>
+-//
+-// Skomakerveien 33
+-// 3185 Skoppum, NORWAY
+-
+-// A routine to get a local IP address that can be presented to a tracker.
+-// (Does not use UPnP etc., so will not understand NAT.)
+-// On a machine with multiple network cards, address selection can be a
+-// complex process, and in general what's selected is a source/destination
+-// address pair. However, this routine will give an approximation that will
+-// be good enough for most purposes and users.
+-
+-#ifndef LIBTORRENT_NET_LOCAL_ADDR_H
+-#define LIBTORRENT_NET_LOCAL_ADDR_H
+-
+-#include <unistd.h>
+-
+-namespace rak {
+- class socket_address;
+-}
+-
+-namespace torrent {
+-
+-// Note: family must currently be rak::af_inet or rak::af_inet6
+-// (rak::af_unspec won't do); anything else will throw an exception.
+-// Returns false if no address of the given family could be found,
+-// either because there are none, or because something went wrong in
+-// the process (e.g., no free file descriptors).
+-bool get_local_address(sa_family_t family, rak::socket_address *address);
+-
+-}
+-
+-#endif /* LIBTORRENT_NET_LOCAL_ADDR_H */
+diff --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
+index fdbbd58a..22c409a1 100644
+--- a/src/tracker/tracker_http.cc
++++ b/src/tracker/tracker_http.cc
+@@ -8,7 +8,6 @@
+ #import <rak/string_manip.h>
+
+ #import "net/address_list.h"
+-#import "net/local_addr.h"
+ #import "torrent/connection_manager.h"
+ #import "torrent/download_info.h"
+ #import "torrent/exceptions.h"
new file mode 100644
@@ -0,0 +1,2979 @@
+From 4cfe9b1dc1349ad167969d6cf87f557600f34a2e Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Sat, 7 Aug 2021 17:49:35 +0900
+Subject: [PATCH] Updated and cleaned up automake. (#224)
+
+---
+ INSTALL | 371 +++++++----
+ autogen.sh | 51 --
+ configure.ac | 32 +-
+ scripts/ax_check_zlib.m4 | 11 +-
+ scripts/ax_cxx_compile_stdcxx.m4 | 962 ++++++++++++++++++++++++++++
+ scripts/ax_cxx_compile_stdcxx_0x.m4 | 106 ---
+ scripts/ax_cxx_compile_stdcxx_11.m4 | 147 -----
+ scripts/ax_pthread.m4 | 453 +++++++++----
+ scripts/checks.m4 | 104 ++-
+ scripts/common.m4 | 29 +-
+ scripts/rak_compiler.m4 | 6 +-
+ scripts/rak_cxx.m4 | 14 -
+ 12 files changed, 1642 insertions(+), 644 deletions(-)
+ delete mode 100755 autogen.sh
+ mode change 100644 => 100755 scripts/ax_check_zlib.m4
+ create mode 100755 scripts/ax_cxx_compile_stdcxx.m4
+ delete mode 100644 scripts/ax_cxx_compile_stdcxx_0x.m4
+ delete mode 100644 scripts/ax_cxx_compile_stdcxx_11.m4
+ mode change 100644 => 100755 scripts/ax_pthread.m4
+ delete mode 100644 scripts/rak_cxx.m4
+
+diff --git a/INSTALL b/INSTALL
+index 54caf7c1..8865734f 100644
+--- a/INSTALL
++++ b/INSTALL
+@@ -1,81 +1,109 @@
+-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
++Installation Instructions
++*************************
++
++ Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software
+ Foundation, Inc.
+
+- This file is free documentation; the Free Software Foundation gives
+-unlimited permission to copy, distribute and modify it.
++ Copying and distribution of this file, with or without modification,
++are permitted in any medium without royalty provided the copyright
++notice and this notice are preserved. This file is offered as-is,
++without warranty of any kind.
+
+ Basic Installation
+ ==================
+
+- These are generic installation instructions.
++ Briefly, the shell command './configure && make && make install'
++should configure, build, and install this package. The following
++more-detailed instructions are generic; see the 'README' file for
++instructions specific to this package. Some packages provide this
++'INSTALL' file but do not implement all of the features documented
++below. The lack of an optional feature in a given package is not
++necessarily a bug. More recommendations for GNU packages can be found
++in *note Makefile Conventions: (standards)Makefile Conventions.
+
+- The `configure' shell script attempts to guess correct values for
++ The 'configure' shell script attempts to guess correct values for
+ various system-dependent variables used during compilation. It uses
+-those values to create a `Makefile' in each directory of the package.
+-It may also create one or more `.h' files containing system-dependent
+-definitions. Finally, it creates a shell script `config.status' that
++those values to create a 'Makefile' in each directory of the package.
++It may also create one or more '.h' files containing system-dependent
++definitions. Finally, it creates a shell script 'config.status' that
+ you can run in the future to recreate the current configuration, and a
+-file `config.log' containing compiler output (useful mainly for
+-debugging `configure').
++file 'config.log' containing compiler output (useful mainly for
++debugging 'configure').
+
+- It can also use an optional file (typically called `config.cache'
+-and enabled with `--cache-file=config.cache' or simply `-C') that saves
+-the results of its tests to speed up reconfiguring. (Caching is
+-disabled by default to prevent problems with accidental use of stale
+-cache files.)
++ It can also use an optional file (typically called 'config.cache' and
++enabled with '--cache-file=config.cache' or simply '-C') that saves the
++results of its tests to speed up reconfiguring. Caching is disabled by
++default to prevent problems with accidental use of stale cache files.
+
+ If you need to do unusual things to compile the package, please try
+-to figure out how `configure' could check whether to do them, and mail
+-diffs or instructions to the address given in the `README' so they can
++to figure out how 'configure' could check whether to do them, and mail
++diffs or instructions to the address given in the 'README' so they can
+ be considered for the next release. If you are using the cache, and at
+-some point `config.cache' contains results you don't want to keep, you
++some point 'config.cache' contains results you don't want to keep, you
+ may remove or edit it.
+
+- The file `configure.ac' (or `configure.in') is used to create
+-`configure' by a program called `autoconf'. You only need
+-`configure.ac' if you want to change it or regenerate `configure' using
+-a newer version of `autoconf'.
++ The file 'configure.ac' (or 'configure.in') is used to create
++'configure' by a program called 'autoconf'. You need 'configure.ac' if
++you want to change it or regenerate 'configure' using a newer version of
++'autoconf'.
++
++ The simplest way to compile this package is:
+
+-The simplest way to compile this package is:
++ 1. 'cd' to the directory containing the package's source code and type
++ './configure' to configure the package for your system.
+
+- 1. `cd' to the directory containing the package's source code and type
+- `./configure' to configure the package for your system. If you're
+- using `csh' on an old version of System V, you might need to type
+- `sh ./configure' instead to prevent `csh' from trying to execute
+- `configure' itself.
++ Running 'configure' might take a while. While running, it prints
++ some messages telling which features it is checking for.
+
+- Running `configure' takes awhile. While running, it prints some
+- messages telling which features it is checking for.
++ 2. Type 'make' to compile the package.
+
+- 2. Type `make' to compile the package.
++ 3. Optionally, type 'make check' to run any self-tests that come with
++ the package, generally using the just-built uninstalled binaries.
+
+- 3. Optionally, type `make check' to run any self-tests that come with
+- the package.
++ 4. Type 'make install' to install the programs and any data files and
++ documentation. When installing into a prefix owned by root, it is
++ recommended that the package be configured and built as a regular
++ user, and only the 'make install' phase executed with root
++ privileges.
+
+- 4. Type `make install' to install the programs and any data files and
+- documentation.
++ 5. Optionally, type 'make installcheck' to repeat any self-tests, but
++ this time using the binaries in their final installed location.
++ This target does not install anything. Running this target as a
++ regular user, particularly if the prior 'make install' required
++ root privileges, verifies that the installation completed
++ correctly.
+
+- 5. You can remove the program binaries and object files from the
+- source code directory by typing `make clean'. To also remove the
+- files that `configure' created (so you can compile the package for
+- a different kind of computer), type `make distclean'. There is
+- also a `make maintainer-clean' target, but that is intended mainly
++ 6. You can remove the program binaries and object files from the
++ source code directory by typing 'make clean'. To also remove the
++ files that 'configure' created (so you can compile the package for
++ a different kind of computer), type 'make distclean'. There is
++ also a 'make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
++ 7. Often, you can also type 'make uninstall' to remove the installed
++ files again. In practice, not all packages have tested that
++ uninstallation works correctly, even though it is required by the
++ GNU Coding Standards.
++
++ 8. Some packages, particularly those that use Automake, provide 'make
++ distcheck', which can by used by developers to test that all other
++ targets like 'make install' and 'make uninstall' work correctly.
++ This target is generally not run by end users.
++
+ Compilers and Options
+ =====================
+
+ Some systems require unusual options for compilation or linking that
+-the `configure' script does not know about. Run `./configure --help'
++the 'configure' script does not know about. Run './configure --help'
+ for details on some of the pertinent environment variables.
+
+- You can give `configure' initial values for configuration parameters
+-by setting variables in the command line or in the environment. Here
+-is an example:
++ You can give 'configure' initial values for configuration parameters
++by setting variables in the command line or in the environment. Here is
++an example:
+
+- ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
++ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+@@ -84,146 +112,257 @@ Compiling For Multiple Architectures
+
+ You can compile the package for more than one kind of computer at the
+ same time, by placing the object files for each architecture in their
+-own directory. To do this, you must use a version of `make' that
+-supports the `VPATH' variable, such as GNU `make'. `cd' to the
++own directory. To do this, you can use GNU 'make'. 'cd' to the
+ directory where you want the object files and executables to go and run
+-the `configure' script. `configure' automatically checks for the
+-source code in the directory that `configure' is in and in `..'.
++the 'configure' script. 'configure' automatically checks for the source
++code in the directory that 'configure' is in and in '..'. This is known
++as a "VPATH" build.
++
++ With a non-GNU 'make', it is safer to compile the package for one
++architecture at a time in the source code directory. After you have
++installed the package for one architecture, use 'make distclean' before
++reconfiguring for another architecture.
++
++ On MacOS X 10.5 and later systems, you can create libraries and
++executables that work on multiple system types--known as "fat" or
++"universal" binaries--by specifying multiple '-arch' options to the
++compiler but only a single '-arch' option to the preprocessor. Like
++this:
++
++ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
++ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
++ CPP="gcc -E" CXXCPP="g++ -E"
+
+- If you have to use a `make' that does not support the `VPATH'
+-variable, you have to compile the package for one architecture at a
+-time in the source code directory. After you have installed the
+-package for one architecture, use `make distclean' before reconfiguring
+-for another architecture.
++ This is not guaranteed to produce working output in all cases, you
++may have to build one architecture at a time and combine the results
++using the 'lipo' tool if you have problems.
+
+ Installation Names
+ ==================
+
+- By default, `make install' will install the package's files in
+-`/usr/local/bin', `/usr/local/man', etc. You can specify an
+-installation prefix other than `/usr/local' by giving `configure' the
+-option `--prefix=PATH'.
++ By default, 'make install' installs the package's commands under
++'/usr/local/bin', include files under '/usr/local/include', etc. You
++can specify an installation prefix other than '/usr/local' by giving
++'configure' the option '--prefix=PREFIX', where PREFIX must be an
++absolute file name.
+
+ You can specify separate installation prefixes for
+ architecture-specific files and architecture-independent files. If you
+-give `configure' the option `--exec-prefix=PATH', the package will use
+-PATH as the prefix for installing programs and libraries.
+-Documentation and other data files will still use the regular prefix.
++pass the option '--exec-prefix=PREFIX' to 'configure', the package uses
++PREFIX as the prefix for installing programs and libraries.
++Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+-options like `--bindir=PATH' to specify different values for particular
+-kinds of files. Run `configure --help' for a list of the directories
+-you can set and what kinds of files go in them.
+-
+- If the package supports it, you can cause programs to be installed
+-with an extra prefix or suffix on their names by giving `configure' the
+-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
++options like '--bindir=DIR' to specify different values for particular
++kinds of files. Run 'configure --help' for a list of the directories
++you can set and what kinds of files go in them. In general, the default
++for these options is expressed in terms of '${prefix}', so that
++specifying just '--prefix' will affect all of the other directory
++specifications that were not explicitly provided.
++
++ The most portable way to affect installation locations is to pass the
++correct locations to 'configure'; however, many packages provide one or
++both of the following shortcuts of passing variable assignments to the
++'make install' command line to change installation locations without
++having to reconfigure or recompile.
++
++ The first method involves providing an override variable for each
++affected directory. For example, 'make install
++prefix=/alternate/directory' will choose an alternate location for all
++directory configuration variables that were expressed in terms of
++'${prefix}'. Any directories that were specified during 'configure',
++but not in terms of '${prefix}', must each be overridden at install time
++for the entire installation to be relocated. The approach of makefile
++variable overrides for each directory variable is required by the GNU
++Coding Standards, and ideally causes no recompilation. However, some
++platforms have known limitations with the semantics of shared libraries
++that end up requiring recompilation when using this method, particularly
++noticeable in packages that use GNU Libtool.
++
++ The second method involves providing the 'DESTDIR' variable. For
++example, 'make install DESTDIR=/alternate/directory' will prepend
++'/alternate/directory' before all installation names. The approach of
++'DESTDIR' overrides is not required by the GNU Coding Standards, and
++does not work on platforms that have drive letters. On the other hand,
++it does better at avoiding recompilation issues, and works well even
++when some directory options were not specified in terms of '${prefix}'
++at 'configure' time.
+
+ Optional Features
+ =================
+
+- Some packages pay attention to `--enable-FEATURE' options to
+-`configure', where FEATURE indicates an optional part of the package.
+-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+-is something like `gnu-as' or `x' (for the X Window System). The
+-`README' should mention any `--enable-' and `--with-' options that the
++ If the package supports it, you can cause programs to be installed
++with an extra prefix or suffix on their names by giving 'configure' the
++option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'.
++
++ Some packages pay attention to '--enable-FEATURE' options to
++'configure', where FEATURE indicates an optional part of the package.
++They may also pay attention to '--with-PACKAGE' options, where PACKAGE
++is something like 'gnu-as' or 'x' (for the X Window System). The
++'README' should mention any '--enable-' and '--with-' options that the
+ package recognizes.
+
+- For packages that use the X Window System, `configure' can usually
++ For packages that use the X Window System, 'configure' can usually
+ find the X include and library files automatically, but if it doesn't,
+-you can use the `configure' options `--x-includes=DIR' and
+-`--x-libraries=DIR' to specify their locations.
++you can use the 'configure' options '--x-includes=DIR' and
++'--x-libraries=DIR' to specify their locations.
++
++ Some packages offer the ability to configure how verbose the
++execution of 'make' will be. For these packages, running './configure
++--enable-silent-rules' sets the default to minimal output, which can be
++overridden with 'make V=1'; while running './configure
++--disable-silent-rules' sets the default to verbose, which can be
++overridden with 'make V=0'.
++
++Particular systems
++==================
++
++ On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC
++is not installed, it is recommended to use the following options in
++order to use an ANSI C compiler:
++
++ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
++
++and if that doesn't work, install pre-built binaries of GCC for HP-UX.
++
++ HP-UX 'make' updates targets which have the same time stamps as their
++prerequisites, which makes it generally unusable when shipped generated
++files such as 'configure' are involved. Use GNU 'make' instead.
++
++ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
++parse its '<wchar.h>' header file. The option '-nodtk' can be used as a
++workaround. If GNU CC is not installed, it is therefore recommended to
++try
++
++ ./configure CC="cc"
++
++and if that doesn't work, try
++
++ ./configure CC="cc -nodtk"
++
++ On Solaris, don't put '/usr/ucb' early in your 'PATH'. This
++directory contains several dysfunctional programs; working variants of
++these programs are available in '/usr/bin'. So, if you need '/usr/ucb'
++in your 'PATH', put it _after_ '/usr/bin'.
++
++ On Haiku, software installed for all users goes in '/boot/common',
++not '/usr/local'. It is recommended to use the following options:
++
++ ./configure --prefix=/boot/common
+
+ Specifying the System Type
+ ==========================
+
+- There may be some features `configure' cannot figure out
++ There may be some features 'configure' cannot figure out
+ automatically, but needs to determine by the type of machine the package
+ will run on. Usually, assuming the package is built to be run on the
+-_same_ architectures, `configure' can figure that out, but if it prints
++_same_ architectures, 'configure' can figure that out, but if it prints
+ a message saying it cannot guess the machine type, give it the
+-`--build=TYPE' option. TYPE can either be a short name for the system
+-type, such as `sun4', or a canonical name which has the form:
++'--build=TYPE' option. TYPE can either be a short name for the system
++type, such as 'sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+ where SYSTEM can have one of these forms:
+
+- OS KERNEL-OS
++ OS
++ KERNEL-OS
+
+- See the file `config.sub' for the possible values of each field. If
+-`config.sub' isn't included in this package, then this package doesn't
++ See the file 'config.sub' for the possible values of each field. If
++'config.sub' isn't included in this package, then this package doesn't
+ need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+-use the `--target=TYPE' option to select the type of system they will
++use the option '--target=TYPE' to select the type of system they will
+ produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+ platform different from the build platform, you should specify the
+ "host" platform (i.e., that on which the generated programs will
+-eventually be run) with `--host=TYPE'.
++eventually be run) with '--host=TYPE'.
+
+ Sharing Defaults
+ ================
+
+- If you want to set default values for `configure' scripts to share,
+-you can create a site shell script called `config.site' that gives
+-default values for variables like `CC', `cache_file', and `prefix'.
+-`configure' looks for `PREFIX/share/config.site' if it exists, then
+-`PREFIX/etc/config.site' if it exists. Or, you can set the
+-`CONFIG_SITE' environment variable to the location of the site script.
+-A warning: not all `configure' scripts look for a site script.
++ If you want to set default values for 'configure' scripts to share,
++you can create a site shell script called 'config.site' that gives
++default values for variables like 'CC', 'cache_file', and 'prefix'.
++'configure' looks for 'PREFIX/share/config.site' if it exists, then
++'PREFIX/etc/config.site' if it exists. Or, you can set the
++'CONFIG_SITE' environment variable to the location of the site script.
++A warning: not all 'configure' scripts look for a site script.
+
+ Defining Variables
+ ==================
+
+ Variables not defined in a site shell script can be set in the
+-environment passed to `configure'. However, some packages may run
++environment passed to 'configure'. However, some packages may run
+ configure again during the build, and the customized values of these
+ variables may be lost. In order to avoid this problem, you should set
+-them in the `configure' command line, using `VAR=value'. For example:
++them in the 'configure' command line, using 'VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+-will cause the specified gcc to be used as the C compiler (unless it is
++causes the specified 'gcc' to be used as the C compiler (unless it is
+ overridden in the site shell script).
+
+-`configure' Invocation
++Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an
++Autoconf limitation. Until the limitation is lifted, you can use this
++workaround:
++
++ CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
++
++'configure' Invocation
+ ======================
+
+- `configure' recognizes the following options to control how it
++ 'configure' recognizes the following options to control how it
+ operates.
+
+-`--help'
+-`-h'
+- Print a summary of the options to `configure', and exit.
++'--help'
++'-h'
++ Print a summary of all of the options to 'configure', and exit.
++
++'--help=short'
++'--help=recursive'
++ Print a summary of the options unique to this package's
++ 'configure', and exit. The 'short' variant lists options used only
++ in the top level, while the 'recursive' variant lists options also
++ present in any nested packages.
+
+-`--version'
+-`-V'
+- Print the version of Autoconf used to generate the `configure'
++'--version'
++'-V'
++ Print the version of Autoconf used to generate the 'configure'
+ script, and exit.
+
+-`--cache-file=FILE'
++'--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+- traditionally `config.cache'. FILE defaults to `/dev/null' to
++ traditionally 'config.cache'. FILE defaults to '/dev/null' to
+ disable caching.
+
+-`--config-cache'
+-`-C'
+- Alias for `--cache-file=config.cache'.
++'--config-cache'
++'-C'
++ Alias for '--cache-file=config.cache'.
+
+-`--quiet'
+-`--silent'
+-`-q'
++'--quiet'
++'--silent'
++'-q'
+ Do not print messages saying which checks are being made. To
+- suppress all normal output, redirect it to `/dev/null' (any error
++ suppress all normal output, redirect it to '/dev/null' (any error
+ messages will still be shown).
+
+-`--srcdir=DIR'
++'--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+- `configure' can determine that directory automatically.
++ 'configure' can determine that directory automatically.
++
++'--prefix=DIR'
++ Use DIR as the installation prefix. *note Installation Names:: for
++ more details, including other options available for fine-tuning the
++ installation locations.
+
+-`configure' also accepts some other, not widely useful, options. Run
+-`configure --help' for more details.
++'--no-create'
++'-n'
++ Run the configure checks, but stop before creating any output
++ files.
+
++'configure' also accepts some other, not widely useful, options. Run
++'configure --help' for more details.
+diff --git a/autogen.sh b/autogen.sh
+deleted file mode 100755
+index 6def96dd..00000000
+--- a/autogen.sh
++++ /dev/null
+@@ -1,51 +0,0 @@
+-#! /bin/sh
+-
+-echo aclocal...
+-(aclocal --version) < /dev/null > /dev/null 2>&1 || {
+- echo aclocal not found
+- exit 1
+-}
+-
+-aclocal -I ./scripts -I . ${ACLOCAL_FLAGS} || exit 1
+-
+-echo autoheader...
+-(autoheader --version) < /dev/null > /dev/null 2>&1 || {
+- echo autoheader not found
+- exit 1
+-}
+-
+-autoheader || exit 1
+-
+-echo -n "libtoolize... "
+-if ( (glibtoolize --version) < /dev/null > /dev/null 2>&1 ); then
+- echo "using glibtoolize"
+- glibtoolize --automake --copy --force || exit 1
+-
+-elif ( (libtoolize --version) < /dev/null > /dev/null 2>&1 ) ; then
+- echo "using libtoolize"
+- libtoolize --automake --copy --force || exit 1
+-
+-else
+- echo "libtoolize nor glibtoolize not found"
+- exit 1
+-fi
+-
+-echo automake...
+-(automake --version) < /dev/null > /dev/null 2>&1 || {
+- echo automake not found
+- exit 1
+-}
+-
+-automake --add-missing --copy --foreign || exit 1
+-
+-echo autoconf...
+-(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+- echo autoconf not found
+- exit 1
+-}
+-
+-autoconf || exit 1
+-
+-echo ready to configure
+-
+-exit 0
+diff --git a/configure.ac b/configure.ac
+index a4f051e4..453e2936 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1,10 +1,14 @@
+-AC_INIT(libtorrent, 0.13.8, sundell.software@gmail.com)
++AC_INIT([[libtorrent]],[[0.13.8]],[[sundell.software@gmail.com]])
+
+-LT_INIT([disable-static])
++AC_CONFIG_HEADERS([config.h])
++AC_CONFIG_MACRO_DIRS([scripts])
++AM_INIT_AUTOMAKE([serial-tests subdir-objects foreign])
++
++LT_INIT([[disable-static]])
+
+ dnl Find a better way to do this
+-AC_DEFINE(PEER_NAME, "-lt0D80-", Identifier that is part of the default peer id)
+-AC_DEFINE(PEER_VERSION, "lt\x0D\x80", 4 byte client and version identifier for DHT)
++AC_DEFINE([[PEER_NAME]], [["-lt0D80-"]], [[Identifier that is part of the default peer id.]])
++AC_DEFINE([[PEER_VERSION]], [["lt\x0D\x80"]], [[4 byte client and version identifier for DHT.]])
+
+ LIBTORRENT_CURRENT=21
+ LIBTORRENT_REVISION=0
+@@ -17,9 +21,6 @@ AC_SUBST(LIBTORRENT_CURRENT)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_INFO)
+ AC_SUBST(LIBTORRENT_INTERFACE_VERSION_NO)
+
+-AM_INIT_AUTOMAKE([serial-tests subdir-objects])
+-AC_CONFIG_HEADERS(config.h)
+-
+ AC_PROG_CXX
+ AC_SYS_LARGEFILE
+
+@@ -29,14 +30,14 @@ AC_C_BIGENDIAN(
+ AC_MSG_ERROR([Could not determine endianness])
+ )
+
++AX_CXX_COMPILE_STDCXX(14, noext, mandatory)
++
+ RAK_CHECK_CFLAGS
+ RAK_CHECK_CXXFLAGS
+ RAK_ENABLE_DEBUG
+ RAK_ENABLE_EXTRA_DEBUG
+ RAK_ENABLE_WERROR
+
+-RAK_CHECK_CXX11
+-
+ TORRENT_ENABLE_ALIGNED
+ TORRENT_ENABLE_INTERRUPT_SOCKET
+
+@@ -55,7 +56,7 @@ TORRENT_WITHOUT_STATFS
+ TORRENT_WITH_INOTIFY
+
+ AC_ARG_ENABLE(attribute-visibility,
+- AC_HELP_STRING([--disable-attribute-visibility], [disable symbol visibility attribute [[default=enable]]]),
++ AS_HELP_STRING([--disable-attribute-visibility],[disable symbol visibility attribute [[default=enable]]]),
+ [
+ if test "$enableval" = "yes"; then
+ CC_ATTRIBUTE_VISIBILITY
+@@ -65,7 +66,7 @@ AC_ARG_ENABLE(attribute-visibility,
+ ])
+
+ AC_ARG_ENABLE(execinfo,
+- AC_HELP_STRING([--disable-execinfo], [disable libexecinfo [[default=enable]]]),
++ AS_HELP_STRING([--disable-execinfo],[disable libexecinfo [[default=enable]]]),
+ [
+ if test "$enableval" = "yes"; then
+ AX_EXECINFO
+@@ -109,12 +110,13 @@ CC_ATTRIBUTE_UNUSED(
+ AC_DEFINE([__UNUSED], [], [Null-wrapper if unused attribute is unsupported])
+ )
+
+-AC_OUTPUT([
++AC_CONFIG_FILES([
+ libtorrent.pc
+ Makefile
+ src/Makefile
+ src/torrent/Makefile
+- test/Makefile
+- test/torrent/net/Makefile
+- test/net/Makefile
++ test/Makefile
++ test/torrent/net/Makefile
++ test/net/Makefile
+ ])
++AC_OUTPUT
+diff --git a/scripts/ax_check_zlib.m4 b/scripts/ax_check_zlib.m4
+old mode 100644
+new mode 100755
+index ae5705f6..1a168430
+--- a/scripts/ax_check_zlib.m4
++++ b/scripts/ax_check_zlib.m4
+@@ -1,5 +1,5 @@
+ # ===========================================================================
+-# http://www.gnu.org/software/autoconf-archive/ax_check_zlib.html
++# https://www.gnu.org/software/autoconf-archive/ax_check_zlib.html
+ # ===========================================================================
+ #
+ # SYNOPSIS
+@@ -47,7 +47,7 @@
+ # Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License along
+-# with this program. If not, see <http://www.gnu.org/licenses/>.
++# with this program. If not, see <https://www.gnu.org/licenses/>.
+ #
+ # As a special exception, the respective Autoconf Macro's copyright owner
+ # gives unlimited permission to copy, distribute and modify the configure
+@@ -62,7 +62,7 @@
+ # modified version of the Autoconf Macro, you may extend this special
+ # exception to the GPL to apply to your modified version as well.
+
+-#serial 14
++#serial 16
+
+ AU_ALIAS([CHECK_ZLIB], [AX_CHECK_ZLIB])
+ AC_DEFUN([AX_CHECK_ZLIB],
+@@ -108,11 +108,10 @@ then
+ LDFLAGS="$LDFLAGS -L${ZLIB_HOME}/lib"
+ CPPFLAGS="$CPPFLAGS -I${ZLIB_HOME}/include"
+ fi
+- AC_LANG_SAVE
+- AC_LANG_C
++ AC_LANG_PUSH([C])
+ AC_CHECK_LIB([z], [inflateEnd], [zlib_cv_libz=yes], [zlib_cv_libz=no])
+ AC_CHECK_HEADER([zlib.h], [zlib_cv_zlib_h=yes], [zlib_cv_zlib_h=no])
+- AC_LANG_RESTORE
++ AC_LANG_POP([C])
+ if test "$zlib_cv_libz" = "yes" && test "$zlib_cv_zlib_h" = "yes"
+ then
+ #
+diff --git a/scripts/ax_cxx_compile_stdcxx.m4 b/scripts/ax_cxx_compile_stdcxx.m4
+new file mode 100755
+index 00000000..9413da62
+--- /dev/null
++++ b/scripts/ax_cxx_compile_stdcxx.m4
+@@ -0,0 +1,962 @@
++# ===========================================================================
++# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
++# ===========================================================================
++#
++# SYNOPSIS
++#
++# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
++#
++# DESCRIPTION
++#
++# Check for baseline language coverage in the compiler for the specified
++# version of the C++ standard. If necessary, add switches to CXX and
++# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
++# or '14' (for the C++14 standard).
++#
++# The second argument, if specified, indicates whether you insist on an
++# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
++# -std=c++11). If neither is specified, you get whatever works, with
++# preference for no added switch, and then for an extended mode.
++#
++# The third argument, if specified 'mandatory' or if left unspecified,
++# indicates that baseline support for the specified C++ standard is
++# required and that the macro should error out if no mode with that
++# support is found. If specified 'optional', then configuration proceeds
++# regardless, after defining HAVE_CXX${VERSION} if and only if a
++# supporting mode is found.
++#
++# LICENSE
++#
++# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
++# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
++# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
++# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
++# Copyright (c) 2015 Paul Norman <penorman@mac.com>
++# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
++# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
++# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
++# Copyright (c) 2020 Jason Merrill <jason@redhat.com>
++#
++# Copying and distribution of this file, with or without modification, are
++# permitted in any medium without royalty provided the copyright notice
++# and this notice are preserved. This file is offered as-is, without any
++# warranty.
++
++#serial 12
++
++dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
++dnl (serial version number 13).
++
++AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
++ m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
++ [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
++ [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
++ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
++ m4_if([$2], [], [],
++ [$2], [ext], [],
++ [$2], [noext], [],
++ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
++ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
++ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
++ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
++ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
++ AC_LANG_PUSH([C++])dnl
++ ac_success=no
++
++ m4_if([$2], [], [dnl
++ AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
++ ax_cv_cxx_compile_cxx$1,
++ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
++ [ax_cv_cxx_compile_cxx$1=yes],
++ [ax_cv_cxx_compile_cxx$1=no])])
++ if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
++ ac_success=yes
++ fi])
++
++ m4_if([$2], [noext], [], [dnl
++ if test x$ac_success = xno; then
++ for alternative in ${ax_cxx_compile_alternatives}; do
++ switch="-std=gnu++${alternative}"
++ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
++ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
++ $cachevar,
++ [ac_save_CXX="$CXX"
++ CXX="$CXX $switch"
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
++ [eval $cachevar=yes],
++ [eval $cachevar=no])
++ CXX="$ac_save_CXX"])
++ if eval test x\$$cachevar = xyes; then
++ CXX="$CXX $switch"
++ if test -n "$CXXCPP" ; then
++ CXXCPP="$CXXCPP $switch"
++ fi
++ ac_success=yes
++ break
++ fi
++ done
++ fi])
++
++ m4_if([$2], [ext], [], [dnl
++ if test x$ac_success = xno; then
++ dnl HP's aCC needs +std=c++11 according to:
++ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
++ dnl Cray's crayCC needs "-h std=c++11"
++ for alternative in ${ax_cxx_compile_alternatives}; do
++ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
++ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
++ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
++ $cachevar,
++ [ac_save_CXX="$CXX"
++ CXX="$CXX $switch"
++ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
++ [eval $cachevar=yes],
++ [eval $cachevar=no])
++ CXX="$ac_save_CXX"])
++ if eval test x\$$cachevar = xyes; then
++ CXX="$CXX $switch"
++ if test -n "$CXXCPP" ; then
++ CXXCPP="$CXXCPP $switch"
++ fi
++ ac_success=yes
++ break
++ fi
++ done
++ if test x$ac_success = xyes; then
++ break
++ fi
++ done
++ fi])
++ AC_LANG_POP([C++])
++ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
++ if test x$ac_success = xno; then
++ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
++ fi
++ fi
++ if test x$ac_success = xno; then
++ HAVE_CXX$1=0
++ AC_MSG_NOTICE([No compiler with C++$1 support was found])
++ else
++ HAVE_CXX$1=1
++ AC_DEFINE(HAVE_CXX$1,1,
++ [define if the compiler supports basic C++$1 syntax])
++ fi
++ AC_SUBST(HAVE_CXX$1)
++])
++
++
++dnl Test body for checking C++11 support
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
++)
++
++
++dnl Test body for checking C++14 support
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
++)
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
++ _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
++)
++
++dnl Tests for new features in C++11
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
++
++// If the compiler admits that it is not ready for C++11, why torture it?
++// Hopefully, this will speed up the test.
++
++#ifndef __cplusplus
++
++#error "This is not a C++ compiler"
++
++#elif __cplusplus < 201103L
++
++#error "This is not a C++11 compiler"
++
++#else
++
++namespace cxx11
++{
++
++ namespace test_static_assert
++ {
++
++ template <typename T>
++ struct check
++ {
++ static_assert(sizeof(int) <= sizeof(T), "not big enough");
++ };
++
++ }
++
++ namespace test_final_override
++ {
++
++ struct Base
++ {
++ virtual ~Base() {}
++ virtual void f() {}
++ };
++
++ struct Derived : public Base
++ {
++ virtual ~Derived() override {}
++ virtual void f() override {}
++ };
++
++ }
++
++ namespace test_double_right_angle_brackets
++ {
++
++ template < typename T >
++ struct check {};
++
++ typedef check<void> single_type;
++ typedef check<check<void>> double_type;
++ typedef check<check<check<void>>> triple_type;
++ typedef check<check<check<check<void>>>> quadruple_type;
++
++ }
++
++ namespace test_decltype
++ {
++
++ int
++ f()
++ {
++ int a = 1;
++ decltype(a) b = 2;
++ return a + b;
++ }
++
++ }
++
++ namespace test_type_deduction
++ {
++
++ template < typename T1, typename T2 >
++ struct is_same
++ {
++ static const bool value = false;
++ };
++
++ template < typename T >
++ struct is_same<T, T>
++ {
++ static const bool value = true;
++ };
++
++ template < typename T1, typename T2 >
++ auto
++ add(T1 a1, T2 a2) -> decltype(a1 + a2)
++ {
++ return a1 + a2;
++ }
++
++ int
++ test(const int c, volatile int v)
++ {
++ static_assert(is_same<int, decltype(0)>::value == true, "");
++ static_assert(is_same<int, decltype(c)>::value == false, "");
++ static_assert(is_same<int, decltype(v)>::value == false, "");
++ auto ac = c;
++ auto av = v;
++ auto sumi = ac + av + 'x';
++ auto sumf = ac + av + 1.0;
++ static_assert(is_same<int, decltype(ac)>::value == true, "");
++ static_assert(is_same<int, decltype(av)>::value == true, "");
++ static_assert(is_same<int, decltype(sumi)>::value == true, "");
++ static_assert(is_same<int, decltype(sumf)>::value == false, "");
++ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
++ return (sumf > 0.0) ? sumi : add(c, v);
++ }
++
++ }
++
++ namespace test_noexcept
++ {
++
++ int f() { return 0; }
++ int g() noexcept { return 0; }
++
++ static_assert(noexcept(f()) == false, "");
++ static_assert(noexcept(g()) == true, "");
++
++ }
++
++ namespace test_constexpr
++ {
++
++ template < typename CharT >
++ unsigned long constexpr
++ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
++ {
++ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
++ }
++
++ template < typename CharT >
++ unsigned long constexpr
++ strlen_c(const CharT *const s) noexcept
++ {
++ return strlen_c_r(s, 0UL);
++ }
++
++ static_assert(strlen_c("") == 0UL, "");
++ static_assert(strlen_c("1") == 1UL, "");
++ static_assert(strlen_c("example") == 7UL, "");
++ static_assert(strlen_c("another\0example") == 7UL, "");
++
++ }
++
++ namespace test_rvalue_references
++ {
++
++ template < int N >
++ struct answer
++ {
++ static constexpr int value = N;
++ };
++
++ answer<1> f(int&) { return answer<1>(); }
++ answer<2> f(const int&) { return answer<2>(); }
++ answer<3> f(int&&) { return answer<3>(); }
++
++ void
++ test()
++ {
++ int i = 0;
++ const int c = 0;
++ static_assert(decltype(f(i))::value == 1, "");
++ static_assert(decltype(f(c))::value == 2, "");
++ static_assert(decltype(f(0))::value == 3, "");
++ }
++
++ }
++
++ namespace test_uniform_initialization
++ {
++
++ struct test
++ {
++ static const int zero {};
++ static const int one {1};
++ };
++
++ static_assert(test::zero == 0, "");
++ static_assert(test::one == 1, "");
++
++ }
++
++ namespace test_lambdas
++ {
++
++ void
++ test1()
++ {
++ auto lambda1 = [](){};
++ auto lambda2 = lambda1;
++ lambda1();
++ lambda2();
++ }
++
++ int
++ test2()
++ {
++ auto a = [](int i, int j){ return i + j; }(1, 2);
++ auto b = []() -> int { return '0'; }();
++ auto c = [=](){ return a + b; }();
++ auto d = [&](){ return c; }();
++ auto e = [a, &b](int x) mutable {
++ const auto identity = [](int y){ return y; };
++ for (auto i = 0; i < a; ++i)
++ a += b--;
++ return x + identity(a + b);
++ }(0);
++ return a + b + c + d + e;
++ }
++
++ int
++ test3()
++ {
++ const auto nullary = [](){ return 0; };
++ const auto unary = [](int x){ return x; };
++ using nullary_t = decltype(nullary);
++ using unary_t = decltype(unary);
++ const auto higher1st = [](nullary_t f){ return f(); };
++ const auto higher2nd = [unary](nullary_t f1){
++ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
++ };
++ return higher1st(nullary) + higher2nd(nullary)(unary);
++ }
++
++ }
++
++ namespace test_variadic_templates
++ {
++
++ template <int...>
++ struct sum;
++
++ template <int N0, int... N1toN>
++ struct sum<N0, N1toN...>
++ {
++ static constexpr auto value = N0 + sum<N1toN...>::value;
++ };
++
++ template <>
++ struct sum<>
++ {
++ static constexpr auto value = 0;
++ };
++
++ static_assert(sum<>::value == 0, "");
++ static_assert(sum<1>::value == 1, "");
++ static_assert(sum<23>::value == 23, "");
++ static_assert(sum<1, 2>::value == 3, "");
++ static_assert(sum<5, 5, 11>::value == 21, "");
++ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
++
++ }
++
++ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
++ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
++ // because of this.
++ namespace test_template_alias_sfinae
++ {
++
++ struct foo {};
++
++ template<typename T>
++ using member = typename T::member_type;
++
++ template<typename T>
++ void func(...) {}
++
++ template<typename T>
++ void func(member<T>*) {}
++
++ void test();
++
++ void test() { func<foo>(0); }
++
++ }
++
++} // namespace cxx11
++
++#endif // __cplusplus >= 201103L
++
++]])
++
++
++dnl Tests for new features in C++14
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
++
++// If the compiler admits that it is not ready for C++14, why torture it?
++// Hopefully, this will speed up the test.
++
++#ifndef __cplusplus
++
++#error "This is not a C++ compiler"
++
++#elif __cplusplus < 201402L
++
++#error "This is not a C++14 compiler"
++
++#else
++
++namespace cxx14
++{
++
++ namespace test_polymorphic_lambdas
++ {
++
++ int
++ test()
++ {
++ const auto lambda = [](auto&&... args){
++ const auto istiny = [](auto x){
++ return (sizeof(x) == 1UL) ? 1 : 0;
++ };
++ const int aretiny[] = { istiny(args)... };
++ return aretiny[0];
++ };
++ return lambda(1, 1L, 1.0f, '1');
++ }
++
++ }
++
++ namespace test_binary_literals
++ {
++
++ constexpr auto ivii = 0b0000000000101010;
++ static_assert(ivii == 42, "wrong value");
++
++ }
++
++ namespace test_generalized_constexpr
++ {
++
++ template < typename CharT >
++ constexpr unsigned long
++ strlen_c(const CharT *const s) noexcept
++ {
++ auto length = 0UL;
++ for (auto p = s; *p; ++p)
++ ++length;
++ return length;
++ }
++
++ static_assert(strlen_c("") == 0UL, "");
++ static_assert(strlen_c("x") == 1UL, "");
++ static_assert(strlen_c("test") == 4UL, "");
++ static_assert(strlen_c("another\0test") == 7UL, "");
++
++ }
++
++ namespace test_lambda_init_capture
++ {
++
++ int
++ test()
++ {
++ auto x = 0;
++ const auto lambda1 = [a = x](int b){ return a + b; };
++ const auto lambda2 = [a = lambda1(x)](){ return a; };
++ return lambda2();
++ }
++
++ }
++
++ namespace test_digit_separators
++ {
++
++ constexpr auto ten_million = 100'000'000;
++ static_assert(ten_million == 100000000, "");
++
++ }
++
++ namespace test_return_type_deduction
++ {
++
++ auto f(int& x) { return x; }
++ decltype(auto) g(int& x) { return x; }
++
++ template < typename T1, typename T2 >
++ struct is_same
++ {
++ static constexpr auto value = false;
++ };
++
++ template < typename T >
++ struct is_same<T, T>
++ {
++ static constexpr auto value = true;
++ };
++
++ int
++ test()
++ {
++ auto x = 0;
++ static_assert(is_same<int, decltype(f(x))>::value, "");
++ static_assert(is_same<int&, decltype(g(x))>::value, "");
++ return x;
++ }
++
++ }
++
++} // namespace cxx14
++
++#endif // __cplusplus >= 201402L
++
++]])
++
++
++dnl Tests for new features in C++17
++
++m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
++
++// If the compiler admits that it is not ready for C++17, why torture it?
++// Hopefully, this will speed up the test.
++
++#ifndef __cplusplus
++
++#error "This is not a C++ compiler"
++
++#elif __cplusplus < 201703L
++
++#error "This is not a C++17 compiler"
++
++#else
++
++#include <initializer_list>
++#include <utility>
++#include <type_traits>
++
++namespace cxx17
++{
++
++ namespace test_constexpr_lambdas
++ {
++
++ constexpr int foo = [](){return 42;}();
++
++ }
++
++ namespace test::nested_namespace::definitions
++ {
++
++ }
++
++ namespace test_fold_expression
++ {
++
++ template<typename... Args>
++ int multiply(Args... args)
++ {
++ return (args * ... * 1);
++ }
++
++ template<typename... Args>
++ bool all(Args... args)
++ {
++ return (args && ...);
++ }
++
++ }
++
++ namespace test_extended_static_assert
++ {
++
++ static_assert (true);
++
++ }
++
++ namespace test_auto_brace_init_list
++ {
++
++ auto foo = {5};
++ auto bar {5};
++
++ static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
++ static_assert(std::is_same<int, decltype(bar)>::value);
++ }
++
++ namespace test_typename_in_template_template_parameter
++ {
++
++ template<template<typename> typename X> struct D;
++
++ }
++
++ namespace test_fallthrough_nodiscard_maybe_unused_attributes
++ {
++
++ int f1()
++ {
++ return 42;
++ }
++
++ [[nodiscard]] int f2()
++ {
++ [[maybe_unused]] auto unused = f1();
++
++ switch (f1())
++ {
++ case 17:
++ f1();
++ [[fallthrough]];
++ case 42:
++ f1();
++ }
++ return f1();
++ }
++
++ }
++
++ namespace test_extended_aggregate_initialization
++ {
++
++ struct base1
++ {
++ int b1, b2 = 42;
++ };
++
++ struct base2
++ {
++ base2() {
++ b3 = 42;
++ }
++ int b3;
++ };
++
++ struct derived : base1, base2
++ {
++ int d;
++ };
++
++ derived d1 {{1, 2}, {}, 4}; // full initialization
++ derived d2 {{}, {}, 4}; // value-initialized bases
++
++ }
++
++ namespace test_general_range_based_for_loop
++ {
++
++ struct iter
++ {
++ int i;
++
++ int& operator* ()
++ {
++ return i;
++ }
++
++ const int& operator* () const
++ {
++ return i;
++ }
++
++ iter& operator++()
++ {
++ ++i;
++ return *this;
++ }
++ };
++
++ struct sentinel
++ {
++ int i;
++ };
++
++ bool operator== (const iter& i, const sentinel& s)
++ {
++ return i.i == s.i;
++ }
++
++ bool operator!= (const iter& i, const sentinel& s)
++ {
++ return !(i == s);
++ }
++
++ struct range
++ {
++ iter begin() const
++ {
++ return {0};
++ }
++
++ sentinel end() const
++ {
++ return {5};
++ }
++ };
++
++ void f()
++ {
++ range r {};
++
++ for (auto i : r)
++ {
++ [[maybe_unused]] auto v = i;
++ }
++ }
++
++ }
++
++ namespace test_lambda_capture_asterisk_this_by_value
++ {
++
++ struct t
++ {
++ int i;
++ int foo()
++ {
++ return [*this]()
++ {
++ return i;
++ }();
++ }
++ };
++
++ }
++
++ namespace test_enum_class_construction
++ {
++
++ enum class byte : unsigned char
++ {};
++
++ byte foo {42};
++
++ }
++
++ namespace test_constexpr_if
++ {
++
++ template <bool cond>
++ int f ()
++ {
++ if constexpr(cond)
++ {
++ return 13;
++ }
++ else
++ {
++ return 42;
++ }
++ }
++
++ }
++
++ namespace test_selection_statement_with_initializer
++ {
++
++ int f()
++ {
++ return 13;
++ }
++
++ int f2()
++ {
++ if (auto i = f(); i > 0)
++ {
++ return 3;
++ }
++
++ switch (auto i = f(); i + 4)
++ {
++ case 17:
++ return 2;
++
++ default:
++ return 1;
++ }
++ }
++
++ }
++
++ namespace test_template_argument_deduction_for_class_templates
++ {
++
++ template <typename T1, typename T2>
++ struct pair
++ {
++ pair (T1 p1, T2 p2)
++ : m1 {p1},
++ m2 {p2}
++ {}
++
++ T1 m1;
++ T2 m2;
++ };
++
++ void f()
++ {
++ [[maybe_unused]] auto p = pair{13, 42u};
++ }
++
++ }
++
++ namespace test_non_type_auto_template_parameters
++ {
++
++ template <auto n>
++ struct B
++ {};
++
++ B<5> b1;
++ B<'a'> b2;
++
++ }
++
++ namespace test_structured_bindings
++ {
++
++ int arr[2] = { 1, 2 };
++ std::pair<int, int> pr = { 1, 2 };
++
++ auto f1() -> int(&)[2]
++ {
++ return arr;
++ }
++
++ auto f2() -> std::pair<int, int>&
++ {
++ return pr;
++ }
++
++ struct S
++ {
++ int x1 : 2;
++ volatile double y1;
++ };
++
++ S f3()
++ {
++ return {};
++ }
++
++ auto [ x1, y1 ] = f1();
++ auto& [ xr1, yr1 ] = f1();
++ auto [ x2, y2 ] = f2();
++ auto& [ xr2, yr2 ] = f2();
++ const auto [ x3, y3 ] = f3();
++
++ }
++
++ namespace test_exception_spec_type_system
++ {
++
++ struct Good {};
++ struct Bad {};
++
++ void g1() noexcept;
++ void g2();
++
++ template<typename T>
++ Bad
++ f(T*, T*);
++
++ template<typename T1, typename T2>
++ Good
++ f(T1*, T2*);
++
++ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
++
++ }
++
++ namespace test_inline_variables
++ {
++
++ template<class T> void f(T)
++ {}
++
++ template<class T> inline T g(T)
++ {
++ return T{};
++ }
++
++ template<> inline void f<>(int)
++ {}
++
++ template<> int g<>(int)
++ {
++ return 5;
++ }
++
++ }
++
++} // namespace cxx17
++
++#endif // __cplusplus < 201703L
++
++]])
+diff --git a/scripts/ax_cxx_compile_stdcxx_0x.m4 b/scripts/ax_cxx_compile_stdcxx_0x.m4
+deleted file mode 100644
+index 5ff134a6..00000000
+--- a/scripts/ax_cxx_compile_stdcxx_0x.m4
++++ /dev/null
+@@ -1,106 +0,0 @@
+-# ============================================================================
+-# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_0x.html
+-# ============================================================================
+-#
+-# SYNOPSIS
+-#
+-# AX_CXX_COMPILE_STDCXX_0X
+-#
+-# DESCRIPTION
+-#
+-# Check for baseline language coverage in the compiler for the C++0x
+-# standard.
+-#
+-# LICENSE
+-#
+-# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+-#
+-# Copying and distribution of this file, with or without modification, are
+-# permitted in any medium without royalty provided the copyright notice
+-# and this notice are preserved. This file is offered as-is, without any
+-# warranty.
+-
+-#serial 7 (+1)
+-
+-AU_ALIAS([AC_CXX_COMPILE_STDCXX_0X], [AX_CXX_COMPILE_STDCXX_0X])
+-AC_DEFUN([AX_CXX_COMPILE_STDCXX_0X], [
+- AC_CACHE_CHECK(if g++ supports C++0x features without additional flags,
+- ax_cv_cxx_compile_cxx0x_native,
+- [AC_LANG_SAVE
+- AC_LANG_CPLUSPLUS
+- AC_TRY_COMPILE([
+- template <typename T>
+- struct check
+- {
+- static_assert(sizeof(int) <= sizeof(T), "not big enough");
+- };
+-
+- typedef check<check<bool>> right_angle_brackets;
+-
+- int a;
+- decltype(a) b;
+-
+- typedef check<int> check_type;
+- check_type c;
+- check_type&& cr = static_cast<check_type&&>(c);],,
+- ax_cv_cxx_compile_cxx0x_native=yes, ax_cv_cxx_compile_cxx0x_native=no)
+- AC_LANG_RESTORE
+- ])
+-
+- AC_CACHE_CHECK(if g++ supports C++0x features with -std=c++0x,
+- ax_cv_cxx_compile_cxx0x_cxx,
+- [AC_LANG_SAVE
+- AC_LANG_CPLUSPLUS
+- ac_save_CXXFLAGS="$CXXFLAGS"
+- CXXFLAGS="$CXXFLAGS -std=c++0x"
+- AC_TRY_COMPILE([
+- template <typename T>
+- struct check
+- {
+- static_assert(sizeof(int) <= sizeof(T), "not big enough");
+- };
+-
+- typedef check<check<bool>> right_angle_brackets;
+-
+- int a;
+- decltype(a) b;
+-
+- typedef check<int> check_type;
+- check_type c;
+- check_type&& cr = static_cast<check_type&&>(c);],,
+- ax_cv_cxx_compile_cxx0x_cxx=yes, ax_cv_cxx_compile_cxx0x_cxx=no)
+- CXXFLAGS="$ac_save_CXXFLAGS"
+- AC_LANG_RESTORE
+- ])
+-
+- AC_CACHE_CHECK(if g++ supports C++0x features with -std=gnu++0x,
+- ax_cv_cxx_compile_cxx0x_gxx,
+- [AC_LANG_SAVE
+- AC_LANG_CPLUSPLUS
+- ac_save_CXXFLAGS="$CXXFLAGS"
+- CXXFLAGS="$CXXFLAGS -std=gnu++0x"
+- AC_TRY_COMPILE([
+- template <typename T>
+- struct check
+- {
+- static_assert(sizeof(int) <= sizeof(T), "not big enough");
+- };
+-
+- typedef check<check<bool>> right_angle_brackets;
+-
+- int a;
+- decltype(a) b;
+-
+- typedef check<int> check_type;
+- check_type c;
+- check_type&& cr = static_cast<check_type&&>(c);],,
+- ax_cv_cxx_compile_cxx0x_gxx=yes, ax_cv_cxx_compile_cxx0x_gxx=no)
+- CXXFLAGS="$ac_save_CXXFLAGS"
+- AC_LANG_RESTORE
+- ])
+-
+- if test "$ax_cv_cxx_compile_cxx0x_cxx" = yes; then
+- AC_DEFINE(HAVE_STDCXX_0X,, [Define if compiler supports C++0x features.])
+- CXXFLAGS="$CXXFLAGS -std=c++0x"
+- fi
+-])
+diff --git a/scripts/ax_cxx_compile_stdcxx_11.m4 b/scripts/ax_cxx_compile_stdcxx_11.m4
+deleted file mode 100644
+index 5cf70eb6..00000000
+--- a/scripts/ax_cxx_compile_stdcxx_11.m4
++++ /dev/null
+@@ -1,147 +0,0 @@
+-# ============================================================================
+-# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+-# ============================================================================
+-#
+-# SYNOPSIS
+-#
+-# AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
+-#
+-# DESCRIPTION
+-#
+-# Check for baseline language coverage in the compiler for the C++11
+-# standard; if necessary, add switches to CXXFLAGS to enable support.
+-#
+-# The first argument, if specified, indicates whether you insist on an
+-# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+-# -std=c++11). If neither is specified, you get whatever works, with
+-# preference for an extended mode.
+-#
+-# The second argument, if specified 'mandatory' or if left unspecified,
+-# indicates that baseline C++11 support is required and that the macro
+-# should error out if no mode with that support is found. If specified
+-# 'optional', then configuration proceeds regardless, after defining
+-# HAVE_CXX11 if and only if a supporting mode is found.
+-#
+-# LICENSE
+-#
+-# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+-# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+-# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+-# Copyright (c) 2014 Alexey Sokolov <sokolov@google.com>
+-# Copyright (c) 2014 Jari Sundell <sundell.software@gmail.com>
+-#
+-# Copying and distribution of this file, with or without modification, are
+-# permitted in any medium without royalty provided the copyright notice
+-# and this notice are preserved. This file is offered as-is, without any
+-# warranty.
+-
+-#serial 5
+-
+-m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[
+- template <typename T>
+- struct check
+- {
+- static_assert(sizeof(int) <= sizeof(T), "not big enough");
+- };
+-
+- struct Base {
+- virtual void f() {}
+- };
+- struct Child : public Base {
+- virtual void f() override {}
+- };
+-
+- typedef check<check<bool>> right_angle_brackets;
+-
+- int a;
+- decltype(a) b;
+-
+- typedef check<int> check_type;
+- check_type c;
+- check_type&& cr = static_cast<check_type&&>(c);
+-
+- auto d = a;
+- auto l = [](){};
+-
+- void unused() {
+- l();
+- }
+-]])
+-
+-AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
+- m4_if([$1], [], [],
+- [$1], [ext], [],
+- [$1], [noext], [],
+- [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
+- m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
+- [$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
+- [$2], [optional], [ax_cxx_compile_cxx11_required=false],
+- [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])
+- AC_LANG_PUSH([C++])dnl
+- ac_success=no
+- AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
+- ax_cv_cxx_compile_cxx11,
+- [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+- [ax_cv_cxx_compile_cxx11=yes],
+- [ax_cv_cxx_compile_cxx11=no])])
+- if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+- ac_success=yes
+- fi
+-
+- m4_if([$1], [noext], [], [dnl
+- if test x$ac_success = xno; then
+- for switch in -std=gnu++11 -std=gnu++0x; do
+- cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+- AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+- $cachevar,
+- [ac_save_CXXFLAGS="$CXXFLAGS"
+- CXXFLAGS="$CXXFLAGS $switch"
+- AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+- [eval $cachevar=yes],
+- [eval $cachevar=no])
+- CXXFLAGS="$ac_save_CXXFLAGS"])
+- if eval test x\$$cachevar = xyes; then
+- CXXFLAGS="$CXXFLAGS $switch"
+- ac_success=yes
+- break
+- fi
+- done
+- fi])
+-
+- m4_if([$1], [ext], [], [dnl
+- if test x$ac_success = xno; then
+- for switch in -std=c++11 -std=c++0x; do
+- cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+- AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+- $cachevar,
+- [ac_save_CXXFLAGS="$CXXFLAGS"
+- CXXFLAGS="$CXXFLAGS $switch"
+- AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+- [eval $cachevar=yes],
+- [eval $cachevar=no])
+- CXXFLAGS="$ac_save_CXXFLAGS"])
+- if eval test x\$$cachevar = xyes; then
+- CXXFLAGS="$CXXFLAGS $switch"
+- ac_success=yes
+- break
+- fi
+- done
+- fi])
+- AC_LANG_POP([C++])
+- if test x$ax_cxx_compile_cxx11_required = xtrue; then
+- if test x$ac_success = xno; then
+- AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
+- fi
+- else
+- if test x$ac_success = xno; then
+- HAVE_CXX11=0
+- AC_MSG_NOTICE([No compiler with C++11 support was found])
+- else
+- HAVE_CXX11=1
+- AC_DEFINE(HAVE_CXX11,1,
+- [define if the compiler supports basic C++11 syntax])
+- fi
+-
+- AC_SUBST(HAVE_CXX11)
+- fi
+-])
+diff --git a/scripts/ax_pthread.m4 b/scripts/ax_pthread.m4
+old mode 100644
+new mode 100755
+index 27f2533c..9f35d139
+--- a/scripts/ax_pthread.m4
++++ b/scripts/ax_pthread.m4
+@@ -1,5 +1,5 @@
+ # ===========================================================================
+-# http://www.gnu.org/software/autoconf-archive/ax_pthread.html
++# https://www.gnu.org/software/autoconf-archive/ax_pthread.html
+ # ===========================================================================
+ #
+ # SYNOPSIS
+@@ -14,24 +14,28 @@
+ # flags that are needed. (The user can also force certain compiler
+ # flags/libs to be tested by setting these environment variables.)
+ #
+-# Also sets PTHREAD_CC to any special C compiler that is needed for
+-# multi-threaded programs (defaults to the value of CC otherwise). (This
+-# is necessary on AIX to use the special cc_r compiler alias.)
++# Also sets PTHREAD_CC and PTHREAD_CXX to any special C compiler that is
++# needed for multi-threaded programs (defaults to the value of CC
++# respectively CXX otherwise). (This is necessary on e.g. AIX to use the
++# special cc_r/CC_r compiler alias.)
+ #
+ # NOTE: You are assumed to not only compile your program with these flags,
+-# but also link it with them as well. e.g. you should link with
++# but also to link with them as well. For example, you might link with
+ # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
++# $PTHREAD_CXX $CXXFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
+ #
+-# If you are only building threads programs, you may wish to use these
++# If you are only building threaded programs, you may wish to use these
+ # variables in your default LIBS, CFLAGS, and CC:
+ #
+ # LIBS="$PTHREAD_LIBS $LIBS"
+ # CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++# CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS"
+ # CC="$PTHREAD_CC"
++# CXX="$PTHREAD_CXX"
+ #
+ # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
+-# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
+-# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
++# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to
++# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+ #
+ # Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the
+ # PTHREAD_PRIO_INHERIT symbol is defined when compiling with
+@@ -55,6 +59,7 @@
+ #
+ # Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
+ # Copyright (c) 2011 Daniel Richard G. <skunk@iSKUNK.ORG>
++# Copyright (c) 2019 Marc Stevens <marc.stevens@cwi.nl>
+ #
+ # 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
+@@ -67,7 +72,7 @@
+ # Public License for more details.
+ #
+ # You should have received a copy of the GNU General Public License along
+-# with this program. If not, see <http://www.gnu.org/licenses/>.
++# with this program. If not, see <https://www.gnu.org/licenses/>.
+ #
+ # As a special exception, the respective Autoconf Macro's copyright owner
+ # gives unlimited permission to copy, distribute and modify the configure
+@@ -82,35 +87,41 @@
+ # modified version of the Autoconf Macro, you may extend this special
+ # exception to the GPL to apply to your modified version as well.
+
+-#serial 17
++#serial 31
+
+ AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
+ AC_DEFUN([AX_PTHREAD], [
+ AC_REQUIRE([AC_CANONICAL_HOST])
++AC_REQUIRE([AC_PROG_CC])
++AC_REQUIRE([AC_PROG_SED])
+ AC_LANG_PUSH([C])
+ ax_pthread_ok=no
+
+ # We used to check for pthread.h first, but this fails if pthread.h
+-# requires special compiler flags (e.g. on True64 or Sequent).
++# requires special compiler flags (e.g. on Tru64 or Sequent).
+ # It gets checked for in the link test anyway.
+
+ # First of all, check if the user has set any of the PTHREAD_LIBS,
+ # etcetera environment variables, and if threads linking works using
+ # them:
+-if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+- save_CFLAGS="$CFLAGS"
++if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then
++ ax_pthread_save_CC="$CC"
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
++ AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"])
++ AS_IF([test "x$PTHREAD_CXX" != "x"], [CXX="$PTHREAD_CXX"])
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+- save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+- AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+- AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
+- AC_MSG_RESULT($ax_pthread_ok)
+- if test x"$ax_pthread_ok" = xno; then
++ AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS])
++ AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes])
++ AC_MSG_RESULT([$ax_pthread_ok])
++ if test "x$ax_pthread_ok" = "xno"; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+- LIBS="$save_LIBS"
+- CFLAGS="$save_CFLAGS"
++ CC="$ax_pthread_save_CC"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
+ fi
+
+ # We must check for the threads library under a number of different
+@@ -118,12 +129,14 @@ fi
+ # (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+ # libraries is broken (non-POSIX).
+
+-# Create a list of thread flags to try. Items starting with a "-" are
+-# C compiler flags, and other items are library names, except for "none"
+-# which indicates that we try without any flags at all, and "pthread-config"
+-# which is a program returning the flags for the Pth emulation library.
++# Create a list of thread flags to try. Items with a "," contain both
++# C compiler flags (before ",") and linker flags (after ","). Other items
++# starting with a "-" are C compiler flags, and remaining items are
++# library names, except for "none" which indicates that we try without
++# any flags at all, and "pthread-config" which is a program returning
++# the flags for the Pth emulation library.
+
+-ax_pthread_flags="none pthreads -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
++ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+ # The ordering *is* (sometimes) important. Some notes on the
+ # individual items follow:
+@@ -132,68 +145,163 @@ ax_pthread_flags="none pthreads -Kthread -kthread lthread -pthread -pthreads -mt
+ # none: in case threads are in libc; should be tried before -Kthread and
+ # other compiler flags to prevent continual compiler warnings
+ # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+-# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+-# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+-# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+-# -pthreads: Solaris/gcc
+-# -mthreads: Mingw32/gcc, Lynx/gcc
++# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64
++# (Note: HP C rejects this with "bad form for `-t' option")
++# -pthreads: Solaris/gcc (Note: HP C also rejects)
+ # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+-# doesn't hurt to check since this sometimes defines pthreads too;
+-# also defines -D_REENTRANT)
+-# ... -mt is also the pthreads flag for HP/aCC
++# doesn't hurt to check since this sometimes defines pthreads and
++# -D_REENTRANT too), HP C (must be checked before -lpthread, which
++# is present but should not be used directly; and before -mthreads,
++# because the compiler interprets this as "-mt" + "-hreads")
++# -mthreads: Mingw32/gcc, Lynx/gcc
+ # pthread: Linux, etcetera
+ # --thread-safe: KAI C++
+ # pthread-config: use pthread-config program (for GNU Pth library)
+
+-case "${host_cpu}-${host_os}" in
+- *solaris*)
++case $host_os in
++
++ freebsd*)
++
++ # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
++ # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
++
++ ax_pthread_flags="-kthread lthread $ax_pthread_flags"
++ ;;
++
++ hpux*)
++
++ # From the cc(1) man page: "[-mt] Sets various -D flags to enable
++ # multi-threading and also sets -lpthread."
++
++ ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags"
++ ;;
++
++ openedition*)
++
++ # IBM z/OS requires a feature-test macro to be defined in order to
++ # enable POSIX threads at all, so give the user a hint if this is
++ # not set. (We don't define these ourselves, as they can affect
++ # other portions of the system API in unpredictable ways.)
++
++ AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING],
++ [
++# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS)
++ AX_PTHREAD_ZOS_MISSING
++# endif
++ ],
++ [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])])
++ ;;
++
++ solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+- # tests will erroneously succeed. (We need to link with -pthreads/-mt/
+- # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+- # a function called by this macro, so we could check for that, but
+- # who knows whether they'll stub that too in a future libc.) So,
+- # we'll just look for -pthreads and -lpthread first:
++ # tests will erroneously succeed. (N.B.: The stubs are missing
++ # pthread_cleanup_push, or rather a function called by this macro,
++ # so we could check for that, but who knows whether they'll stub
++ # that too in a future libc.) So we'll check first for the
++ # standard Solaris way of linking pthreads (-mt -lpthread).
+
+- ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
++ ax_pthread_flags="-mt,-lpthread pthread $ax_pthread_flags"
+ ;;
++esac
+
+- *-darwin*)
+- ax_pthread_flags="none -pthread $ax_pthread_flags"
++# Are we compiling with Clang?
++
++AC_CACHE_CHECK([whether $CC is Clang],
++ [ax_cv_PTHREAD_CLANG],
++ [ax_cv_PTHREAD_CLANG=no
++ # Note that Autoconf sets GCC=yes for Clang as well as GCC
++ if test "x$GCC" = "xyes"; then
++ AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG],
++ [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */
++# if defined(__clang__) && defined(__llvm__)
++ AX_PTHREAD_CC_IS_CLANG
++# endif
++ ],
++ [ax_cv_PTHREAD_CLANG=yes])
++ fi
++ ])
++ax_pthread_clang="$ax_cv_PTHREAD_CLANG"
++
++
++# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC)
++
++# Note that for GCC and Clang -pthread generally implies -lpthread,
++# except when -nostdlib is passed.
++# This is problematic using libtool to build C++ shared libraries with pthread:
++# [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25460
++# [2] https://bugzilla.redhat.com/show_bug.cgi?id=661333
++# [3] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=468555
++# To solve this, first try -pthread together with -lpthread for GCC
++
++AS_IF([test "x$GCC" = "xyes"],
++ [ax_pthread_flags="-pthread,-lpthread -pthread -pthreads $ax_pthread_flags"])
++
++# Clang takes -pthread (never supported any other flag), but we'll try with -lpthread first
++
++AS_IF([test "x$ax_pthread_clang" = "xyes"],
++ [ax_pthread_flags="-pthread,-lpthread -pthread"])
++
++
++# The presence of a feature test macro requesting re-entrant function
++# definitions is, on some systems, a strong hint that pthreads support is
++# correctly enabled
++
++case $host_os in
++ darwin* | hpux* | linux* | osf* | solaris*)
++ ax_pthread_check_macro="_REENTRANT"
++ ;;
++
++ aix*)
++ ax_pthread_check_macro="_THREAD_SAFE"
++ ;;
++
++ *)
++ ax_pthread_check_macro="--"
+ ;;
+ esac
++AS_IF([test "x$ax_pthread_check_macro" = "x--"],
++ [ax_pthread_check_cond=0],
++ [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"])
++
+
+-if test x"$ax_pthread_ok" = xno; then
+-for flag in $ax_pthread_flags; do
++if test "x$ax_pthread_ok" = "xno"; then
++for ax_pthread_try_flag in $ax_pthread_flags; do
+
+- case $flag in
++ case $ax_pthread_try_flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
++ *,*)
++ PTHREAD_CFLAGS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\1/"`
++ PTHREAD_LIBS=`echo $ax_pthread_try_flag | sed "s/^\(.*\),\(.*\)$/\2/"`
++ AC_MSG_CHECKING([whether pthreads work with "$PTHREAD_CFLAGS" and "$PTHREAD_LIBS"])
++ ;;
++
+ -*)
+- AC_MSG_CHECKING([whether pthreads work with $flag])
+- PTHREAD_CFLAGS="$flag"
++ AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag])
++ PTHREAD_CFLAGS="$ax_pthread_try_flag"
+ ;;
+
+ pthread-config)
+- AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
+- if test x"$ax_pthread_config" = xno; then continue; fi
++ AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no])
++ AS_IF([test "x$ax_pthread_config" = "xno"], [continue])
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+- AC_MSG_CHECKING([for the pthreads library -l$flag])
+- PTHREAD_LIBS="-l$flag"
++ AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag])
++ PTHREAD_LIBS="-l$ax_pthread_try_flag"
+ ;;
+ esac
+
+- save_LIBS="$LIBS"
+- save_CFLAGS="$CFLAGS"
+- LIBS="$PTHREAD_LIBS $LIBS"
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+@@ -204,8 +312,18 @@ for flag in $ax_pthread_flags; do
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
++
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>
+- static void routine(void *a) { a = 0; }
++# if $ax_pthread_check_cond
++# error "$ax_pthread_check_macro must be defined"
++# endif
++ static void *some_global = NULL;
++ static void routine(void *a)
++ {
++ /* To avoid any unused-parameter or
++ unused-but-set-parameter warning. */
++ some_global = a;
++ }
+ static void *start_routine(void *a) { return a; }],
+ [pthread_t th; pthread_attr_t attr;
+ pthread_create(&th, 0, start_routine, 0);
+@@ -213,93 +331,188 @@ for flag in $ax_pthread_flags; do
+ pthread_attr_init(&attr);
+ pthread_cleanup_push(routine, 0);
+ pthread_cleanup_pop(0) /* ; */])],
+- [ax_pthread_ok=yes],
+- [])
++ [ax_pthread_ok=yes],
++ [])
+
+- LIBS="$save_LIBS"
+- CFLAGS="$save_CFLAGS"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
+
+- AC_MSG_RESULT($ax_pthread_ok)
+- if test "x$ax_pthread_ok" = xyes; then
+- break;
+- fi
++ AC_MSG_RESULT([$ax_pthread_ok])
++ AS_IF([test "x$ax_pthread_ok" = "xyes"], [break])
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ done
+ fi
+
++
++# Clang needs special handling, because older versions handle the -pthread
++# option in a rather... idiosyncratic way
++
++if test "x$ax_pthread_clang" = "xyes"; then
++
++ # Clang takes -pthread; it has never supported any other flag
++
++ # (Note 1: This will need to be revisited if a system that Clang
++ # supports has POSIX threads in a separate library. This tends not
++ # to be the way of modern systems, but it's conceivable.)
++
++ # (Note 2: On some systems, notably Darwin, -pthread is not needed
++ # to get POSIX threads support; the API is always present and
++ # active. We could reasonably leave PTHREAD_CFLAGS empty. But
++ # -pthread does define _REENTRANT, and while the Darwin headers
++ # ignore this macro, third-party headers might not.)
++
++ # However, older versions of Clang make a point of warning the user
++ # that, in an invocation where only linking and no compilation is
++ # taking place, the -pthread option has no effect ("argument unused
++ # during compilation"). They expect -pthread to be passed in only
++ # when source code is being compiled.
++ #
++ # Problem is, this is at odds with the way Automake and most other
++ # C build frameworks function, which is that the same flags used in
++ # compilation (CFLAGS) are also used in linking. Many systems
++ # supported by AX_PTHREAD require exactly this for POSIX threads
++ # support, and in fact it is often not straightforward to specify a
++ # flag that is used only in the compilation phase and not in
++ # linking. Such a scenario is extremely rare in practice.
++ #
++ # Even though use of the -pthread flag in linking would only print
++ # a warning, this can be a nuisance for well-run software projects
++ # that build with -Werror. So if the active version of Clang has
++ # this misfeature, we search for an option to squash it.
++
++ AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread],
++ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG],
++ [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
++ # Create an alternate version of $ac_link that compiles and
++ # links in two steps (.c -> .o, .o -> exe) instead of one
++ # (.c -> exe), because the warning occurs only in the second
++ # step
++ ax_pthread_save_ac_link="$ac_link"
++ ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g'
++ ax_pthread_link_step=`AS_ECHO(["$ac_link"]) | sed "$ax_pthread_sed"`
++ ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)"
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do
++ AS_IF([test "x$ax_pthread_try" = "xunknown"], [break])
++ CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS"
++ ac_link="$ax_pthread_save_ac_link"
++ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
++ [ac_link="$ax_pthread_2step_ac_link"
++ AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])],
++ [break])
++ ])
++ done
++ ac_link="$ax_pthread_save_ac_link"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no])
++ ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
++ ])
++
++ case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in
++ no | unknown) ;;
++ *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;;
++ esac
++
++fi # $ax_pthread_clang = yes
++
++
++
+ # Various other checks:
+-if test "x$ax_pthread_ok" = xyes; then
+- save_LIBS="$LIBS"
+- LIBS="$PTHREAD_LIBS $LIBS"
+- save_CFLAGS="$CFLAGS"
++if test "x$ax_pthread_ok" = "xyes"; then
++ ax_pthread_save_CFLAGS="$CFLAGS"
++ ax_pthread_save_LIBS="$LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
++ LIBS="$PTHREAD_LIBS $LIBS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+- AC_MSG_CHECKING([for joinable pthread attribute])
+- attr_name=unknown
+- for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+- AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
+- [int attr = $attr; return attr /* ; */])],
+- [attr_name=$attr; break],
+- [])
+- done
+- AC_MSG_RESULT($attr_name)
+- if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+- AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+- [Define to necessary symbol if this constant
+- uses a non-standard name on your system.])
+- fi
++ AC_CACHE_CHECK([for joinable pthread attribute],
++ [ax_cv_PTHREAD_JOINABLE_ATTR],
++ [ax_cv_PTHREAD_JOINABLE_ATTR=unknown
++ for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <pthread.h>],
++ [int attr = $ax_pthread_attr; return attr /* ; */])],
++ [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break],
++ [])
++ done
++ ])
++ AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \
++ test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \
++ test "x$ax_pthread_joinable_attr_defined" != "xyes"],
++ [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE],
++ [$ax_cv_PTHREAD_JOINABLE_ATTR],
++ [Define to necessary symbol if this constant
++ uses a non-standard name on your system.])
++ ax_pthread_joinable_attr_defined=yes
++ ])
+
+- AC_MSG_CHECKING([if more special flags are required for pthreads])
+- flag=no
+- case "${host_cpu}-${host_os}" in
+- *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
+- *-osf* | *-hpux*) flag="-D_REENTRANT";;
+- *solaris*)
+- if test "$GCC" = "yes"; then
+- flag="-D_REENTRANT"
+- else
+- flag="-mt -D_REENTRANT"
+- fi
+- ;;
+- esac
+- AC_MSG_RESULT(${flag})
+- if test "x$flag" != xno; then
+- PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
+- fi
++ AC_CACHE_CHECK([whether more special flags are required for pthreads],
++ [ax_cv_PTHREAD_SPECIAL_FLAGS],
++ [ax_cv_PTHREAD_SPECIAL_FLAGS=no
++ case $host_os in
++ solaris*)
++ ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
++ ;;
++ esac
++ ])
++ AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \
++ test "x$ax_pthread_special_flags_added" != "xyes"],
++ [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS"
++ ax_pthread_special_flags_added=yes])
+
+ AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT],
+- ax_cv_PTHREAD_PRIO_INHERIT, [
+- AC_LINK_IFELSE([
+- AC_LANG_PROGRAM([[#include <pthread.h>]], [[int i = PTHREAD_PRIO_INHERIT;]])],
+- [ax_cv_PTHREAD_PRIO_INHERIT=yes],
+- [ax_cv_PTHREAD_PRIO_INHERIT=no])
++ [ax_cv_PTHREAD_PRIO_INHERIT],
++ [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]],
++ [[int i = PTHREAD_PRIO_INHERIT;
++ return i;]])],
++ [ax_cv_PTHREAD_PRIO_INHERIT=yes],
++ [ax_cv_PTHREAD_PRIO_INHERIT=no])
+ ])
+- AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes"],
+- AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], 1, [Have PTHREAD_PRIO_INHERIT.]))
++ AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \
++ test "x$ax_pthread_prio_inherit_defined" != "xyes"],
++ [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.])
++ ax_pthread_prio_inherit_defined=yes
++ ])
+
+- LIBS="$save_LIBS"
+- CFLAGS="$save_CFLAGS"
++ CFLAGS="$ax_pthread_save_CFLAGS"
++ LIBS="$ax_pthread_save_LIBS"
+
+- # More AIX lossage: must compile with xlc_r or cc_r
+- if test x"$GCC" != xyes; then
+- AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
+- else
+- PTHREAD_CC=$CC
++ # More AIX lossage: compile with *_r variant
++ if test "x$GCC" != "xyes"; then
++ case $host_os in
++ aix*)
++ AS_CASE(["x/$CC"],
++ [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6],
++ [#handle absolute path differently from PATH based program lookup
++ AS_CASE(["x$CC"],
++ [x/*],
++ [
++ AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])
++ AS_IF([test "x${CXX}" != "x"], [AS_IF([AS_EXECUTABLE_P([${CXX}_r])],[PTHREAD_CXX="${CXX}_r"])])
++ ],
++ [
++ AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])
++ AS_IF([test "x${CXX}" != "x"], [AC_CHECK_PROGS([PTHREAD_CXX],[${CXX}_r],[$CXX])])
++ ]
++ )
++ ])
++ ;;
++ esac
+ fi
+-else
+- PTHREAD_CC="$CC"
+ fi
+
+-AC_SUBST(PTHREAD_LIBS)
+-AC_SUBST(PTHREAD_CFLAGS)
+-AC_SUBST(PTHREAD_CC)
++test -n "$PTHREAD_CC" || PTHREAD_CC="$CC"
++test -n "$PTHREAD_CXX" || PTHREAD_CXX="$CXX"
++
++AC_SUBST([PTHREAD_LIBS])
++AC_SUBST([PTHREAD_CFLAGS])
++AC_SUBST([PTHREAD_CC])
++AC_SUBST([PTHREAD_CXX])
+
+ # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+-if test x"$ax_pthread_ok" = xyes; then
+- ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
++if test "x$ax_pthread_ok" = "xyes"; then
++ ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1])
+ :
+ else
+ ax_pthread_ok=no
+diff --git a/scripts/checks.m4 b/scripts/checks.m4
+index 915a5011..b9095cee 100644
+--- a/scripts/checks.m4
++++ b/scripts/checks.m4
+@@ -21,7 +21,7 @@ AC_DEFUN([TORRENT_CHECK_XFS], [
+
+ AC_DEFUN([TORRENT_WITHOUT_XFS], [
+ AC_ARG_WITH(xfs,
+- AC_HELP_STRING([--without-xfs], [do not check for XFS filesystem support]),
++ AS_HELP_STRING([--without-xfs],[do not check for XFS filesystem support]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_XFS
+@@ -34,7 +34,7 @@ AC_DEFUN([TORRENT_WITHOUT_XFS], [
+
+ AC_DEFUN([TORRENT_WITH_XFS], [
+ AC_ARG_WITH(xfs,
+- AC_HELP_STRING([--with-xfs], [check for XFS filesystem support]),
++ AS_HELP_STRING([--with-xfs],[check for XFS filesystem support]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_XFS
+@@ -63,7 +63,7 @@ AC_DEFUN([TORRENT_CHECK_EPOLL], [
+
+ AC_DEFUN([TORRENT_WITHOUT_EPOLL], [
+ AC_ARG_WITH(epoll,
+- AC_HELP_STRING([--without-epoll], [do not check for epoll support]),
++ AS_HELP_STRING([--without-epoll],[do not check for epoll support]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_EPOLL
+@@ -134,7 +134,7 @@ AC_DEFUN([TORRENT_CHECK_KQUEUE_SOCKET_ONLY], [
+
+ AC_DEFUN([TORRENT_WITH_KQUEUE], [
+ AC_ARG_WITH(kqueue,
+- AC_HELP_STRING([--with-kqueue], [enable kqueue [[default=no]]]),
++ AS_HELP_STRING([--with-kqueue],[enable kqueue [[default=no]]]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+@@ -145,7 +145,7 @@ AC_DEFUN([TORRENT_WITH_KQUEUE], [
+
+ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
+ AC_ARG_WITH(kqueue,
+- AC_HELP_STRING([--without-kqueue], [do not check for kqueue support]),
++ AS_HELP_STRING([--without-kqueue],[do not check for kqueue support]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_KQUEUE
+@@ -158,7 +158,7 @@ AC_DEFUN([TORRENT_WITHOUT_KQUEUE], [
+
+ AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
+ AC_ARG_WITH(variable-fdset,
+- AC_HELP_STRING([--without-variable-fdset], [do not use non-portable variable sized fd_set's]),
++ AS_HELP_STRING([--without-variable-fdset],[do not use non-portable variable sized fd_set's]),
+ [
+ if test "$withval" = "yes"; then
+ AC_DEFINE(USE_VARIABLE_FDSET, 1, defined when we allow the use of fd_set's of any size)
+@@ -172,14 +172,13 @@ AC_DEFUN([TORRENT_WITHOUT_VARIABLE_FDSET], [
+ AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
+ AC_MSG_CHECKING(for fallocate)
+
+- AC_TRY_LINK([#define _GNU_SOURCE
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#define _GNU_SOURCE
+ #include <fcntl.h>
+- ],[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
+- ],
+- [
++ ]], [[ fallocate(0, FALLOC_FL_KEEP_SIZE, 0, 0); return 0;
++ ]])],[
+ AC_DEFINE(HAVE_FALLOCATE, 1, Linux's fallocate supported.)
+ AC_MSG_RESULT(yes)
+- ], [
++ ],[
+ AC_MSG_RESULT(no)
+ ])
+ ])
+@@ -188,13 +187,12 @@ AC_DEFUN([TORRENT_CHECK_FALLOCATE], [
+ AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
+ AC_MSG_CHECKING(for posix_fallocate)
+
+- AC_TRY_LINK([#include <fcntl.h>
+- ],[ posix_fallocate(0, 0, 0);
+- ],
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <fcntl.h>
++ ]], [[ posix_fallocate(0, 0, 0);
++ ]])],[
+ AC_DEFINE(USE_POSIX_FALLOCATE, 1, posix_fallocate supported.)
+ AC_MSG_RESULT(yes)
+- ], [
++ ],[
+ AC_MSG_RESULT(no)
+ ])
+ ])
+@@ -202,7 +200,7 @@ AC_DEFUN([TORRENT_CHECK_POSIX_FALLOCATE], [
+
+ AC_DEFUN([TORRENT_WITH_POSIX_FALLOCATE], [
+ AC_ARG_WITH(posix-fallocate,
+- AC_HELP_STRING([--with-posix-fallocate], [check for and use posix_fallocate to allocate files]),
++ AS_HELP_STRING([--with-posix-fallocate],[check for and use posix_fallocate to allocate files]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_POSIX_FALLOCATE
+@@ -215,8 +213,7 @@ AC_DEFUN([TORRENT_CHECK_STATVFS], [
+
+ AC_MSG_CHECKING(for statvfs)
+
+- AC_TRY_LINK(
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ #if HAVE_SYS_VFS_H
+ #include <sys/vfs.h>
+ #endif
+@@ -226,12 +223,11 @@ AC_DEFUN([TORRENT_CHECK_STATVFS], [
+ #if HAVE_SYS_STATFS_H
+ #include <sys/statfs.h>
+ #endif
+- ],[
++ ]], [[
+ struct statvfs s; fsblkcnt_t c;
+ statvfs("", &s);
+ fstatvfs(0, &s);
+- ],
+- [
++ ]])],[
+ AC_DEFINE(FS_STAT_FD, [fstatvfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
+ AC_DEFINE(FS_STAT_FN, [statvfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
+ AC_DEFINE(FS_STAT_STRUCT, [struct statvfs], Type of second argument to statfs function)
+@@ -240,8 +236,7 @@ AC_DEFUN([TORRENT_CHECK_STATVFS], [
+ AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_frsize)], Determine the block size)
+ AC_MSG_RESULT(ok)
+ have_stat_vfs=yes
+- ],
+- [
++ ],[
+ AC_MSG_RESULT(no)
+ have_stat_vfs=no
+ ])
+@@ -252,8 +247,7 @@ AC_DEFUN([TORRENT_CHECK_STATFS], [
+
+ AC_MSG_CHECKING(for statfs)
+
+- AC_TRY_LINK(
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ #if HAVE_SYS_STATFS_H
+ #include <sys/statfs.h>
+ #endif
+@@ -263,12 +257,11 @@ AC_DEFUN([TORRENT_CHECK_STATFS], [
+ #if HAVE_SYS_MOUNT_H
+ #include <sys/mount.h>
+ #endif
+- ],[
++ ]], [[
+ struct statfs s;
+ statfs("", &s);
+ fstatfs(0, &s);
+- ],
+- [
++ ]])],[
+ AC_DEFINE(FS_STAT_FD, [fstatfs(fd, &m_stat) == 0], Function to determine filesystem stats from fd)
+ AC_DEFINE(FS_STAT_FN, [statfs(fn, &m_stat) == 0], Function to determine filesystem stats from filename)
+ AC_DEFINE(FS_STAT_STRUCT, [struct statfs], Type of second argument to statfs function)
+@@ -277,8 +270,7 @@ AC_DEFUN([TORRENT_CHECK_STATFS], [
+ AC_DEFINE(FS_STAT_BLOCK_SIZE, [(m_stat.f_bsize)], Determine the block size)
+ AC_MSG_RESULT(ok)
+ have_stat_vfs=yes
+- ],
+- [
++ ],[
+ AC_MSG_RESULT(no)
+ have_stat_vfs=no
+ ])
+@@ -296,7 +288,7 @@ AC_DEFUN([TORRENT_DISABLED_STATFS], [
+
+ AC_DEFUN([TORRENT_WITHOUT_STATVFS], [
+ AC_ARG_WITH(statvfs,
+- AC_HELP_STRING([--without-statvfs], [don't try to use statvfs to find free diskspace]),
++ AS_HELP_STRING([--without-statvfs],[don't try to use statvfs to find free diskspace]),
+ [
+ if test "$withval" = "yes"; then
+ TORRENT_CHECK_STATVFS
+@@ -311,7 +303,7 @@ AC_DEFUN([TORRENT_WITHOUT_STATVFS], [
+
+ AC_DEFUN([TORRENT_WITHOUT_STATFS], [
+ AC_ARG_WITH(statfs,
+- AC_HELP_STRING([--without-statfs], [don't try to use statfs to find free diskspace]),
++ AS_HELP_STRING([--without-statfs],[don't try to use statfs to find free diskspace]),
+ [
+ if test "$have_stat_vfs" = "no"; then
+ if test "$withval" = "yes"; then
+@@ -333,7 +325,7 @@ AC_DEFUN([TORRENT_WITHOUT_STATFS], [
+
+ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
+ AC_ARG_WITH(address-space,
+- AC_HELP_STRING([--with-address-space=MB], [change the default address space size [[default=1024mb]]]),
++ AS_HELP_STRING([--with-address-space=MB],[change the default address space size [[default=1024mb]]]),
+ [
+ if test ! -z $withval -a "$withval" != "yes" -a "$withval" != "no"; then
+ AC_DEFINE_UNQUOTED(DEFAULT_ADDRESS_SPACE_SIZE, [$withval])
+@@ -354,7 +346,7 @@ AC_DEFUN([TORRENT_WITH_ADDRESS_SPACE], [
+
+ AC_DEFUN([TORRENT_WITH_FASTCGI], [
+ AC_ARG_WITH(fastcgi,
+- AC_HELP_STRING([--with-fastcgi=PATH], [enable FastCGI RPC support (DO NOT USE)]),
++ AS_HELP_STRING([--with-fastcgi=PATH],[enable FastCGI RPC support (DO NOT USE)]),
+ [
+ AC_MSG_CHECKING([for FastCGI (DO NOT USE)])
+
+@@ -365,13 +357,10 @@ AC_DEFUN([TORRENT_WITH_FASTCGI], [
+ CXXFLAGS="$CXXFLAGS"
+ LIBS="$LIBS -lfcgi"
+
+- AC_TRY_LINK(
+- [ #include <fcgiapp.h>
+- ],[ FCGX_Init(); ],
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
++ ]], [[ FCGX_Init(); ]])],[
+ AC_MSG_RESULT(ok)
+- ],
+- [
++ ],[
+ AC_MSG_RESULT(not found)
+ AC_MSG_ERROR(Could not compile FastCGI test.)
+ ])
+@@ -382,13 +371,10 @@ AC_DEFUN([TORRENT_WITH_FASTCGI], [
+ CXXFLAGS="$CXXFLAGS -I$withval/include"
+ LIBS="$LIBS -lfcgi -L$withval/lib"
+
+- AC_TRY_LINK(
+- [ #include <fcgiapp.h>
+- ],[ FCGX_Init(); ],
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <fcgiapp.h>
++ ]], [[ FCGX_Init(); ]])],[
+ AC_MSG_RESULT(ok)
+- ],
+- [
++ ],[
+ AC_MSG_RESULT(not found)
+ AC_MSG_ERROR(Could not compile FastCGI test.)
+ ])
+@@ -403,7 +389,7 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
+ AC_MSG_CHECKING(for XMLRPC-C)
+
+ AC_ARG_WITH(xmlrpc-c,
+- AC_HELP_STRING([--with-xmlrpc-c=PATH], [enable XMLRPC-C support]),
++ AS_HELP_STRING([--with-xmlrpc-c=PATH],[enable XMLRPC-C support]),
+ [
+ if test "$withval" = "no"; then
+ AC_MSG_RESULT(no)
+@@ -419,12 +405,10 @@ AC_DEFUN([TORRENT_WITH_XMLRPC_C], [
+ CXXFLAGS="$CXXFLAGS `$xmlrpc_cc_prg --cflags server-util`"
+ LIBS="$LIBS `$xmlrpc_cc_prg server-util --libs`"
+
+- AC_TRY_LINK(
+- [ #include <xmlrpc-c/server.h>
+- ],[ xmlrpc_registry_new(NULL); ],
+- [
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <xmlrpc-c/server.h>
++ ]], [[ xmlrpc_registry_new(NULL); ]])],[
+ AC_MSG_RESULT(ok)
+- ], [
++ ],[
+ AC_MSG_RESULT(failed)
+ AC_MSG_ERROR(Could not compile XMLRPC-C test.)
+ ])
+@@ -466,23 +450,23 @@ AC_DEFUN([TORRENT_CHECK_PTHREAD_SETNAME_NP], [
+
+ AC_MSG_CHECKING(for pthread_setname_np type)
+
+- AC_TRY_LINK([
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ #include <pthread.h>
+ #include <sys/types.h>
+- ],[
++ ]], [[
+ pthread_t t;
+ pthread_setname_np(t, "foo");
+- ],[
++ ]])],[
+ AC_DEFINE(HAS_PTHREAD_SETNAME_NP_GENERIC, 1, The function to set pthread name has a pthread_t argumet.)
+ AC_MSG_RESULT(generic)
+ ],[
+- AC_TRY_LINK([
++ AC_LINK_IFELSE([AC_LANG_PROGRAM([[
+ #include <pthread.h>
+ #include <sys/types.h>
+- ],[
++ ]],[[
+ pthread_t t;
+ pthread_setname_np("foo");
+- ],[
++ ]])],[
+ AC_DEFINE(HAS_PTHREAD_SETNAME_NP_DARWIN, 1, The function to set pthread name has no pthread argument.)
+ AC_MSG_RESULT(darwin)
+ ],[
+@@ -495,7 +479,7 @@ AC_DEFUN([TORRENT_DISABLE_PTHREAD_SETNAME_NP], [
+ AC_MSG_CHECKING([for pthread_setname_no])
+
+ AC_ARG_ENABLE(pthread-setname-np,
+- AC_HELP_STRING([--disable-pthread-setname-np], [disable pthread_setname_np]),
++ AS_HELP_STRING([--disable-pthread-setname-np],[disable pthread_setname_np]),
+ [
+ if test "$enableval" = "no"; then
+ AC_MSG_RESULT(disabled)
+diff --git a/scripts/common.m4 b/scripts/common.m4
+index 55e8d66e..2f54402e 100644
+--- a/scripts/common.m4
++++ b/scripts/common.m4
+@@ -1,6 +1,7 @@
+ AC_DEFUN([TORRENT_WITH_SYSROOT], [
+ AC_ARG_WITH(sysroot,
+- AC_HELP_STRING([--with-sysroot=PATH], [compile and link with a specific sysroot]),
++ AS_HELP_STRING([--with-sysroot=PATH],
++ [compile and link with a specific sysroot]),
+ [
+ AC_MSG_CHECKING(for sysroot)
+
+@@ -22,7 +23,8 @@ AC_DEFUN([TORRENT_WITH_SYSROOT], [
+
+ AC_DEFUN([TORRENT_ENABLE_ARCH], [
+ AC_ARG_ENABLE(arch,
+- AC_HELP_STRING([--enable-arch=ARCH], [comma seprated list of architectures to compile for]),
++ AS_HELP_STRING([--enable-arch=ARCH],
++ [comma seprated list of architectures to compile for]),
+ [
+ AC_MSG_CHECKING(for target architectures)
+
+@@ -82,7 +84,8 @@ AC_DEFUN([TORRENT_MINCORE_SIGNEDNESS], [
+
+ AC_DEFUN([TORRENT_MINCORE], [
+ AC_ARG_ENABLE(mincore,
+- AC_HELP_STRING([--disable-mincore], [disable mincore check [[default=enable]]]),
++ AS_HELP_STRING([--disable-mincore],
++ [disable mincore check [[default=enable]]]),
+ [
+ if test "$enableval" = "yes"; then
+ TORRENT_MINCORE_SIGNEDNESS()
+@@ -174,7 +177,8 @@ AC_DEFUN([TORRENT_CHECK_ALIGNED], [
+
+ AC_DEFUN([TORRENT_ENABLE_ALIGNED], [
+ AC_ARG_ENABLE(aligned,
+- AC_HELP_STRING([--enable-aligned], [enable alignment safe code [[default=check]]]),
++ AS_HELP_STRING([--enable-aligned],
++ [enable alignment safe code [[default=check]]]),
+ [
+ if test "$enableval" = "yes"; then
+ AC_DEFINE(USE_ALIGNED, 1, Require byte alignment)
+@@ -189,7 +193,8 @@ AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [
+ AC_MSG_CHECKING([if instrumentation should be included])
+
+ AC_ARG_ENABLE(instrumentation,
+- AC_HELP_STRING([--disable-instrumentation], [disable instrumentation [[default=enabled]]]),
++ AS_HELP_STRING([--disable-instrumentation],
++ [disable instrumentation [[default=enabled]]]),
+ [
+ if test "$enableval" = "yes"; then
+ AC_DEFINE(LT_INSTRUMENTATION, 1, enable instrumentation)
+@@ -206,7 +211,8 @@ AC_DEFUN([TORRENT_DISABLE_INSTRUMENTATION], [
+
+ AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [
+ AC_ARG_ENABLE(interrupt-socket,
+- AC_HELP_STRING([--enable-interrupt-socket], [enable interrupt socket [[default=no]]]),
++ AS_HELP_STRING([--enable-interrupt-socket],
++ [enable interrupt socket [[default=no]]]),
+ [
+ if test "$enableval" = "yes"; then
+ AC_DEFINE(USE_INTERRUPT_SOCKET, 1, Use interrupt socket instead of pthread_kill)
+@@ -214,3 +220,14 @@ AC_DEFUN([TORRENT_ENABLE_INTERRUPT_SOCKET], [
+ ]
+ )
+ ])
++
++AC_DEFUN([TORRENT_DISABLE_IPV6], [
++ AC_ARG_ENABLE(ipv6,
++ AS_HELP_STRING([--enable-ipv6],
++ [enable ipv6 [[default=no]]]),
++ [
++ if test "$enableval" = "yes"; then
++ AC_DEFINE(RAK_USE_INET6, 1, enable ipv6 stuff)
++ fi
++ ])
++])
+diff --git a/scripts/rak_compiler.m4 b/scripts/rak_compiler.m4
+index 9a361bed..bc1572a3 100644
+--- a/scripts/rak_compiler.m4
++++ b/scripts/rak_compiler.m4
+@@ -26,7 +26,7 @@ AC_DEFUN([RAK_CHECK_CXXFLAGS], [
+
+ AC_DEFUN([RAK_ENABLE_DEBUG], [
+ AC_ARG_ENABLE(debug,
+- AC_HELP_STRING([--enable-debug], [enable debug information [[default=yes]]]),
++ AS_HELP_STRING([--enable-debug],[enable debug information [[default=yes]]]),
+ [
+ if test "$enableval" = "yes"; then
+ CXXFLAGS="$CXXFLAGS -g -DDEBUG"
+@@ -41,7 +41,7 @@ AC_DEFUN([RAK_ENABLE_DEBUG], [
+
+ AC_DEFUN([RAK_ENABLE_WERROR], [
+ AC_ARG_ENABLE(werror,
+- AC_HELP_STRING([--enable-werror], [enable the -Werror and -Wall flags [[default -Wall only]]]),
++ AS_HELP_STRING([--enable-werror],[enable the -Werror and -Wall flags [[default -Wall only]]]),
+ [
+ if test "$enableval" = "yes"; then
+ CXXFLAGS="$CXXFLAGS -Werror -Wall"
+@@ -54,7 +54,7 @@ AC_DEFUN([RAK_ENABLE_WERROR], [
+
+ AC_DEFUN([RAK_ENABLE_EXTRA_DEBUG], [
+ AC_ARG_ENABLE(extra-debug,
+- AC_HELP_STRING([--enable-extra-debug], [enable extra debugging checks [[default=no]]]),
++ AS_HELP_STRING([--enable-extra-debug],[enable extra debugging checks [[default=no]]]),
+ [
+ if test "$enableval" = "yes"; then
+ AC_DEFINE(USE_EXTRA_DEBUG, 1, Enable extra debugging checks.)
+diff --git a/scripts/rak_cxx.m4 b/scripts/rak_cxx.m4
+deleted file mode 100644
+index 0db61b83..00000000
+--- a/scripts/rak_cxx.m4
++++ /dev/null
+@@ -1,14 +0,0 @@
+-AC_DEFUN([RAK_CHECK_CXX11], [
+- AC_ARG_ENABLE([c++0x],
+- AC_HELP_STRING([--enable-c++0x], [compile with C++0x (unsupported)]),
+- [
+- if test "$enableval" = "yes"; then
+- AX_CXX_COMPILE_STDCXX_0X
+- else
+- AX_CXX_COMPILE_STDCXX_11(noext)
+- fi
+- ],[
+- AX_CXX_COMPILE_STDCXX_11(noext)
+- ]
+- )
+-])
new file mode 100644
@@ -0,0 +1,29 @@
+From eca577e2a29d64251b5df1c69be53c5b1ffe6bde Mon Sep 17 00:00:00 2001
+From: Jari Sundell <sundell.software@gmail.com>
+Date: Thu, 8 Sep 2022 05:08:44 +0900
+Subject: [PATCH] Create FUNDING.yml
+
+---
+ .github/FUNDING.yml | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+ create mode 100644 .github/FUNDING.yml
+
+diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
+new file mode 100644
+index 00000000..ad5998e8
+--- /dev/null
++++ b/.github/FUNDING.yml
+@@ -0,0 +1,13 @@
++# These are supported funding model platforms
++
++github: [rakshasa]
++patreon: rtorrent
++open_collective: # Replace with a single Open Collective username
++ko_fi: # Replace with a single Ko-fi username
++tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
++community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
++liberapay: # Replace with a single Liberapay username
++issuehunt: # Replace with a single IssueHunt username
++otechie: # Replace with a single Otechie username
++lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
++custom: ['https://rakshasa.github.io/rtorrent/donate.html']
@@ -6,12 +6,57 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=393a5ca445f6965873eca0259a17f833"
DEPENDS = "zlib libsigc++-2.0 openssl cppunit"
-SRC_URI = "git://github.com/rakshasa/libtorrent;branch=master;protocol=https"
-SRCREV = "e60f222241319aaae482789517ad00ae9344bd13"
+SRC_URI = "git://github.com/rakshasa/libtorrent;branch=master;protocol=https \
+ file://0001-Fix-compilation-issue-with-gcc-v6.x-and-empty-CXXFLA.patch \
+ file://0002-Modfiy-gcc-v6.x-fix-for-empty-CXXFLAGS-See-10.patch \
+ file://0003-Add-space-to-fmt-str-in-log_gz_file_write.patch \
+ file://0004-IPv4-filter-enhancement-11IPv4-filter-enhancement-Cl.patch \
+ file://0005-Disable-extents-test-to-pass-TravisCI-See-11.patch \
+ file://0006-Bumped-version-to-0.13.7.patch \
+ file://0007-Added-support-for-openssl-1.1.patch \
+ file://0008-Use-AC_COMPILE-instead-of-AC_RUN-to-check-for-execin.patch \
+ file://0009-Modify-configure-to-prevent-unnecessary-kqueue-check.patch \
+ file://0010-Display-info-on-failed-tracker-bencode-parsing-See-9.patch \
+ file://0011-Strip-tags-also-when-displaying-info-on-failed-track.patch \
+ file://0012-Switch-to-C-11-MRT-RNG-for-random-bytes.patch \
+ file://0013-Prevent-loss-of-m_ipv6_socket-attribute-which-led-to.patch \
+ file://0014-If-during-socket-creation-AF_INET6-failes-initialize.patch \
+ file://0015-Fixes-https-github.com-rakshasa-rtorrent-issues-731.patch \
+ file://0016-Fix-honoring-throttle.min_peers-settings-in-rtorrent.patch \
+ file://0017-increase-piece-length-max.patch \
+ file://0018-Set-max-piece-size-512mb.patch \
+ file://0019-Fixed-compiler-warning.patch \
+ file://0020-Added-_GNU_SOURCE-to-fallocate-test.-neheb.patch \
+ file://0021-Fixed-diffie-hellman-implementation.patch \
+ file://0022-Increased-max-timeout-for-tracker-requests.patch \
+ file://0023-Close-log-files-when-reusing-a-name.patch \
+ file://0024-Bumped-to-version-0.13.8.patch \
+ file://0025-Allow-logs-to-be-appended-rather-than-overwritten.patch \
+ file://0026-Removed-log-append-function.-Added-append-parameter-.patch \
+ file://0027-Backport-changes-from-feature-bind.-200.patch \
+ file://0028-libtorrent.pc.in-add-Libs.Private-202.patch \
+ file://0029-Fix-for-inotify-missing-quickly-renamed-files-203.patch \
+ file://0030-Fix-compiler-warnings.-204.patch \
+ file://0031-Fix-log-format-so-GCC-can-check-it.-205.patch \
+ file://0032-Consolidate-make-script-to-optimize-build.-206.patch \
+ file://0033-Refactor-make-process.-207.patch \
+ file://0034-Changes-automake-required-files.patch \
+ file://0035-Replaced-custom-execinfo-autoconf-test.patch \
+ file://0036-Added-option-to-disable-pthread_setname_np.patch \
+ file://0037-Improved-backtrace-error-checking.patch \
+ file://0038-Fixed-issue-with-multiple-connections-from-NAT-not-w.patch \
+ file://0039-Added-disable-execinfo-option-to-configure.patch \
+ file://0040-Detect-ip-address.patch \
+ file://0041-Added-ipv6-options.patch \
+ file://0042-Removed-obsolete-files.patch \
+ file://0043-Updated-and-cleaned-up-automake.-224.patch \
+ file://0044-Create-FUNDING.yml.patch \
+ "
+SRCREV = "c167c5a9e0bcf0df23ae5efd91396aae0e37eb87"
CVE_STATUS[CVE-2009-1760] = "backported-patch: patched in our product"
-PV = "0.13.8+git${SRCPV}"
+PV = "1"
S = "${WORKDIR}/git"
Hello, this email is a notification from the Auto Upgrade Helper that the automatic attempt to upgrade the recipe *libtorrent* to *1* has Succeeded. Next steps: - apply the patch: git am 0001-libtorrent-upgrade-0.13.8-1.patch - check the changes to upstream patches and summarize them in the commit message, - compile an image that contains the package - perform some basic sanity tests - amend the patch and sign it off: git commit -s --reset-author --amend - send it to the appropriate mailing list Alternatively, if you believe the recipe should not be upgraded at this time, you can fill RECIPE_NO_UPDATE_REASON in respective recipe file so that automatic upgrades would no longer be attempted. Please review the attached files for further information and build/update failures. Any problem please file a bug at https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Automated%20Update%20Handler Regards, The Upgrade Helper -- >8 -- From 83a38bc5603090648c65b7bd15a471401a083a6e Mon Sep 17 00:00:00 2001 From: Upgrade Helper <auh@yoctoproject.org> Date: Fri, 3 Nov 2023 15:07:47 +0000 Subject: [PATCH] libtorrent: upgrade 0.13.8 -> 1 --- ...issue-with-gcc-v6.x-and-empty-CXXFLA.patch | 44 + ...c-v6.x-fix-for-empty-CXXFLAGS-See-10.patch | 56 + ...pace-to-fmt-str-in-log_gz_file_write.patch | 27 + ...ncement-11IPv4-filter-enhancement-Cl.patch | 379 + ...extents-test-to-pass-TravisCI-See-11.patch | 47 + .../0006-Bumped-version-to-0.13.7.patch | 30 + .../0007-Added-support-for-openssl-1.1.patch | 105 + ...nstead-of-AC_RUN-to-check-for-execin.patch | 24 + ...-to-prevent-unnecessary-kqueue-check.patch | 46 + ...failed-tracker-bencode-parsing-See-9.patch | 58 + ...when-displaying-info-on-failed-track.patch | 60 + ...tch-to-C-11-MRT-RNG-for-random-bytes.patch | 45 + ...m_ipv6_socket-attribute-which-led-to.patch | 45 + ...-creation-AF_INET6-failes-initialize.patch | 27 + ...hub.com-rakshasa-rtorrent-issues-731.patch | 29 + ...ottle.min_peers-settings-in-rtorrent.patch | 40 + .../0017-increase-piece-length-max.patch | 22 + .../0018-Set-max-piece-size-512mb.patch | 22 + .../0019-Fixed-compiler-warning.patch | 22 + ..._GNU_SOURCE-to-fallocate-test.-neheb.patch | 74 + ...-Fixed-diffie-hellman-implementation.patch | 293 + ...sed-max-timeout-for-tracker-requests.patch | 64 + ...-Close-log-files-when-reusing-a-name.patch | 123 + .../0024-Bumped-to-version-0.13.8.patch | 30 + ...-be-appended-rather-than-overwritten.patch | 109 + ...nd-function.-Added-append-parameter-.patch | 101 + ...kport-changes-from-feature-bind.-200.patch | 7365 +++++++++++++++++ ...ibtorrent.pc.in-add-Libs.Private-202.patch | 26 + ...fy-missing-quickly-renamed-files-203.patch | 27 + .../0030-Fix-compiler-warnings.-204.patch | 470 ++ ...-log-format-so-GCC-can-check-it.-205.patch | 33 + ...e-make-script-to-optimize-build.-206.patch | 843 ++ .../0033-Refactor-make-process.-207.patch | 2710 ++++++ ...0034-Changes-automake-required-files.patch | 22 + ...placed-custom-execinfo-autoconf-test.patch | 48 + ...option-to-disable-pthread_setname_np.patch | 135 + ...37-Improved-backtrace-error-checking.patch | 594 ++ ...-multiple-connections-from-NAT-not-w.patch | 199 + ...disable-execinfo-option-to-configure.patch | 32 + .../libtorrent/0040-Detect-ip-address.patch | 457 + .../libtorrent/0041-Added-ipv6-options.patch | 85 + .../0042-Removed-obsolete-files.patch | 444 + ...Updated-and-cleaned-up-automake.-224.patch | 2979 +++++++ .../libtorrent/0044-Create-FUNDING.yml.patch | 29 + .../libtorrent/libtorrent_git.bb | 51 +- 45 files changed, 18468 insertions(+), 3 deletions(-) create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0001-Fix-compilation-issue-with-gcc-v6.x-and-empty-CXXFLA.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0002-Modfiy-gcc-v6.x-fix-for-empty-CXXFLAGS-See-10.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0003-Add-space-to-fmt-str-in-log_gz_file_write.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0004-IPv4-filter-enhancement-11IPv4-filter-enhancement-Cl.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0005-Disable-extents-test-to-pass-TravisCI-See-11.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0006-Bumped-version-to-0.13.7.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0007-Added-support-for-openssl-1.1.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0008-Use-AC_COMPILE-instead-of-AC_RUN-to-check-for-execin.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0009-Modify-configure-to-prevent-unnecessary-kqueue-check.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0010-Display-info-on-failed-tracker-bencode-parsing-See-9.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0011-Strip-tags-also-when-displaying-info-on-failed-track.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0012-Switch-to-C-11-MRT-RNG-for-random-bytes.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0013-Prevent-loss-of-m_ipv6_socket-attribute-which-led-to.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0014-If-during-socket-creation-AF_INET6-failes-initialize.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0015-Fixes-https-github.com-rakshasa-rtorrent-issues-731.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0016-Fix-honoring-throttle.min_peers-settings-in-rtorrent.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0017-increase-piece-length-max.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0018-Set-max-piece-size-512mb.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0019-Fixed-compiler-warning.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0020-Added-_GNU_SOURCE-to-fallocate-test.-neheb.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0021-Fixed-diffie-hellman-implementation.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0022-Increased-max-timeout-for-tracker-requests.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0023-Close-log-files-when-reusing-a-name.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0024-Bumped-to-version-0.13.8.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0025-Allow-logs-to-be-appended-rather-than-overwritten.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0026-Removed-log-append-function.-Added-append-parameter-.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0027-Backport-changes-from-feature-bind.-200.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0028-libtorrent.pc.in-add-Libs.Private-202.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0029-Fix-for-inotify-missing-quickly-renamed-files-203.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0030-Fix-compiler-warnings.-204.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0031-Fix-log-format-so-GCC-can-check-it.-205.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0032-Consolidate-make-script-to-optimize-build.-206.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0033-Refactor-make-process.-207.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0034-Changes-automake-required-files.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0035-Replaced-custom-execinfo-autoconf-test.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0036-Added-option-to-disable-pthread_setname_np.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0037-Improved-backtrace-error-checking.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0038-Fixed-issue-with-multiple-connections-from-NAT-not-w.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0039-Added-disable-execinfo-option-to-configure.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0040-Detect-ip-address.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0041-Added-ipv6-options.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0042-Removed-obsolete-files.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0043-Updated-and-cleaned-up-automake.-224.patch create mode 100644 meta-oe/recipes-connectivity/libtorrent/libtorrent/0044-Create-FUNDING.yml.patch