diff -ur rtorrent-0.8.6.orig/rak/socket_address.h rtorrent-0.8.6/rak/socket_address.h --- rtorrent-0.8.6.orig/rak/socket_address.h 2009-11-12 09:03:48.000000000 +0100 +++ rtorrent-0.8.6/rak/socket_address.h 2010-07-23 14:45:07.000000000 +0200 @@ -145,7 +145,7 @@ }; }; -// Remeber to set the AF_INET. +// Remember to set the AF_INET. class socket_address_inet { public: @@ -184,6 +184,10 @@ const sockaddr* c_sockaddr() const { return reinterpret_cast(&m_sockaddr); } const sockaddr_in* c_sockaddr_inet() const { return &m_sockaddr; } + +#ifdef RAK_USE_INET6 + socket_address_inet6 to_mapped_address() const; +#endif bool operator == (const socket_address_inet& rhs) const; bool operator < (const socket_address_inet& rhs) const; @@ -192,6 +196,52 @@ struct sockaddr_in m_sockaddr; }; +#ifdef RAK_USE_INET6 +// Remember to set the AF_INET6. + +class socket_address_inet6 { +public: + 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; } + + void clear() { std::memset(this, 0, sizeof(socket_address_inet6)); set_family(); } + + 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; } + + in6_addr address() const { return m_sockaddr.sin6_addr; } + std::string address_str() const; + bool address_c_str(char* buf, socklen_t size) const; + + 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); + + void set_address_any() { set_port(0); set_address(in6addr_any); } + + sa_family_t family() const { return m_sockaddr.sin6_family; } + void set_family() { m_sockaddr.sin6_family = AF_INET6; } + + sockaddr* c_sockaddr() { return reinterpret_cast(&m_sockaddr); } + sockaddr_in6* c_sockaddr_inet6() { return &m_sockaddr; } + + const sockaddr* c_sockaddr() const { return reinterpret_cast(&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: + struct sockaddr_in6 m_sockaddr; +}; +#endif + // Unique key for the address, excluding port numbers etc. class socket_address_key { public: @@ -241,8 +291,10 @@ switch (family()) { case af_inet: return sa_inet()->is_valid(); -// case af_inet6: -// return sa_inet6().is_valid(); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->is_valid(); +#endif default: return false; } @@ -253,6 +305,10 @@ switch (family()) { case af_inet: return !sa_inet()->is_address_any(); +#ifdef RAK_USE_INET6 + case af_inet6: + return !sa_inet6()->is_address_any(); +#endif default: return false; } @@ -263,6 +319,10 @@ switch (family()) { case af_inet: return sa_inet()->is_address_any(); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->is_address_any(); +#endif default: return true; } @@ -273,6 +333,10 @@ switch (family()) { case af_inet: return sa_inet()->port(); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->port(); +#endif default: return 0; } @@ -283,6 +347,10 @@ switch (family()) { case af_inet: return sa_inet()->set_port(p); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->set_port(p); +#endif default: break; } @@ -293,6 +361,10 @@ switch (family()) { case af_inet: return sa_inet()->address_str(); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->address_str(); +#endif default: return std::string(); } @@ -303,6 +375,10 @@ switch (family()) { case af_inet: return sa_inet()->address_c_str(buf, size); +#ifdef RAK_USE_INET6 + case af_inet6: + return sa_inet6()->address_c_str(buf, size); +#endif default: return false; } @@ -314,6 +390,12 @@ sa_inet()->set_family(); return true; +#ifdef RAK_USE_INET6 + } else if (sa_inet6()->set_address_c_str(a)) { + sa_inet6()->set_family(); + return true; +#endif + } else { return false; } @@ -325,6 +407,10 @@ switch(family()) { case af_inet: return sizeof(sockaddr_in); +#ifdef RAK_USE_INET6 + case af_inet6: + return sizeof(sockaddr_in6); +#endif default: return 0; } @@ -349,8 +435,10 @@ switch (family()) { case af_inet: return *sa_inet() == *rhs.sa_inet(); -// case af_inet6: -// return *sa_inet6() == *rhs.sa_inet6(); +#ifdef RAK_USE_INET6 + case af_inet6: + return *sa_inet6() == *rhs.sa_inet6(); +#endif default: throw std::logic_error("socket_address::operator == (rhs) invalid type comparison."); } @@ -364,8 +452,10 @@ switch (family()) { case af_inet: return *sa_inet() < *rhs.sa_inet(); -// case af_inet6: -// return *sa_inet6() < *rhs.sa_inet6(); +#ifdef RAK_USE_INET6 + case af_inet6: + return *sa_inet6() < *rhs.sa_inet6(); +#endif default: throw std::logic_error("socket_address::operator < (rhs) invalid type comparison."); } @@ -391,6 +481,23 @@ return inet_pton(AF_INET, a, &m_sockaddr.sin_addr); } +#ifdef RAK_USE_INET6 +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(addr32)); + sa.set_port_n(m_sockaddr.sin_port); + return sa; +} +#endif + inline bool socket_address_inet::operator == (const socket_address_inet& rhs) const { return @@ -406,6 +513,59 @@ m_sockaddr.sin_port < rhs.m_sockaddr.sin_port); } +#ifdef RAK_USE_INET6 + +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(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(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 + } #endif diff -ur rtorrent-0.8.6.orig/src/command_peer.cc rtorrent-0.8.6/src/command_peer.cc --- rtorrent-0.8.6.orig/src/command_peer.cc 2009-11-12 09:03:48.000000000 +0100 +++ rtorrent-0.8.6/src/command_peer.cc 2010-07-23 14:45:07.000000000 +0200 @@ -68,7 +68,13 @@ 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()); +#ifdef RAK_USE_INET6 + if (addr->family() == rak::socket_address::af_inet6) + return "[" + addr->address_str() + "]"; + else +#endif + return addr->address_str(); } torrent::Object diff -ur rtorrent-0.8.6.orig/src/core/curl_get.cc rtorrent-0.8.6/src/core/curl_get.cc --- rtorrent-0.8.6.orig/src/core/curl_get.cc 2009-11-12 09:03:44.000000000 +0100 +++ rtorrent-0.8.6/src/core/curl_get.cc 2010-07-23 14:45:07.000000000 +0200 @@ -88,8 +88,20 @@ 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); + + // Even when IPv6-enabled, we don't want to use CURL_IPRESOLVE_WHATEVER, + // since that will usually prefer connecting over IPv6 to the tracker. + // Since it's usually a lot easier to find our global IPv6 address + // (if we have one) than our global IPv4 address, we prefer connecting + // over IPv4 if we can, so that the tracker will get our IPv4 address + // that way. If the resolve fails, CurlStack will call retry_ipv6() + // on us and we'll make a second attempt with CURL_IPRESOLVE_V6. curl_easy_setopt(m_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(m_handle, CURLOPT_ENCODING, ""); +#ifdef RAK_USE_INET6 + m_ipv6 = false; +#endif m_stack->add_get(this); } @@ -107,6 +119,17 @@ m_handle = NULL; } +#ifdef RAK_USE_INET6 +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; +} +#endif + void CurlGet::receive_timeout() { return m_stack->transfer_done(m_handle, "Timed out"); diff -ur rtorrent-0.8.6.orig/src/core/curl_get.h rtorrent-0.8.6/src/core/curl_get.h --- rtorrent-0.8.6.orig/src/core/curl_get.h 2009-11-12 09:03:44.000000000 +0100 +++ rtorrent-0.8.6/src/core/curl_get.h 2010-07-23 14:45:07.000000000 +0200 @@ -56,6 +56,10 @@ void start(); void close(); +#ifdef RAK_USE_INET6 + bool is_using_ipv6() { return m_ipv6; } + void retry_ipv6(); +#endif bool is_busy() const { return m_handle; } bool is_active() const { return m_active; } @@ -74,6 +78,9 @@ void receive_timeout(); bool m_active; +#ifdef RAK_USE_INET6 + bool m_ipv6; +#endif rak::priority_item m_taskTimeout; diff -ur rtorrent-0.8.6.orig/src/core/curl_stack.cc rtorrent-0.8.6/src/core/curl_stack.cc --- rtorrent-0.8.6.orig/src/core/curl_stack.cc 2009-11-12 09:03:44.000000000 +0100 +++ rtorrent-0.8.6/src/core/curl_stack.cc 2010-07-23 14:45:07.000000000 +0200 @@ -111,6 +111,21 @@ if (msg->msg != CURLMSG_DONE) throw torrent::internal_error("CurlStack::receive_action() msg->msg != CURLMSG_DONE."); +#ifdef RAK_USE_INET6 + 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."); + continue; + } + } else +#endif transfer_done(msg->easy_handle, msg->data.result == CURLE_OK ? NULL : curl_easy_strerror(msg->data.result)); } diff -ur rtorrent-0.8.6.orig/src/display/window_peer_list.cc rtorrent-0.8.6/src/display/window_peer_list.cc --- rtorrent-0.8.6.orig/src/display/window_peer_list.cc 2009-11-12 09:03:47.000000000 +0100 +++ rtorrent-0.8.6/src/display/window_peer_list.cc 2010-07-23 14:45:07.000000000 +0200 @@ -68,7 +68,11 @@ int x = 2; int y = 0; - m_canvas->print(x, y, "IP"); x += 16; +#ifdef RAK_USE_INET6 + m_canvas->print(x, y, "IP"); x += 25; +#else + m_canvas->print(x, y, "IP"); x += 16; +#endif 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 +103,21 @@ x = 0; + std::string ip_address = rak::socket_address::cast_from(p->address())->address_str(); +#ifdef RAK_USE_INET6 + if (ip_address.size() >= 24) { + ip_address.replace(ip_address.begin() + 21, ip_address.end(), "..."); + } +#endif + m_canvas->print(x, y, "%c %s", range.first == *m_focus ? '*' : ' ', - rak::socket_address::cast_from(p->address())->address_str().c_str()); + ip_address.c_str()); +#ifdef RAK_USE_INET6 + x += 27; +#else x += 18; +#endif 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 -ur rtorrent-0.8.6.orig/src/utils/socket_fd.cc rtorrent-0.8.6/src/utils/socket_fd.cc --- rtorrent-0.8.6.orig/src/utils/socket_fd.cc 2009-11-12 09:03:46.000000000 +0100 +++ rtorrent-0.8.6/src/utils/socket_fd.cc 2010-07-23 23:11:07.000000000 +0200 @@ -71,6 +71,11 @@ check_valid(); int opt = p; +#ifdef RAK_USE_INET6 + if (m_ipv6_socket) + return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0; + else +#endif return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0; } @@ -130,12 +135,36 @@ bool SocketFd::open_stream() { +#ifdef RAK_USE_INET6 + 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; +#else return (m_fd = socket(rak::socket_address::pf_inet, SOCK_STREAM, IPPROTO_TCP)) != -1; +#endif } bool SocketFd::open_datagram() { +#ifdef RAK_USE_INET6 + 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; +#else return (m_fd = socket(rak::socket_address::pf_inet, SOCK_DGRAM, 0)) != -1; +#endif } bool @@ -153,6 +182,12 @@ SocketFd::bind(const rak::socket_address& sa) { check_valid(); +#ifdef RAK_USE_INET6 + 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)); + } +#endif return !::bind(m_fd, sa.c_sockaddr(), sa.length()); } @@ -160,6 +195,12 @@ SocketFd::bind(const rak::socket_address& sa, unsigned int length) { check_valid(); +#ifdef RAK_USE_INET6 + 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)); + } +#endif return !::bind(m_fd, sa.c_sockaddr(), length); } @@ -167,10 +208,34 @@ SocketFd::connect(const rak::socket_address& sa) { check_valid(); +#ifdef RAK_USE_INET6 + 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; + } +#endif 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; + } + +#ifdef RAK_USE_INET6 + if (m_ipv6_socket && sa->family() == rak::socket_address::af_inet6) { + *sa = sa->sa_inet6()->normalize_address(); + } +#endif + + return true; +} + +bool SocketFd::listen(int size) { check_valid(); @@ -182,7 +247,18 @@ check_valid(); socklen_t len = sizeof(rak::socket_address); +#ifdef RAK_USE_INET6 + 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); +#else return SocketFd(::accept(m_fd, sa != NULL ? sa->c_sockaddr() : NULL, &len)); +#endif } // unsigned int diff -ur rtorrent-0.8.6.orig/src/utils/socket_fd.h rtorrent-0.8.6/src/utils/socket_fd.h --- rtorrent-0.8.6.orig/src/utils/socket_fd.h 2009-11-12 09:03:46.000000000 +0100 +++ rtorrent-0.8.6/src/utils/socket_fd.h 2010-07-23 14:45:07.000000000 +0200 @@ -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,9 @@ inline void check_valid() const; int m_fd; +#ifdef RAK_USE_INET6 + bool m_ipv6_socket; +#endif }; }