File libtorrent-ipv6.patch of Package libtorrent-pyro-git

diff -Naur a/AUTHORS b/AUTHORS
--- a/AUTHORS	2015-09-04 02:30:55.000000000 +0800
+++ b/AUTHORS	2015-11-06 21:14:18.038154781 +0800
@@ -1 +1 @@
-Jari Sundell <jaris@ifi.uio.no>
+Jari Sundell <sundell.software@gmail.com>
diff -Naur a/README b/README
--- a/README	2015-09-04 02:30:55.000000000 +0800
+++ b/README	2015-11-06 21:14:18.038154781 +0800
@@ -1,5 +1,7 @@
 LibTorrent
 
+Copyright (C) 2005-2014, Jari Sundell
+
 LICENSE
 
  GNU GPL, see COPYING. "libtorrent/src/utils/sha_fast.{cc,h}" is
@@ -15,22 +17,11 @@
 
 CONTACT
 
- Send bug reports, suggestions and patches to <jaris@ifi.uio.no> or
-to the mailinglist.
-
-LIBRARY DEPENDENCIES
-
-  g++ >= 4.2.1
-
-SIGC++
+ Jari Sundell
 
- The API will use sigc++ signals to give the client a simple, yet
-powerful way of reacting to events from the library. The client can
-hook multiple slots on each signal and modify the parameters to suit
-the functions. This avoids lots of unnecessary code in the client.
+ Skomakerveien 33
+ 3185 Skoppum, NORWAY
 
-POLLING
+ Send bug reports, suggestions and patches to
+<sundell.software@gmail.com> or to the mailinglist.
 
- "libtorrent/src/torrent/poll.h" provides an abstract class for
-implementing any kind of polling the client wishes to use. Currently
-epoll and select based polling is included.
diff -Naur a/configure.ac b/configure.ac
--- a/configure.ac	2015-09-04 02:30:55.000000000 +0800
+++ b/configure.ac	2015-11-06 21:14:18.039154812 +0800
@@ -104,7 +104,6 @@
 TORRENT_MINCORE()
 TORRENT_OTFD()
 
-TORRENT_DISABLE_IPV6
 TORRENT_DISABLE_INSTRUMENTATION
 
 LIBTORRENT_LIBS="-ltorrent"
@@ -125,9 +124,10 @@
 	Makefile
 	src/Makefile
 	src/torrent/Makefile
-	src/torrent/peer/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
diff -Naur a/rak/socket_address.h b/rak/socket_address.h
--- a/rak/socket_address.h	2015-09-04 02:30:55.000000000 +0800
+++ b/rak/socket_address.h	2015-11-06 21:14:18.039154812 +0800
@@ -109,13 +109,11 @@
   const sockaddr*     c_sockaddr() const                      { return &m_sockaddr; }
   const sockaddr_in*  c_sockaddr_inet() const                 { return &m_sockaddrInet; }
 
-#ifdef RAK_USE_INET6
   socket_address_inet6*       sa_inet6()                      { return reinterpret_cast<socket_address_inet6*>(this); }
   const socket_address_inet6* sa_inet6() const                { return reinterpret_cast<const socket_address_inet6*>(this); }
 
   sockaddr_in6*       c_sockaddr_inet6()                      { return &m_sockaddrInet6; }
   const sockaddr_in6* c_sockaddr_inet6() const                { return &m_sockaddrInet6; }
-#endif
 
   // Copy a socket address which has the length 'length. Zero out any
   // extranous bytes and ensure it does not go beyond the size of this
@@ -139,13 +137,11 @@
   union {
     sockaddr            m_sockaddr;
     sockaddr_in         m_sockaddrInet;
-#ifdef RAK_USE_INET6
     sockaddr_in6        m_sockaddrInet6;
-#endif
   };
 };
 
-// Remeber to set the AF_INET.
+// Remember to set the AF_INET.
 
 class socket_address_inet {
 public:
@@ -184,6 +180,8 @@
 
   const sockaddr*     c_sockaddr() const                      { return reinterpret_cast<const sockaddr*>(&m_sockaddr); }
   const sockaddr_in*  c_sockaddr_inet() const                 { return &m_sockaddr; }
+  
+  socket_address_inet6 to_mapped_address() const;
 
   bool                operator == (const socket_address_inet& rhs) const;
   bool                operator < (const socket_address_inet& rhs) const;
@@ -192,48 +190,47 @@
   struct sockaddr_in  m_sockaddr;
 };
 
-// Unique key for the address, excluding port numbers etc.
-class socket_address_key {
+class socket_address_inet6 {
 public:
-//   socket_address_host_key() {}
+  bool                is_any() const                          { return is_port_any() && is_address_any(); }
+  bool                is_valid() const                        { return !is_port_any() && !is_address_any(); }
+  bool                is_port_any() const                     { return port() == 0; }
+  bool                is_address_any() const                  { return std::memcmp(&m_sockaddr.sin6_addr, &in6addr_any, sizeof(in6_addr)) == 0; }
 
-  socket_address_key(const socket_address& sa) {
-    *this = sa;
-  }
+  void                clear()                                 { std::memset(this, 0, sizeof(socket_address_inet6)); set_family(); }
 
-  socket_address_key& operator = (const socket_address& sa) {
-    if (sa.family() == 0) {
-      std::memset(this, 0, sizeof(socket_address_key));
+  uint16_t            port() const                            { return ntohs(m_sockaddr.sin6_port); }
+  uint16_t            port_n() const                          { return m_sockaddr.sin6_port; }
+  void                set_port(uint16_t p)                    { m_sockaddr.sin6_port = htons(p); }
+  void                set_port_n(uint16_t p)                  { m_sockaddr.sin6_port = p; }
 
-    } else if (sa.family() == socket_address::af_inet) {
-      // Using hardware order as we use operator < to compare when
-      // using inet only.
-      m_addr.s_addr = sa.sa_inet()->address_h();
+  in6_addr            address() const                         { return m_sockaddr.sin6_addr; }
+  const in6_addr*     address_ptr() const                     { return &m_sockaddr.sin6_addr; }
+  std::string         address_str() const;
+  bool                address_c_str(char* buf, socklen_t size) const;
 
-    } else {
-      // When we implement INET6 handling, embed the ipv4 address in
-      // the ipv6 address.
-      throw std::logic_error("socket_address_key(...) received an unsupported protocol family.");
-    }
+  void                set_address(in6_addr a)                 { m_sockaddr.sin6_addr = a; }
+  bool                set_address_str(const std::string& a)   { return set_address_c_str(a.c_str()); }
+  bool                set_address_c_str(const char* a);
 
-    return *this;
-  }
+  void                set_address_any()                       { set_port(0); set_address(in6addr_any); }
 
-//   socket_address_key& operator = (const socket_address_key& sa) {
-//   }
+  sa_family_t         family() const                          { return m_sockaddr.sin6_family; }
+  void                set_family()                            { m_sockaddr.sin6_family = AF_INET6; }
 
-  bool operator < (const socket_address_key& sa) const {
-    // Compare the memory area instead.
-    return m_addr.s_addr < sa.m_addr.s_addr;
-  }    
+  sockaddr*           c_sockaddr()                            { return reinterpret_cast<sockaddr*>(&m_sockaddr); }
+  sockaddr_in6*       c_sockaddr_inet6()                      { return &m_sockaddr; }
+
+  const sockaddr*     c_sockaddr() const                      { return reinterpret_cast<const sockaddr*>(&m_sockaddr); }
+  const sockaddr_in6* c_sockaddr_inet6() const                { return &m_sockaddr; }
+
+  socket_address      normalize_address() const;
+
+  bool                operator == (const socket_address_inet6& rhs) const;
+  bool                operator < (const socket_address_inet6& rhs) const;
 
 private:
-  union {
-    in_addr m_addr;
-// #ifdef RAK_USE_INET6
-//     in_addr6 m_addr6;
-// #endif
-  };
+  struct sockaddr_in6 m_sockaddr;
 };
 
 inline bool
@@ -241,8 +238,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->is_valid();
-//   case af_inet6:
-//     return sa_inet6().is_valid();
+  case af_inet6:
+    return sa_inet6()->is_valid();
   default:
     return false;
   }
@@ -253,6 +250,8 @@
   switch (family()) {
   case af_inet:
     return !sa_inet()->is_address_any();
+  case af_inet6:
+    return !sa_inet6()->is_address_any();
   default:
     return false;
   }
@@ -263,6 +262,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->is_address_any();
+  case af_inet6:
+    return sa_inet6()->is_address_any();
   default:
     return true;
   }
@@ -273,6 +274,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->port();
+  case af_inet6:
+    return sa_inet6()->port();
   default:
     return 0;
   }
@@ -283,6 +286,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->set_port(p);
+  case af_inet6:
+    return sa_inet6()->set_port(p);
   default:
     break;
   }
@@ -293,6 +298,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->address_str();
+  case af_inet6:
+    return sa_inet6()->address_str();
   default:
     return std::string();
   }
@@ -303,6 +310,8 @@
   switch (family()) {
   case af_inet:
     return sa_inet()->address_c_str(buf, size);
+  case af_inet6:
+    return sa_inet6()->address_c_str(buf, size);
   default:
     return false;
   }
@@ -314,6 +323,10 @@
     sa_inet()->set_family();
     return true;
 
+  } else if (sa_inet6()->set_address_c_str(a)) {
+    sa_inet6()->set_family();
+    return true;
+
   } else {
     return false;
   }
@@ -325,6 +338,8 @@
   switch(family()) {
   case af_inet:
     return sizeof(sockaddr_in);
+  case af_inet6:
+    return sizeof(sockaddr_in6);
   default:
     return 0;
   }      
@@ -349,8 +364,8 @@
   switch (family()) {
   case af_inet:
     return *sa_inet() == *rhs.sa_inet();
-//   case af_inet6:
-//     return *sa_inet6() == *rhs.sa_inet6();
+  case af_inet6:
+    return *sa_inet6() == *rhs.sa_inet6();
   default:
     throw std::logic_error("socket_address::operator == (rhs) invalid type comparison.");
   }
@@ -364,8 +379,8 @@
   switch (family()) {
   case af_inet:
     return *sa_inet() < *rhs.sa_inet();
-//   case af_inet6:
-//     return *sa_inet6() < *rhs.sa_inet6();
+  case af_inet6:
+    return *sa_inet6() < *rhs.sa_inet6();
   default:
     throw std::logic_error("socket_address::operator < (rhs) invalid type comparison.");
   }
@@ -391,6 +406,21 @@
   return inet_pton(AF_INET, a, &m_sockaddr.sin_addr);
 }
 
+inline socket_address_inet6
+socket_address_inet::to_mapped_address() const {
+  uint32_t addr32[4];
+  addr32[0] = 0;
+  addr32[1] = 0;
+  addr32[2] = htonl(0xffff);
+  addr32[3] = m_sockaddr.sin_addr.s_addr;
+  
+  socket_address_inet6 sa;
+  sa.clear();
+  sa.set_address(*reinterpret_cast<in6_addr *>(addr32));
+  sa.set_port_n(m_sockaddr.sin_port);
+  return sa;
+}
+
 inline bool
 socket_address_inet::operator == (const socket_address_inet& rhs) const {
   return
@@ -406,6 +436,55 @@
      m_sockaddr.sin_port < rhs.m_sockaddr.sin_port);
 }
 
+inline std::string
+socket_address_inet6::address_str() const {
+  char buf[INET6_ADDRSTRLEN];
+
+  if (!address_c_str(buf, INET6_ADDRSTRLEN))
+    return std::string();
+
+  return std::string(buf);
+}
+
+inline bool
+socket_address_inet6::address_c_str(char* buf, socklen_t size) const {
+  return inet_ntop(family(), &m_sockaddr.sin6_addr, buf, size);
+}
+
+inline bool
+socket_address_inet6::set_address_c_str(const char* a) {
+  return inet_pton(AF_INET6, a, &m_sockaddr.sin6_addr);
+}
+
+inline socket_address
+socket_address_inet6::normalize_address() const {
+  const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(m_sockaddr.sin6_addr.s6_addr);
+  if (addr32[0] == 0 && addr32[1] == 0 && addr32[2] == htonl(0xffff)) {
+    socket_address addr4;
+    addr4.sa_inet()->set_family();
+    addr4.sa_inet()->set_address_n(addr32[3]);
+    addr4.sa_inet()->set_port_n(m_sockaddr.sin6_port);
+    return addr4;
+  }
+  return *reinterpret_cast<const socket_address*>(this);
+}
+
+inline bool
+socket_address_inet6::operator == (const socket_address_inet6& rhs) const {
+  return
+    memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr)) == 0 &&
+    m_sockaddr.sin6_port == rhs.m_sockaddr.sin6_port;
+}
+
+inline bool
+socket_address_inet6::operator < (const socket_address_inet6& rhs) const {
+  int addr_comp = memcmp(&m_sockaddr.sin6_addr, &rhs.m_sockaddr.sin6_addr, sizeof(in6_addr));
+  return
+    addr_comp < 0 ||
+    (addr_comp == 0 ||
+     m_sockaddr.sin6_port < rhs.m_sockaddr.sin6_port);
+}
+
 }
 
 #endif
diff -Naur a/src/Makefile.am b/src/Makefile.am
--- a/src/Makefile.am	2015-09-04 02:30:55.000000000 +0800
+++ b/src/Makefile.am	2015-11-06 21:14:18.039154812 +0800
@@ -15,6 +15,7 @@
 	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 \
diff -Naur a/src/dht/dht_node.cc b/src/dht/dht_node.cc
--- a/src/dht/dht_node.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/dht/dht_node.cc	2015-11-06 21:14:18.040154843 +0800
@@ -54,8 +54,9 @@
   m_recentlyInactive(0),
   m_bucket(NULL) {
 
-  if (sa->family() != rak::socket_address::af_inet)
-    throw resource_error("Address not af_inet");
+  if (sa->family() != rak::socket_address::af_inet &&
+      (sa->family() != rak::socket_address::af_inet6 || !sa->sa_inet6()->is_any()))
+    throw resource_error("Addres not af_inet or in6addr_any");
 }
 
 DhtNode::DhtNode(const std::string& id, const Object& cache) :
@@ -84,8 +85,19 @@
 
 Object*
 DhtNode::store_cache(Object* container) const {
-  container->insert_key("i", m_socketAddress.sa_inet()->address_h());
-  container->insert_key("p", m_socketAddress.sa_inet()->port());
+  if (m_socketAddress.family() == rak::socket_address::af_inet6) {
+    // Currently, all we support is in6addr_any (checked in the constructor),
+    // which is effectively equivalent to this. Note that we need to specify
+    // int64_t explicitly here because a zero constant is special in C++ and
+    // thus we need an explicit match.
+    container->insert_key("i", int64_t(0));
+    container->insert_key("p", m_socketAddress.sa_inet6()->port());
+
+  } else {
+    container->insert_key("i", m_socketAddress.sa_inet()->address_h());
+    container->insert_key("p", m_socketAddress.sa_inet()->port());
+  }
+
   container->insert_key("t", m_lastSeen);
   return container;
 }
diff -Naur a/src/dht/dht_server.cc b/src/dht/dht_server.cc
--- a/src/dht/dht_server.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/dht/dht_server.cc	2015-11-06 21:14:18.040154843 +0800
@@ -701,6 +701,14 @@
       if (read < 0)
         break;
 
+      // We can currently only process mapped-IPv4 addresses, not real IPv6.
+      // Translate them to an af_inet socket_address.
+      if (sa.family() == rak::socket_address::af_inet6)
+        sa = sa.sa_inet6()->normalize_address();
+
+      if (sa.family() != rak::socket_address::af_inet)
+        continue;
+
       total += read;
 
       // If it's not a valid bencode dictionary at all, it's probably not a DHT
diff -Naur a/src/net/Makefile.am b/src/net/Makefile.am
--- a/src/net/Makefile.am	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/Makefile.am	2015-11-06 21:14:18.040154843 +0800
@@ -4,6 +4,8 @@
 	address_list.cc \
 	address_list.h \
 	data_buffer.h \
+	local_addr.cc \
+	local_addr.h \
 	listen.cc \
 	listen.h \
 	protocol_buffer.h \
diff -Naur a/src/net/address_list.cc b/src/net/address_list.cc
--- a/src/net/address_list.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/address_list.cc	2015-11-06 21:14:18.040154843 +0800
@@ -78,6 +78,16 @@
 }
 
 void
+AddressList::parse_address_compact_ipv6(const std::string& s) {
+  if (sizeof(const SocketAddressCompact6) != 18)
+    throw internal_error("ConnectionList::AddressList::parse_address_compact_ipv6(...) bad struct size.");
+
+  std::copy(reinterpret_cast<const SocketAddressCompact6*>(s.c_str()),
+            reinterpret_cast<const SocketAddressCompact6*>(s.c_str() + s.size() - s.size() % sizeof(SocketAddressCompact6)),
+            std::back_inserter(*this));
+}
+
+void
 AddressList::parse_address_bencode(raw_list s) {
   if (sizeof(const SocketAddressCompact) != 6)
     throw internal_error("AddressList::parse_address_bencode(...) bad struct size.");
diff -Naur a/src/net/address_list.h b/src/net/address_list.h
--- a/src/net/address_list.h	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/address_list.h	2015-11-06 21:14:18.041154875 +0800
@@ -54,6 +54,7 @@
 
   void                        parse_address_compact(raw_string s);
   void                        parse_address_compact(const std::string& s);
+  void                        parse_address_compact_ipv6(const std::string& s);
 
 private:
   static rak::socket_address  parse_address(const Object& b);
@@ -97,6 +98,26 @@
   uint16_t port;
 
   const char*         c_str() const { return reinterpret_cast<const char*>(this); }
+} __attribute__ ((packed));
+
+struct SocketAddressCompact6 {
+  SocketAddressCompact6() {}
+  SocketAddressCompact6(in6_addr a, uint16_t p) : addr(a), port(p) {}
+  SocketAddressCompact6(const rak::socket_address_inet6* sa) : addr(sa->address()), port(sa->port_n()) {}
+
+  operator rak::socket_address () const {
+    rak::socket_address sa;
+    sa.sa_inet6()->clear();
+    sa.sa_inet6()->set_port_n(port);
+    sa.sa_inet6()->set_address(addr);
+
+    return sa;
+  }
+
+  in6_addr addr;
+  uint16_t port;
+
+  const char*         c_str() const { return reinterpret_cast<const char*>(this); }
 } __attribute__ ((packed));
 
 }
diff -Naur a/src/net/listen.cc b/src/net/listen.cc
--- a/src/net/listen.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/listen.cc	2015-11-06 21:14:18.041154875 +0800
@@ -61,7 +61,8 @@
   if (first == 0 || first > last)
     throw input_error("Tried to open listening port with an invalid range.");
 
-  if (bindAddress->family() != rak::socket_address::af_inet &&
+  if (bindAddress->family() != 0 &&
+      bindAddress->family() != rak::socket_address::af_inet &&
       bindAddress->family() != rak::socket_address::af_inet6)
     throw input_error("Listening socket must be bound to an inet or inet6 address.");
 
@@ -71,7 +72,13 @@
     throw resource_error("Could not allocate socket for listening.");
 
   rak::socket_address sa;
-  sa.copy(*bindAddress, bindAddress->length());
+
+  // TODO: Temporary until we refactor:
+  if (bindAddress->family() == 0) {
+    sa.sa_inet6()->clear();
+  } else {
+    sa.copy(*bindAddress, bindAddress->length());
+  }
 
   do {
     sa.set_port(first);
diff -Naur a/src/net/local_addr.cc b/src/net/local_addr.cc
--- a/src/net/local_addr.cc	1970-01-01 08:00:00.000000000 +0800
+++ b/src/net/local_addr.cc	2015-11-06 21:14:18.041154875 +0800
@@ -0,0 +1,333 @@
+// 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
+
+#include "config.h"
+
+#include <stdio.h>
+#include <ifaddrs.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) {
+  ifaddrs *ifaddrs;
+  if (getifaddrs(&ifaddrs)) {
+    return false;
+  }
+
+  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:700::"); 
+    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 -Naur a/src/net/local_addr.h b/src/net/local_addr.h
--- a/src/net/local_addr.h	1970-01-01 08:00:00.000000000 +0800
+++ b/src/net/local_addr.h	2015-11-06 21:14:18.041154875 +0800
@@ -0,0 +1,64 @@
+// 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 -Naur a/src/net/socket_datagram.cc b/src/net/socket_datagram.cc
--- a/src/net/socket_datagram.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/socket_datagram.cc	2015-11-06 21:14:18.041154875 +0800
@@ -73,7 +73,12 @@
   int r;
 
   if (sa != NULL) {
-    r = ::sendto(m_fileDesc, buffer, length, 0, sa->sa_inet()->c_sockaddr(), sizeof(rak::socket_address_inet));
+    if (m_ipv6_socket && sa->family() == rak::socket_address::pf_inet) {
+      rak::socket_address_inet6 sa_mapped = sa->sa_inet()->to_mapped_address();
+      r = ::sendto(m_fileDesc, buffer, length, 0, sa_mapped.c_sockaddr(), sizeof(rak::socket_address_inet6));
+    } else {
+      r = ::sendto(m_fileDesc, buffer, length, 0, sa->c_sockaddr(), sa->length());
+    }
   } else {
     r = ::send(m_fileDesc, buffer, length, 0);
   }
diff -Naur a/src/net/socket_fd.cc b/src/net/socket_fd.cc
--- a/src/net/socket_fd.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/socket_fd.cc	2015-11-06 21:14:18.042154905 +0800
@@ -70,7 +70,10 @@
   check_valid();
   int opt = p;
 
-  return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0;
+  if (m_ipv6_socket)
+    return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0;
+  else
+    return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0;
 }
 
 bool
@@ -112,12 +115,32 @@
 
 bool
 SocketFd::open_stream() {
-  return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1;
+  m_fd = socket(rak::socket_address::pf_inet6, SOCK_STREAM, IPPROTO_TCP);
+
+  if (m_fd == -1) {
+    m_ipv6_socket = false;
+    return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1;
+  }
+
+  m_ipv6_socket = true;
+
+  int zero = 0;
+  return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1;
 }
 
 bool
 SocketFd::open_datagram() {
-  return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1;
+  m_fd = socket(rak::socket_address::pf_inet6, SOCK_DGRAM, 0);
+
+  if (m_fd == -1) {
+    m_ipv6_socket = false;
+    return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1;
+  }
+
+  m_ipv6_socket = true;
+
+  int zero = 0;
+  return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1;
 }
 
 bool
@@ -148,6 +171,11 @@
 SocketFd::bind(const rak::socket_address& sa) {
   check_valid();
 
+  if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) {
+    rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address();
+    return !::bind(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped));
+  }
+
   return !::bind(m_fd, sa.c_sockaddr(), sa.length());
 }
 
@@ -155,6 +183,11 @@
 SocketFd::bind(const rak::socket_address& sa, unsigned int length) {
   check_valid();
 
+  if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) {
+    rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address();
+    return !::bind(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped));
+  }
+
   return !::bind(m_fd, sa.c_sockaddr(), length);
 }
 
@@ -162,10 +195,31 @@
 SocketFd::connect(const rak::socket_address& sa) {
   check_valid();
 
+  if (m_ipv6_socket && sa.family() == rak::socket_address::pf_inet) {
+    rak::socket_address_inet6 sa_mapped = sa.sa_inet()->to_mapped_address();
+    return !::connect(m_fd, sa_mapped.c_sockaddr(), sizeof(sa_mapped)) || errno == EINPROGRESS;
+  }
+
   return !::connect(m_fd, sa.c_sockaddr(), sa.length()) || errno == EINPROGRESS;
 }
 
 bool
+SocketFd::getsockname(rak::socket_address *sa) {
+  check_valid();
+
+  socklen_t len = sizeof(rak::socket_address);
+  if (::getsockname(m_fd, sa->c_sockaddr(), &len)) {
+    return false;
+  }
+
+  if (m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) {
+    *sa = sa->sa_inet6()->normalize_address();
+  }
+
+  return true;
+}
+
+bool
 SocketFd::listen(int size) {
   check_valid();
 
@@ -177,7 +231,17 @@
   check_valid();
   socklen_t len = sizeof(rak::socket_address);
 
-  return SocketFd(::accept(m_fd, sa != NULL ? sa->c_sockaddr() : NULL, &len));
+  if (sa == NULL) {
+    return SocketFd(::accept(m_fd, NULL, &len));
+  }
+
+  int fd = ::accept(m_fd, sa->c_sockaddr(), &len);
+
+  if (fd != -1 && m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) {
+    *sa = sa->sa_inet6()->normalize_address();
+  }
+
+  return SocketFd(fd);
 }
 
 // unsigned int
diff -Naur a/src/net/socket_fd.h b/src/net/socket_fd.h
--- a/src/net/socket_fd.h	2015-09-04 02:30:55.000000000 +0800
+++ b/src/net/socket_fd.h	2015-11-06 21:14:18.042154905 +0800
@@ -79,6 +79,7 @@
   bool                bind(const rak::socket_address& sa);
   bool                bind(const rak::socket_address& sa, unsigned int length);
   bool                connect(const rak::socket_address& sa);
+  bool                getsockname(rak::socket_address* sa);
 
   bool                listen(int size);
   SocketFd            accept(rak::socket_address* sa);
@@ -90,6 +91,7 @@
   inline void         check_valid() const;
 
   int                 m_fd;
+  bool                m_ipv6_socket;
 };
 
 }
diff -Naur a/src/torrent/Makefile.am b/src/torrent/Makefile.am
--- a/src/torrent/Makefile.am	2015-09-04 02:30:55.000000000 +0800
+++ b/src/torrent/Makefile.am	2015-11-06 21:14:18.042154905 +0800
@@ -1,6 +1,7 @@
 SUBDIRS = \
 	data \
 	download \
+	net \
 	peer \
 	utils
 
diff -Naur a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc
--- a/src/torrent/connection_manager.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/torrent/connection_manager.cc	2015-11-06 21:14:18.042154905 +0800
@@ -94,13 +94,12 @@
   m_listen_backlog(SOMAXCONN) {
 
   m_bindAddress = (new rak::socket_address())->c_sockaddr();
-  rak::socket_address::cast_from(m_bindAddress)->sa_inet()->clear();
-
   m_localAddress = (new rak::socket_address())->c_sockaddr();
-  rak::socket_address::cast_from(m_localAddress)->sa_inet()->clear();
-
   m_proxyAddress = (new rak::socket_address())->c_sockaddr();
-  rak::socket_address::cast_from(m_proxyAddress)->sa_inet()->clear();
+
+  rak::socket_address::cast_from(m_bindAddress)->clear();
+  rak::socket_address::cast_from(m_localAddress)->clear();
+  rak::socket_address::cast_from(m_proxyAddress)->clear();
 
   m_slot_resolver = tr1::bind(&resolve_host,
                               tr1::placeholders::_1,
diff -Naur a/src/torrent/event.h b/src/torrent/event.h
--- a/src/torrent/event.h	2015-09-04 02:30:55.000000000 +0800
+++ b/src/torrent/event.h	2015-11-06 21:14:18.042154905 +0800
@@ -60,6 +60,7 @@
 
 protected:
   int                 m_fileDesc;
+  bool                m_ipv6_socket;
 };
 
 }
diff -Naur a/src/torrent/net/Makefile.am b/src/torrent/net/Makefile.am
--- a/src/torrent/net/Makefile.am	1970-01-01 08:00:00.000000000 +0800
+++ b/src/torrent/net/Makefile.am	2015-11-06 21:14:18.043154937 +0800
@@ -0,0 +1,11 @@
+noinst_LTLIBRARIES = libsub_torrentnet.la
+
+libsub_torrentnet_la_SOURCES = \
+	socket_address_key.cc \
+	socket_address_key.h
+
+AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/.. -I$(srcdir)/../.. -I$(top_srcdir)
+
+libtorrentincludedir = $(includedir)/torrent/net
+libtorrentinclude_HEADERS = \
+	socket_address_key.h
diff -Naur a/src/torrent/net/socket_address_compact.h b/src/torrent/net/socket_address_compact.h
--- a/src/torrent/net/socket_address_compact.h	1970-01-01 08:00:00.000000000 +0800
+++ b/src/torrent/net/socket_address_compact.h	2015-11-06 21:14:18.043154937 +0800
@@ -0,0 +1,58 @@
+// Copyright (C) 2005-2014, Jari Sundell
+// All rights reserved.
+
+#ifndef LIBTORRENT_UTILS_SOCKET_ADDRESS_COMPACT_H
+#define LIBTORRENT_UTILS_SOCKET_ADDRESS_COMPACT_H
+
+// Unique key for the socket address, excluding port numbers, etc.
+
+// TODO: Add include files...
+
+#include <rak/socket_address.h>
+
+namespace torrent {
+
+struct socket_address_compact {
+  socket_address_compact() {}
+  socket_address_compact(uint32_t a, uint16_t p) : addr(a), port(p) {}
+  socket_address_compact(const rak::socket_address_inet* sa) : addr(sa->address_n()), port(sa->port_n()) {}
+
+  operator rak::socket_address () const {
+    rak::socket_address sa;
+    sa.sa_inet()->clear();
+    sa.sa_inet()->set_port_n(port);
+    sa.sa_inet()->set_address_n(addr);
+
+    return sa;
+  }
+
+  uint32_t addr;
+  uint16_t port;
+
+  // TODO: c_str? should be c_ptr or something.
+  const char*         c_str() const { return reinterpret_cast<const char*>(this); }
+} __attribute__ ((packed));
+
+struct socket_address_compact6 {
+  socket_address_compact6() {}
+  socket_address_compact6(in6_addr a, uint16_t p) : addr(a), port(p) {}
+  socket_address_compact6(const rak::socket_address_inet6* sa) : addr(sa->address()), port(sa->port_n()) {}
+
+  operator rak::socket_address () const {
+    rak::socket_address sa;
+    sa.sa_inet6()->clear();
+    sa.sa_inet6()->set_port_n(port);
+    sa.sa_inet6()->set_address(addr);
+
+    return sa;
+  }
+
+  in6_addr addr;
+  uint16_t port;
+
+  const char*         c_str() const { return reinterpret_cast<const char*>(this); }
+} __attribute__ ((packed));
+
+}
+
+#endif
diff -Naur a/src/torrent/net/socket_address_key.cc b/src/torrent/net/socket_address_key.cc
--- a/src/torrent/net/socket_address_key.cc	1970-01-01 08:00:00.000000000 +0800
+++ b/src/torrent/net/socket_address_key.cc	2015-11-06 21:14:18.043154937 +0800
@@ -0,0 +1,5 @@
+// Copyright (C) 2005-2014, Jari Sundell
+// All rights reserved.
+
+#include "config.h"
+
diff -Naur a/src/torrent/net/socket_address_key.h b/src/torrent/net/socket_address_key.h
--- a/src/torrent/net/socket_address_key.h	1970-01-01 08:00:00.000000000 +0800
+++ b/src/torrent/net/socket_address_key.h	2015-11-06 21:14:18.043154937 +0800
@@ -0,0 +1,126 @@
+// Copyright (C) 2005-2014, Jari Sundell
+// All rights reserved.
+
+#ifndef LIBTORRENT_UTILS_SOCKET_ADDRESS_KEY_H
+#define LIBTORRENT_UTILS_SOCKET_ADDRESS_KEY_H
+
+#include <cstring>
+#include <inttypes.h>
+#include <netinet/in.h>
+
+// Unique key for the socket address, excluding port numbers, etc.
+
+// TODO: Add include files...
+
+namespace torrent {
+
+class socket_address_key {
+public:
+  // TODO: Disable default ctor?
+
+  // socket_address_key(const sockaddr* sa) : m_sockaddr(sa) {}
+
+  bool is_valid() const { return m_family != AF_UNSPEC; }
+
+  // // Rename, add same family, valid inet4/6.
+
+  // TODO: Make from_sockaddr an rvalue reference.
+  static bool is_comparable_sockaddr(const sockaddr* sa);
+
+  static socket_address_key from_sockaddr(const sockaddr* sa);
+  static socket_address_key from_sin_addr(const sockaddr_in& sa);
+  static socket_address_key from_sin6_addr(const sockaddr_in6& sa);
+
+  bool operator < (const socket_address_key& sa) const;
+  bool operator > (const socket_address_key& sa) const;
+  bool operator == (const socket_address_key& sa) const;
+
+private:
+  sa_family_t m_family;
+
+  union {
+    in_addr m_addr;
+    in6_addr m_addr6;
+  };
+} __attribute__ ((packed));
+
+inline bool
+socket_address_key::is_comparable_sockaddr(const sockaddr* sa) {
+  return sa != NULL && (sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+}
+
+// TODO: Require socket length?
+
+inline socket_address_key
+socket_address_key::from_sockaddr(const sockaddr* sa) {
+  socket_address_key result;
+
+  std::memset(&result, 0, sizeof(socket_address_key));
+
+  result.m_family = AF_UNSPEC;
+
+  if (sa == NULL)
+    return result;
+
+  switch (sa->sa_family) {
+  case AF_INET:
+    // Using hardware order to allo for the use of operator < to
+    // sort in lexical order.
+    result.m_family = AF_INET;
+    result.m_addr.s_addr = ntohl(reinterpret_cast<const struct sockaddr_in*>(sa)->sin_addr.s_addr);
+    break;
+
+  case AF_INET6:
+    result.m_family = AF_INET6;
+    result.m_addr6 = reinterpret_cast<const struct sockaddr_in6*>(sa)->sin6_addr;
+    break;
+   
+  default:
+    break;
+  }
+
+  return result;
+}
+
+inline socket_address_key
+socket_address_key::from_sin_addr(const sockaddr_in& sa) {
+  socket_address_key result;
+
+  std::memset(&result, 0, sizeof(socket_address_key));
+
+  result.m_family = AF_INET;
+  result.m_addr.s_addr = ntohl(sa.sin_addr.s_addr);
+
+  return result;
+}
+
+inline socket_address_key
+socket_address_key::from_sin6_addr(const sockaddr_in6& sa) {
+  socket_address_key result;
+
+  std::memset(&result, 0, sizeof(socket_address_key));
+
+  result.m_family = AF_INET6;
+  result.m_addr6 = sa.sin6_addr;
+
+  return result;
+}
+
+inline bool
+socket_address_key::operator < (const socket_address_key& sa) const {
+  return std::memcmp(this, &sa, sizeof(socket_address_key)) < 0;
+}
+
+inline bool
+socket_address_key::operator > (const socket_address_key& sa) const {
+  return std::memcmp(this, &sa, sizeof(socket_address_key)) > 0;
+}
+
+inline bool
+socket_address_key::operator == (const socket_address_key& sa) const {
+  return std::memcmp(this, &sa, sizeof(socket_address_key)) == 0;
+}
+
+}
+
+#endif
diff -Naur a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc
--- a/src/torrent/peer/peer_list.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/torrent/peer/peer_list.cc	2015-11-06 21:14:18.044154968 +0800
@@ -62,28 +62,29 @@
 
 ipv4_table PeerList::m_ipv4_table;
 
+// TODO: Clean up...
 bool
 socket_address_less(const sockaddr* s1, const sockaddr* s2) {
   const rak::socket_address* sa1 = rak::socket_address::cast_from(s1);
   const rak::socket_address* sa2 = rak::socket_address::cast_from(s2);
 
-  if (sa1->family() != sa2->family())
+  if (sa1->family() != sa2->family()) {
     return sa1->family() < sa2->family();
 
-  else if (sa1->family() == rak::socket_address::af_inet)
+  } else if (sa1->family() == rak::socket_address::af_inet) {
     // Sort by hardware byte order to ensure proper ordering for
     // humans.
     return sa1->sa_inet()->address_h() < sa2->sa_inet()->address_h();
 
-  else
-    // When we implement INET6 handling, embed the ipv4 address in
-    // the ipv6 address.
-    throw internal_error("socket_address_key(...) tried to compare an invalid family type.");
-}
+  } else if (sa1->family() == rak::socket_address::af_inet6) {
+    const in6_addr addr1 = sa1->sa_inet6()->address();
+    const in6_addr addr2 = sa2->sa_inet6()->address();
 
-inline bool
-socket_address_key::is_comparable(const sockaddr* sa) {
-  return rak::socket_address::cast_from(sa)->family() == rak::socket_address::af_inet;
+    return memcmp(&addr1, &addr2, sizeof(in6_addr)) < 0;
+
+  } else {
+    throw internal_error("socket_address_key(...) tried to compare an invalid family type.");
+  }
 }
 
 struct peer_list_equal_port : public std::binary_function<PeerList::reference, uint16_t, bool> {
@@ -120,14 +121,17 @@
 
 PeerInfo*
 PeerList::insert_address(const sockaddr* sa, int flags) {
-  if (!socket_address_key::is_comparable(sa)) {
+  socket_address_key sock_key = socket_address_key::from_sockaddr(sa);
+
+  if (sock_key.is_valid() &&
+      !socket_address_key::is_comparable_sockaddr(sa)) {
     LT_LOG_EVENTS("address not comparable", 0);
     return NULL;
   }
 
   const rak::socket_address* address = rak::socket_address::cast_from(sa);
 
-  range_type range = base_type::equal_range(sa);
+  range_type range = base_type::equal_range(sock_key);
 
   // Do some special handling if we got a new port number but the
   // address was present.
@@ -146,7 +150,7 @@
   
   manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info());
 
-  base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo));
+  base_type::insert(range.second, value_type(sock_key, peerInfo));
 
   if ((flags & address_available) && peerInfo->listen_port() != 0) {
     m_available_list->push_back(address);
@@ -186,7 +190,7 @@
   AvailableList::const_iterator availLast = m_available_list->end();
 
   for (; itr != last; itr++) {
-    if (!socket_address_key::is_comparable(itr->c_sockaddr()) || itr->port() == 0) {
+    if (!socket_address_key::is_comparable_sockaddr(itr->c_sockaddr()) || itr->port() == 0) {
       invalid++;
       continue;
     }
@@ -200,11 +204,13 @@
       continue;
     }
 
+    socket_address_key sock_key = socket_address_key::from_sockaddr(itr->c_sockaddr());
+
     // Check if the peerinfo exists, if it does, check if we would
     // ever want to connect. Just update the timer for the last
     // availability notice if the peer isn't really ideal, but might
     // be used in an emergency.
-    range_type range = base_type::equal_range(itr->c_sockaddr());
+    range_type range = base_type::equal_range(sock_key);
 
     if (range.first != range.second) {
       // Add some logic here to select the best PeerInfo, but for now
@@ -252,8 +258,10 @@
 PeerInfo*
 PeerList::connected(const sockaddr* sa, int flags) {
   const rak::socket_address* address = rak::socket_address::cast_from(sa);
+  socket_address_key sock_key = socket_address_key::from_sockaddr(sa);
 
-  if (!socket_address_key::is_comparable(sa))
+  if (!sock_key.is_valid() ||
+      !socket_address_key::is_comparable_sockaddr(sa))
     return NULL;
 
   int filter_value = m_ipv4_table.at(address->sa_inet()->address_h());
@@ -264,14 +272,14 @@
     return NULL;
 
   PeerInfo* peerInfo;
-  range_type range = base_type::equal_range(sa);
+  range_type range = base_type::equal_range(sock_key);
 
   if (range.first == range.second) {
     // Create a new entry.
     peerInfo = new PeerInfo(sa);
     peerInfo->set_flags(filter_value & PeerInfo::mask_ip_table);
 
-    base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo));
+    base_type::insert(range.second, value_type(sock_key, peerInfo));
 
   } else if (!range.first->second->is_connected()) {
     // Use an old entry.
@@ -315,7 +323,9 @@
 
 void
 PeerList::disconnected(PeerInfo* p, int flags) {
-  range_type range = base_type::equal_range(p->socket_address());
+  socket_address_key sock_key = socket_address_key::from_sockaddr(p->socket_address());
+
+  range_type range = base_type::equal_range(sock_key);
   
   iterator itr = std::find_if(range.first, range.second, rak::equal(p, rak::mem_ref(&value_type::second)));
 
diff -Naur a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h
--- a/src/torrent/peer/peer_list.h	2015-09-04 02:30:55.000000000 +0800
+++ b/src/torrent/peer/peer_list.h	2015-11-06 21:14:18.044154968 +0800
@@ -39,6 +39,7 @@
 
 #include <map>
 #include <torrent/common.h>
+#include <torrent/net/socket_address_key.h>
 #include <torrent/utils/extents.h>
 
 namespace torrent {
@@ -47,21 +48,6 @@
 
 typedef extents<uint32_t, int, 32, 256, 8> ipv4_table;
 
-bool socket_address_less(const sockaddr* s1, const sockaddr* s2);
-
-// Unique key for the address, excluding port numbers etc.
-class LIBTORRENT_EXPORT socket_address_key {
-public:
-  socket_address_key(const sockaddr* sa) : m_sockaddr(sa) {}
-
-  inline static bool is_comparable(const sockaddr* sa);
-
-  bool operator < (const socket_address_key& sa) const { return socket_address_less(m_sockaddr, sa.m_sockaddr); }
-
-private:
-  const sockaddr*     m_sockaddr;
-};
-
 class LIBTORRENT_EXPORT PeerList : private std::multimap<socket_address_key, PeerInfo*> {
 public:
   friend class DownloadWrapper;
diff -Naur a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc
--- a/src/tracker/tracker_http.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/tracker/tracker_http.cc	2015-11-06 21:14:18.044154968 +0800
@@ -44,6 +44,7 @@
 #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"
@@ -143,9 +144,14 @@
 
   const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address());
 
-  if (localAddress->family() == rak::socket_address::af_inet &&
-      !localAddress->sa_inet()->is_address_any())
+  if (!localAddress->is_address_any())
     s << "&ip=" << localAddress->address_str();
+  
+  if (localAddress->is_address_any() || localAddress->family() != rak::socket_address::pf_inet6) {
+    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 (info->is_compact())
     s << "&compact=1";
@@ -334,19 +340,27 @@
 
   AddressList l;
 
-  try {
-    // Due to some trackers sending the wrong type when no peers are
-    // available, don't bork on it.
-    if (object.get_key("peers").is_string())
-      l.parse_address_compact(object.get_key_string("peers"));
+  if (!object.has_key("peers") && !object.has_key("peers6"))
+    return receive_failed("No peers returned");
 
-    else if (object.get_key("peers").is_list())
-      l.parse_address_normal(object.get_key_list("peers"));
-
-  } catch (bencode_error& e) {
-    return receive_failed(e.what());
+  if (object.has_key("peers")) {
+    try {
+      // Due to some trackers sending the wrong type when no peers are
+      // available, don't bork on it.
+      if (object.get_key("peers").is_string())
+        l.parse_address_compact(object.get_key_string("peers"));
+
+      else if (object.get_key("peers").is_list())
+        l.parse_address_normal(object.get_key_list("peers"));
+
+    } catch (bencode_error& e) {
+      return receive_failed(e.what());
+    }
   }
 
+  if (object.has_key("peers6"))
+    l.parse_address_compact_ipv6(object.get_key_string("peers6"));
+
   close_directly();
   m_parent->receive_success(this, &l);
 }
diff -Naur a/src/tracker/tracker_udp.cc b/src/tracker/tracker_udp.cc
--- a/src/tracker/tracker_udp.cc	2015-09-04 02:30:55.000000000 +0800
+++ b/src/tracker/tracker_udp.cc	2015-11-06 21:14:18.044154968 +0800
@@ -324,11 +324,12 @@
 
   const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address());
 
-  // This code assumes we're have a inet address.
-  if (localAddress->family() != rak::socket_address::af_inet)
-    throw internal_error("TrackerUdp::prepare_announce_input() info->local_address() not of family AF_INET.");
+  uint32_t local_addr = 0;
 
-  m_writeBuffer->write_32_n(localAddress->sa_inet()->address_n());
+  if (localAddress->family() == rak::socket_address::af_inet)
+    local_addr = localAddress->sa_inet()->address_n();
+
+  m_writeBuffer->write_32_n(local_addr);
   m_writeBuffer->write_32(m_parent->key());
   m_writeBuffer->write_32(m_parent->numwant());
   m_writeBuffer->write_16(manager->connection_manager()->listen_port());
openSUSE Build Service is sponsored by