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

diff -Naur a/AUTHORS b/AUTHORS
--- a/AUTHORS	2015-09-04 03:03:45.000000000 +0800
+++ b/AUTHORS	2015-11-06 21:12:08.793123648 +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 03:03:45.000000000 +0800
+++ b/README	2015-11-06 21:12:08.793123648 +0800
@@ -32,5 +32,10 @@
 
 CONTACT
 
- Send bug reports, suggestions and patches to <jaris@ifi.uio.no> or
-to the mailinglist.
+ Jari Sundell
+
+ Skomakerveien 33
+ 3185 Skoppum, NORWAY
+
+ Send bug reports, suggestions and patches to
+<sundell.software@gmail.com> or to the mailinglist.
diff -Naur a/rak/socket_address.h b/rak/socket_address.h
--- a/rak/socket_address.h	2015-09-04 03:03:45.000000000 +0800
+++ b/rak/socket_address.h	2015-11-06 21:12:08.794123680 +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/command_download.cc b/src/command_download.cc
--- a/src/command_download.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/command_download.cc	2015-11-06 21:12:08.794123680 +0800
@@ -308,7 +308,10 @@
   if (download->download()->info()->is_private())
     throw torrent::input_error("Download is private.");
 
-  ret = std::sscanf(arg.c_str(), "%1023[^:]:%i%c", host, &port, &dummy);
+  ret = std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", host, &port, &dummy);
+
+  if (ret < 1)
+    ret = std::sscanf(arg.c_str(), "%1023[^:]:%i%c", host, &port, &dummy);
 
   if (ret == 1)
     port = 6881;
@@ -318,7 +321,7 @@
   if (port < 1 || port > 65535)
     throw torrent::input_error("Invalid port number.");
 
-  torrent::connection_manager()->resolver()(host, (int)rak::socket_address::pf_inet, SOCK_STREAM, call_add_d_peer_t(download, port));
+  torrent::connection_manager()->resolver()(host, (int)rak::socket_address::pf_unspec, SOCK_STREAM, call_add_d_peer_t(download, port));
 }
 
 torrent::Object
diff -Naur a/src/command_network.cc b/src/command_network.cc
--- a/src/command_network.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/command_network.cc	2015-11-06 21:12:08.794123680 +0800
@@ -172,8 +172,9 @@
         lt_log_print(torrent::LOG_RPC_EVENTS,
                      "The SCGI socket has not been bound to any address and likely poses a security risk.");
 
-      } else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2) {
-        if ((err = rak::address_info::get_address_info(address, PF_INET, SOCK_STREAM, &ai)) != 0)
+      } else if (std::sscanf(arg.c_str(), "%1023[^:]:%i%c", address, &port, &dummy) == 2 ||
+                 std::sscanf(arg.c_str(), "[%64[^]]]:%i%c", address, &port, &dummy) == 2) { // [xx::xx]:port format
+        if ((err = rak::address_info::get_address_info(address,PF_UNSPEC, SOCK_STREAM, &ai)) != 0)
           throw torrent::input_error("Could not bind address: " + std::string(rak::address_info::strerror(err)) + ".");
 
         saPtr = ai->address();
diff -Naur a/src/command_peer.cc b/src/command_peer.cc
--- a/src/command_peer.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/command_peer.cc	2015-11-06 21:12:08.795123711 +0800
@@ -69,7 +69,12 @@
 
 torrent::Object
 retrieve_p_address(torrent::Peer* peer) {
-  return rak::socket_address::cast_from(peer->peer_info()->socket_address())->address_str();
+  const rak::socket_address *addr = rak::socket_address::cast_from(peer->peer_info()->socket_address());
+
+  if (addr->family() == rak::socket_address::af_inet6)
+    return "[" + addr->address_str() + "]";
+  else
+    return addr->address_str();
 }
 
 torrent::Object
diff -Naur a/src/core/curl_get.cc b/src/core/curl_get.cc
--- a/src/core/curl_get.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/core/curl_get.cc	2015-11-06 21:12:08.795123711 +0800
@@ -91,9 +91,13 @@
   curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL,       (long)1);
   curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, (long)1);
   curl_easy_setopt(m_handle, CURLOPT_MAXREDIRS,      (long)5);
-  curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE,      CURL_IPRESOLVE_V4);
+
+  curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE,      CURL_IPRESOLVE_WHATEVER);
+
   curl_easy_setopt(m_handle, CURLOPT_ENCODING,       "");
 
+  m_ipv6 = false;
+
   m_stack->add_get(this);
 }
 
@@ -111,6 +115,17 @@
 }
 
 void
+CurlGet::retry_ipv6() {
+  CURL* nhandle = curl_easy_duphandle(m_handle);
+
+  curl_easy_setopt(nhandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
+  curl_easy_cleanup(m_handle);
+
+  m_handle = nhandle;
+  m_ipv6 = true;
+}
+
+void
 CurlGet::receive_timeout() {
   return m_stack->transfer_done(m_handle, "Timed out");
 }
diff -Naur a/src/core/curl_get.h b/src/core/curl_get.h
--- a/src/core/curl_get.h	2015-09-04 03:03:45.000000000 +0800
+++ b/src/core/curl_get.h	2015-11-06 21:12:08.795123711 +0800
@@ -58,6 +58,9 @@
   void               start();
   void               close();
 
+  bool               is_using_ipv6()    { return m_ipv6; }
+  void               retry_ipv6();
+
   bool               is_busy() const    { return m_handle; }
   bool               is_active() const  { return m_active; }
 
@@ -75,6 +78,7 @@
   void               receive_timeout();
 
   bool               m_active;
+  bool               m_ipv6;
 
   rak::priority_item m_taskTimeout;
   
diff -Naur a/src/core/curl_stack.cc b/src/core/curl_stack.cc
--- a/src/core/curl_stack.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/core/curl_stack.cc	2015-11-06 21:12:08.795123711 +0800
@@ -131,8 +131,23 @@
   if (msg->msg != CURLMSG_DONE)
     throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE.");
 
-  transfer_done(msg->easy_handle,
-                msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result));
+  if (msg->data.result == CURLE_COULDNT_RESOLVE_HOST) {
+    iterator itr = std::find_if(begin(), end(), rak::equal(msg->easy_handle, std::mem_fun(&CurlGet::handle)));
+ 
+    if (itr == end())
+      throw torrent::internal_error("Could not find CurlGet when calling CurlStack::receive_action.");
+ 
+    if (!(*itr)->is_using_ipv6()) {
+      (*itr)->retry_ipv6();
+
+      if (curl_multi_add_handle((CURLM*)m_handle, (*itr)->handle()) > 0)
+        throw torrent::internal_error("Error calling curl_multi_add_handle.");
+    }
+
+  } else {
+    transfer_done(msg->easy_handle,
+                  msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result));
+  }
 
   return remaining_msgs != 0;
 }
diff -Naur a/src/core/manager.cc b/src/core/manager.cc
--- a/src/core/manager.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/core/manager.cc	2015-11-06 21:12:08.796123742 +0800
@@ -223,7 +223,8 @@
   int err;
   rak::address_info* ai;
 
-  if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0)
+  if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
+      (err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
     throw torrent::input_error("Could not set bind address: " + std::string(rak::address_info::strerror(err)) + ".");
   
   try {
@@ -257,7 +258,8 @@
   int err;
   rak::address_info* ai;
 
-  if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0)
+  if ((err = rak::address_info::get_address_info(addr.c_str(), PF_INET, SOCK_STREAM, &ai)) != 0 &&
+      (err = rak::address_info::get_address_info(addr.c_str(), PF_INET6, SOCK_STREAM, &ai)) != 0)
     throw torrent::input_error("Could not set local address: " + std::string(rak::address_info::strerror(err)) + ".");
   
   try {
diff -Naur a/src/display/window_peer_list.cc b/src/display/window_peer_list.cc
--- a/src/display/window_peer_list.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/display/window_peer_list.cc	2015-11-06 21:12:08.796123742 +0800
@@ -68,7 +68,7 @@
   int x = 2;
   int y = 0;
 
-  m_canvas->print(x, y, "IP");     x += 16;
+  m_canvas->print(x, y, "IP");      x += 25;
   m_canvas->print(x, y, "UP");      x += 7;
   m_canvas->print(x, y, "DOWN");    x += 7;
   m_canvas->print(x, y, "PEER");    x += 7;
@@ -99,10 +99,16 @@
 
     x = 0;
 
+    std::string ip_address = rak::socket_address::cast_from(p->address())->address_str();
+
+    if (ip_address.size() >= 24) {
+      ip_address.replace(ip_address.begin() + 21, ip_address.end(), "...");
+    }
+
     m_canvas->print(x, y, "%c %s",
                     range.first == *m_focus ? '*' : ' ',
-                    rak::socket_address::cast_from(p->address())->address_str().c_str());
-    x += 18;
+                    ip_address.c_str());
+    x += 27;
 
     m_canvas->print(x, y, "%.1f", (double)p->up_rate()->rate() / 1024); x += 7;
     m_canvas->print(x, y, "%.1f", (double)p->down_rate()->rate() / 1024); x += 7;
diff -Naur a/src/main.cc b/src/main.cc
--- a/src/main.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/main.cc	2015-11-06 21:12:08.796123742 +0800
@@ -273,6 +273,7 @@
 
        "method.set_key = event.download.resumed,   !_timestamp, ((d.timestamp.started.set_if_z, ((system.time)) ))\n"
        "method.set_key = event.download.finished,  !_timestamp, ((d.timestamp.finished.set_if_z, ((system.time)) ))\n"
+       "method.set_key = event.download.hash_done, !_timestamp, {(branch,((d.complete)),((d.timestamp.finished.set_if_z,(system.time))))}\n"
 
        "method.insert.c_simple = group.insert_persistent_view,"
        "((view.add,((argument.0)))),((view.persistent,((argument.0)))),((group.insert,((argument.0)),((argument.0))))\n"
diff -Naur a/src/utils/socket_fd.cc b/src/utils/socket_fd.cc
--- a/src/utils/socket_fd.cc	2015-09-04 03:03:45.000000000 +0800
+++ b/src/utils/socket_fd.cc	2015-11-06 21:12:08.796123742 +0800
@@ -71,7 +71,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
@@ -130,12 +133,30 @@
 
 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
@@ -153,6 +174,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());
 }
 
@@ -160,6 +186,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);
 }
 
@@ -167,10 +198,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();
 
@@ -182,7 +234,14 @@
   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/utils/socket_fd.h b/src/utils/socket_fd.h
--- a/src/utils/socket_fd.h	2015-09-04 03:03:45.000000000 +0800
+++ b/src/utils/socket_fd.h	2015-11-06 21:12:08.797123773 +0800
@@ -80,6 +80,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);
@@ -91,6 +92,7 @@
   inline void         check_valid() const;
 
   int                 m_fd;
+  bool                m_ipv6_socket;
 };
 
 }
openSUSE Build Service is sponsored by