diff options
author | Allen Zhong | 2015-09-28 10:31:22 +0800 |
---|---|---|
committer | Allen Zhong | 2015-09-28 10:31:22 +0800 |
commit | b08ca42d673ba35ac25f2a1f4bf468a5141dba22 (patch) | |
tree | b933374b18436ee7471a735ba047f563ad049f55 | |
download | aur-b08ca42d673ba35ac25f2a1f4bf468a5141dba22.tar.gz |
newpkg: libtorrent-ipv6 0.13.6-1
-rw-r--r-- | .SRCINFO | 18 | ||||
-rw-r--r-- | .gitignore | 19 | ||||
-rw-r--r-- | PKGBUILD | 47 | ||||
-rw-r--r-- | libtorrent-ipv6.patch | 1650 |
4 files changed, 1734 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO new file mode 100644 index 000000000000..7e86ec019bb7 --- /dev/null +++ b/.SRCINFO @@ -0,0 +1,18 @@ +pkgbase = libtorrent-ipv6 + pkgdesc = BitTorrent library with a focus on high performance and good code, with ipv6 support + pkgver = 0.13.6 + pkgrel = 1 + url = http://rakshasa.github.io/rtorrent/ + arch = i686 + arch = x86_64 + license = GPL + depends = openssl + provides = libtorrent + conflicts = libtorrent + source = libtorrent-0.13.6.tar.gz::https://github.com/rakshasa/libtorrent/archive/0.13.6.tar.gz + source = libtorrent-ipv6.patch + sha1sums = 9ca6ca9698f81c758fe934b52374f23588a7cc78 + sha1sums = 3a0925101ada86a4b0cff03ce1c704fd8dfd80a3 + +pkgname = libtorrent-ipv6 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000000..4601b3df4a5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +build/ +test/ +src/ +pkg/ +*.xz +*.gz +*.zip +*.bz2 +*.tgz +*.sig +*.asc +*.swp +cookies + +*.c +*.cpp +*.h +*.hpp +*.o diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 000000000000..c4a784ed2642 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,47 @@ +# $Id$ +# Maintainer: Allen Zhong <moeallenz@gmail.com> +# Contributor: Gaetan Bisson <bisson@archlinux.org> +# Contributor: Jaroslav Lichtblau <svetlemodry@archlinux.org> +# Contributor: Daenyth <Daenyth+Arch [at] gmail [dot] com> +# Contributor: Jeff Mickey <jeff@archlinux.org> +# Contributor: sh__ + +_pkgname=libtorrent +pkgname=libtorrent-ipv6 +pkgver=0.13.6 +pkgrel=1 +pkgdesc='BitTorrent library with a focus on high performance and good code, with ipv6 support' +url='http://rakshasa.github.io/rtorrent/' +arch=('i686' 'x86_64') +license=('GPL') +depends=('openssl') +conflicts=("${_pkgname}") +provides=("${_pkgname}") +source=("$_pkgname-$pkgver.tar.gz::https://github.com/rakshasa/${_pkgname}/archive/${pkgver}.tar.gz" + "${_pkgname}-ipv6.patch") +sha1sums=('9ca6ca9698f81c758fe934b52374f23588a7cc78' + '3a0925101ada86a4b0cff03ce1c704fd8dfd80a3') + +prepare() { + cd "${srcdir}/${_pkgname}-${pkgver}" + patch -uNp1 -i "${srcdir}/${_pkgname}-ipv6.patch" + sed '/AM_PATH_CPPUNIT/d' -i configure.ac +} + +build() { + cd "${srcdir}/${_pkgname}-${pkgver}" + ./autogen.sh + + export CXXFLAGS="${CXXFLAGS} -fno-strict-aliasing" + ./configure \ + --prefix=/usr \ + --disable-debug \ + --enable-ipv6 + + make +} + +package() { + cd "${srcdir}/${_pkgname}-${pkgver}" + make DESTDIR="${pkgdir}" install +} diff --git a/libtorrent-ipv6.patch b/libtorrent-ipv6.patch new file mode 100644 index 000000000000..43e7e060c378 --- /dev/null +++ b/libtorrent-ipv6.patch @@ -0,0 +1,1650 @@ +diff --git a/AUTHORS b/AUTHORS +index 195fc22..f9bdb53 100644 +--- a/AUTHORS ++++ b/AUTHORS +@@ -1 +1 @@ +-Jari Sundell <jaris@ifi.uio.no> ++Jari Sundell <sundell.software@gmail.com> +diff --git a/README b/README +index 6ebca06..76aa08a 100644 +--- a/README ++++ b/README +@@ -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 @@ compiled if the user wishes to avoid using OpenSSL. + + 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 --git a/configure.ac b/configure.ac +index a15b91a..2b3eb7a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -105,7 +105,6 @@ TORRENT_CHECK_EXECINFO() + TORRENT_CHECK_PTHREAD_SETNAME_NP() + TORRENT_MINCORE() + +-TORRENT_DISABLE_IPV6 + TORRENT_DISABLE_INSTRUMENTATION + + LIBTORRENT_LIBS="-ltorrent" +@@ -126,9 +125,10 @@ AC_OUTPUT([ + 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 --git a/rak/socket_address.h b/rak/socket_address.h +index 25fdb37..d1d5b72 100644 +--- a/rak/socket_address.h ++++ b/rak/socket_address.h +@@ -109,13 +109,11 @@ public: + 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 @@ private: + 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 @@ public: + + 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 @@ private: + 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 @@ socket_address::is_valid() const { + 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 @@ socket_address::is_bindable() const { + 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 @@ socket_address::is_address_any() const { + 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 @@ socket_address::port() const { + switch (family()) { + case af_inet: + return sa_inet()->port(); ++ case af_inet6: ++ return sa_inet6()->port(); + default: + return 0; + } +@@ -283,6 +286,8 @@ socket_address::set_port(uint16_t p) { + 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 @@ socket_address::address_str() const { + 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 @@ socket_address::address_c_str(char* buf, socklen_t size) const { + 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 @@ socket_address::set_address_c_str(const char* a) { + 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 @@ socket_address::length() const { + switch(family()) { + case af_inet: + return sizeof(sockaddr_in); ++ case af_inet6: ++ return sizeof(sockaddr_in6); + default: + return 0; + } +@@ -349,8 +364,8 @@ socket_address::operator == (const socket_address& rhs) const { + 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 @@ socket_address::operator < (const socket_address& rhs) const { + 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 @@ socket_address_inet::set_address_c_str(const char* a) { + 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 @@ socket_address_inet::operator < (const socket_address_inet& rhs) const { + 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 --git a/src/Makefile.am b/src/Makefile.am +index 110da5a..99aaace 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -15,6 +15,7 @@ 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 \ +diff --git a/src/dht/dht_node.cc b/src/dht/dht_node.cc +index 69b16db..830b0c9 100644 +--- a/src/dht/dht_node.cc ++++ b/src/dht/dht_node.cc +@@ -54,8 +54,9 @@ DhtNode::DhtNode(const HashString& id, const rak::socket_address* sa) : + 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 @@ DhtNode::store_compact(char* buffer) const { + + 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 --git a/src/dht/dht_server.cc b/src/dht/dht_server.cc +index 844d5f7..1a00908 100644 +--- a/src/dht/dht_server.cc ++++ b/src/dht/dht_server.cc +@@ -701,6 +701,14 @@ DhtServer::event_read() { + 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 --git a/src/net/Makefile.am b/src/net/Makefile.am +index 4e6446c..fb4da4f 100644 +--- a/src/net/Makefile.am ++++ b/src/net/Makefile.am +@@ -4,6 +4,8 @@ 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 \ +diff --git a/src/net/address_list.cc b/src/net/address_list.cc +index 2952149..66b97c1 100644 +--- a/src/net/address_list.cc ++++ b/src/net/address_list.cc +@@ -78,6 +78,16 @@ AddressList::parse_address_compact(raw_string s) { + } + + 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 --git a/src/net/address_list.h b/src/net/address_list.h +index c884da3..d40efd9 100644 +--- a/src/net/address_list.h ++++ b/src/net/address_list.h +@@ -54,6 +54,7 @@ public: + + 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); +@@ -99,6 +100,26 @@ struct SocketAddressCompact { + 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)); ++ + } + + #endif +diff --git a/src/net/listen.cc b/src/net/listen.cc +index 79c52f4..da1c2e8 100644 +--- a/src/net/listen.cc ++++ b/src/net/listen.cc +@@ -61,7 +61,8 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre + 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 @@ Listen::open(uint16_t first, uint16_t last, int backlog, const rak::socket_addre + 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 --git a/src/net/local_addr.cc b/src/net/local_addr.cc +new file mode 100644 +index 0000000..1682e5f +--- /dev/null ++++ b/src/net/local_addr.cc +@@ -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 --git a/src/net/local_addr.h b/src/net/local_addr.h +new file mode 100644 +index 0000000..43bc820 +--- /dev/null ++++ b/src/net/local_addr.h +@@ -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 --git a/src/net/socket_datagram.cc b/src/net/socket_datagram.cc +index 57fbb1f..e7c5e1a 100644 +--- a/src/net/socket_datagram.cc ++++ b/src/net/socket_datagram.cc +@@ -73,7 +73,12 @@ SocketDatagram::write_datagram(const void* buffer, unsigned int length, rak::soc + 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 --git a/src/net/socket_fd.cc b/src/net/socket_fd.cc +index 8c6a477..6238fcf 100644 +--- a/src/net/socket_fd.cc ++++ b/src/net/socket_fd.cc +@@ -70,7 +70,10 @@ SocketFd::set_priority(priority_type p) { + 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 @@ SocketFd::get_error() const { + + 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 @@ bool + 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 @@ bool + 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 @@ bool + 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 @@ SocketFd::accept(rak::socket_address* sa) { + 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 --git a/src/net/socket_fd.h b/src/net/socket_fd.h +index 3435ead..bcb302d 100644 +--- a/src/net/socket_fd.h ++++ b/src/net/socket_fd.h +@@ -79,6 +79,7 @@ public: + 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 @@ private: + inline void check_valid() const; + + int m_fd; ++ bool m_ipv6_socket; + }; + + } +diff --git a/src/torrent/Makefile.am b/src/torrent/Makefile.am +index cea5b72..1bdfde3 100644 +--- a/src/torrent/Makefile.am ++++ b/src/torrent/Makefile.am +@@ -1,6 +1,7 @@ + SUBDIRS = \ + data \ + download \ ++ net \ + peer \ + utils + +diff --git a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc +index 33ae1d9..972dcbf 100644 +--- a/src/torrent/connection_manager.cc ++++ b/src/torrent/connection_manager.cc +@@ -92,13 +92,12 @@ ConnectionManager::ConnectionManager() : + 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 = std::bind(&resolve_host, + std::placeholders::_1, +diff --git a/src/torrent/event.h b/src/torrent/event.h +index c336947..f354976 100644 +--- a/src/torrent/event.h ++++ b/src/torrent/event.h +@@ -60,6 +60,7 @@ public: + + protected: + int m_fileDesc; ++ bool m_ipv6_socket; + }; + + } +diff --git a/src/torrent/net/Makefile.am b/src/torrent/net/Makefile.am +new file mode 100644 +index 0000000..51999d1 +--- /dev/null ++++ b/src/torrent/net/Makefile.am +@@ -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 --git a/src/torrent/net/socket_address_compact.h b/src/torrent/net/socket_address_compact.h +new file mode 100644 +index 0000000..44474ef +--- /dev/null ++++ b/src/torrent/net/socket_address_compact.h +@@ -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 --git a/src/torrent/net/socket_address_key.cc b/src/torrent/net/socket_address_key.cc +new file mode 100644 +index 0000000..f5e0d3b +--- /dev/null ++++ b/src/torrent/net/socket_address_key.cc +@@ -0,0 +1,5 @@ ++// Copyright (C) 2005-2014, Jari Sundell ++// All rights reserved. ++ ++#include "config.h" ++ +diff --git a/src/torrent/net/socket_address_key.h b/src/torrent/net/socket_address_key.h +new file mode 100644 +index 0000000..9d6e0c4 +--- /dev/null ++++ b/src/torrent/net/socket_address_key.h +@@ -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 --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc +index be55400..23ca651 100644 +--- a/src/torrent/peer/peer_list.cc ++++ b/src/torrent/peer/peer_list.cc +@@ -62,28 +62,29 @@ namespace torrent { + + 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 @@ PeerList::set_info(DownloadInfo* info) { + + 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 @@ PeerList::insert_address(const sockaddr* sa, int flags) { + + 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 @@ PeerList::insert_available(const void* al) { + 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 @@ PeerList::insert_available(const void* al) { + 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 @@ PeerList::available_list_size() const { + 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 @@ PeerList::connected(const sockaddr* sa, int flags) { + 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 @@ PeerList::connected(const sockaddr* sa, int flags) { + + 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 --git a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h +index 1d63bc3..a3b409c 100644 +--- a/src/torrent/peer/peer_list.h ++++ b/src/torrent/peer/peer_list.h +@@ -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 @@ class DownloadInfo; + + 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 --git a/src/tracker/tracker_http.cc b/src/tracker/tracker_http.cc +index 6dbd0f5..0bf0f36 100644 +--- a/src/tracker/tracker_http.cc ++++ b/src/tracker/tracker_http.cc +@@ -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" +@@ -141,9 +142,14 @@ TrackerHttp::send_state(int state) { + + 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"; +@@ -332,19 +338,27 @@ TrackerHttp::process_success(const Object& object) { + + 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")); ++ 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")); + +- } catch (bencode_error& e) { +- return receive_failed(e.what()); ++ 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 --git a/src/tracker/tracker_udp.cc b/src/tracker/tracker_udp.cc +index fc76c14..aafca67 100644 +--- a/src/tracker/tracker_udp.cc ++++ b/src/tracker/tracker_udp.cc +@@ -337,11 +337,12 @@ TrackerUdp::prepare_announce_input() { + + 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()); |