diff --git a/ChangeLog b/ChangeLog index 252840655..ed6bd7a04 100644 --- a/ChangeLog +++ b/ChangeLog @@ -84,6 +84,7 @@ * almost completely changed the storage interface (for custom storage) * added support for hashing pieces in multiple threads + * improve reliability of binding listen sockets * support SNI in https web seeds and trackers * fix unhandled exception in DHT when receiving a DHT packet over IPv6 diff --git a/include/libtorrent/socket.hpp b/include/libtorrent/socket.hpp index 1f06db42c..03417a210 100644 --- a/include/libtorrent/socket.hpp +++ b/include/libtorrent/socket.hpp @@ -120,7 +120,21 @@ namespace libtorrent size_t size(Protocol const&) const { return sizeof(m_value); } int m_value; }; -#endif + + struct exclusive_address_use + { + exclusive_address_use(int enable): m_value(enable) {} + template + int level(Protocol const&) const { return SOL_SOCKET; } + template + int name(Protocol const&) const { return SO_EXCLUSIVEADDRUSE; } + template + int const* data(Protocol const&) const { return &m_value; } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + int m_value; + }; +#endif // TORRENT_WINDOWS #ifdef IPV6_TCLASS struct traffic_class diff --git a/src/session_impl.cpp b/src/session_impl.cpp index dbf080eeb..43a59bcd2 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1708,16 +1708,14 @@ namespace aux { return ret; } - // SO_REUSEADDR on windows is a bit special. It actually allows - // two active sockets to bind to the same port. That means we - // may end up binding to the same socket as some other random - // application. Don't do it! -#ifndef TORRENT_WINDOWS { - error_code err; // ignore errors here + // this is best-effort. ignore errors + error_code err; +#ifdef TORRENT_WINDOWS + ret.sock->set_option(exclusive_address_use(true), err); +#endif ret.sock->set_option(tcp::acceptor::reuse_address(true), err); } -#endif #if TORRENT_USE_IPV6 if (!ipv4) @@ -1726,6 +1724,7 @@ namespace aux { #ifdef IPV6_V6ONLY ret.sock->set_option(v6only(true), err); #endif + #ifdef TORRENT_WINDOWS #ifndef PROTECTION_LEVEL_UNRESTRICTED @@ -1733,7 +1732,7 @@ namespace aux { #endif // enable Teredo on windows ret.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); -#endif +#endif // TORRENT_WINDOWS } #endif // TORRENT_USE_IPV6 @@ -4893,6 +4892,9 @@ retry: tcp::endpoint bind_ep(address_v4(), 0); if (m_settings.get_int(settings_pack::outgoing_port) > 0) { +#ifdef TORRENT_WINDOWS + s.set_option(exclusive_address_use(true), ec); +#endif s.set_option(tcp::acceptor::reuse_address(true), ec); // ignore errors because the underlying socket may not // be opened yet. This happens when we're routing through diff --git a/src/udp_socket.cpp b/src/udp_socket.cpp index c3726b5ca..b4f57affb 100644 --- a/src/udp_socket.cpp +++ b/src/udp_socket.cpp @@ -789,6 +789,14 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec) { m_ipv4_sock.open(udp::v4(), ec); if (ec) return; + + // this is best-effort. ignore errors + error_code err; +#ifdef TORRENT_WINDOWS + m_ipv4_sock.set_option(exclusive_address_use(true), err); +#endif + m_ipv4_sock.set_option(boost::asio::socket_base::reuse_address(true), err); + m_ipv4_sock.bind(ep, ec); if (ec) return; udp::socket::non_blocking_io ioc(true); @@ -807,10 +815,17 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec) if (is_any(ep.address())) ep6.address(address_v6::any()); m_ipv6_sock.open(udp::v6(), ec); if (ec) return; -#ifdef IPV6_V6ONLY - m_ipv6_sock.set_option(v6only(true), ec); - ec.clear(); + + // this is best-effort. ignore errors + error_code err; +#ifdef TORRENT_WINDOWS + m_ipv4_sock.set_option(exclusive_address_use(true), err); #endif + m_ipv4_sock.set_option(boost::asio::socket_base::reuse_address(true), err); +#ifdef IPV6_V6ONLY + m_ipv6_sock.set_option(v6only(true), err); +#endif + m_ipv6_sock.bind(ep6, ec); if (ec != error_code(boost::system::errc::address_not_available , boost::system::generic_category()))