From cb6f38f056d057dc623358ee353dc6b691207e3b Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Wed, 16 Feb 2011 06:35:53 +0000 Subject: [PATCH] added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 --- ChangeLog | 1 + docs/manual.rst | 36 +++++++++---- include/libtorrent/aux_/session_impl.hpp | 7 +-- include/libtorrent/session.hpp | 16 +++++- src/session.cpp | 21 +++++++- src/session_impl.cpp | 66 +++++++++++++----------- 6 files changed, 103 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index d2e584555..00a7c3cef 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0 * added option to not recheck on missing or incomplete resume data * extended stats logging with statistics=on builds * added new session functions to more efficiently query torrent status diff --git a/docs/manual.rst b/docs/manual.rst index 495ac4f51..653a1cf5b 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -207,9 +207,14 @@ The ``session`` class has the following synopsis:: bool is_listening() const; unsigned short listen_port() const; - enum { listen_reuse_address = 1 }; - bool listen_on( + enum { + listen_reuse_address = 1, + listen_no_system_port = 2 + }; + + void listen_on( std::pair const& port_range + , error_code& ec , char const* interface = 0 , int flags = 0); @@ -927,8 +932,15 @@ is_listening() listen_port() listen_on() bool is_listening() const; unsigned short listen_port() const; - bool listen_on( + + enum { + listen_reuse_address = 1, + listen_no_system_port = 2 + }; + + void listen_on( std::pair const& port_range + , error_code& ec , char const* interface = 0 , int flags = 0); @@ -946,12 +958,18 @@ will be opened with these new settings. The port range is the ports it will try to listen on, if the first port fails, it will continue trying the next port within the range and so on. The interface parameter can be left as 0, in that case the os will decide which interface to listen on, otherwise it should be the ip-address -of the interface you want the listener socket bound to. ``listen_on()`` returns true -if it managed to open the socket, and false if it failed. If it fails, it will also -generate an appropriate alert (listen_failed_alert_). If all ports in the specified -range fails to be opened for listening, libtorrent will try to use port 0 (which -tells the operating system to pick a port that's free). If that still fails you -may see a listen_failed_alert_ with port 0 even if you didn't ask to listen on it. +of the interface you want the listener socket bound to. ``listen_on()`` returns the +error code of the operation in ``ec``. If this indicates success, the session is +listening on a port within the specified range. If it fails, it will also +generate an appropriate alert (listen_failed_alert_). + +If all ports in the specified range fails to be opened for listening, libtorrent will +try to use port 0 (which tells the operating system to pick a port that's free). If +that still fails you may see a listen_failed_alert_ with port 0 even if you didn't +ask to listen on it. + +It is possible to prevent libtorrent from binding to port 0 by passing in the flag +``session::no_system_port`` in the ``flags`` argument. The interface parameter can also be a hostname that will resolve to the device you want to listen on. If you don't specify an interface, libtorrent may attempt to diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index 1a85d95bc..e2a8d83c1 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -174,7 +174,7 @@ namespace libtorrent #endif void main_thread(); - void open_listen_port(bool reuse_address); + void open_listen_port(int flags, error_code& ec); // if we are listening on an IPv6 interface // this will return one of the IPv6 addresses on this @@ -260,8 +260,9 @@ namespace libtorrent void set_port_filter(port_filter const& f); - bool listen_on( + void listen_on( std::pair const& port_range + , error_code& ec , const char* net_interface = 0 , int flags = 0); bool is_listening() const; @@ -667,7 +668,7 @@ namespace libtorrent #endif listen_socket_t setup_listener(tcp::endpoint ep, int retries - , bool v6_only, bool reuse_address); + , bool v6_only, int flags, error_code& ec); // the settings for the client session_settings m_settings; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 759cd9036..834024aef 100644 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -317,11 +317,25 @@ namespace libtorrent // this function will return false on failure. // If it fails, it will also generate alerts describing // the error. It will return true on success. - enum { listen_reuse_address = 1 }; + enum + { + listen_reuse_address = 0x01, + listen_no_system_port = 0x02 + }; +#ifndef TORRENT_NO_DEPRECATE + // deprecated in 0.16 + TORRENT_DEPRECATED_PREFIX bool listen_on( std::pair const& port_range , const char* net_interface = 0 + , int flags = 0) TORRENT_DEPRECATED; +#endif + + void listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface = 0 , int flags = 0); // returns the port we ended up listening on diff --git a/src/session.cpp b/src/session.cpp index c498b9a6a..e48cd9d5b 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -306,6 +306,12 @@ namespace libtorrent m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3)))); \ do { m_impl->cond.wait(l); } while(!done) +#define TORRENT_SYNC_CALL4(x, a1, a2, a3, a4) \ + bool done = false; \ + mutex::scoped_lock l(m_impl->mut); \ + m_impl->m_io_service.post(boost::bind(&fun_wrap, &done, &m_impl->cond, &m_impl->mut, boost::function(boost::bind(&session_impl:: x, m_impl.get(), a1, a2, a3, a4)))); \ + do { m_impl->cond.wait(l); } while(!done) + #define TORRENT_SYNC_CALL_RET(type, x) \ bool done = false; \ type r; \ @@ -648,12 +654,23 @@ namespace libtorrent TORRENT_ASYNC_CALL2(remove_torrent, h, options); } +#ifndef TORRENT_NO_DEPRECATE bool session::listen_on( std::pair const& port_range , const char* net_interface, int flags) { - TORRENT_SYNC_CALL_RET3(bool, listen_on, port_range, net_interface, flags); - return r; + error_code ec; + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); + return bool(ec); + } +#endif + + void session::listen_on( + std::pair const& port_range + , error_code& ec + , const char* net_interface, int flags) + { + TORRENT_SYNC_CALL4(listen_on, port_range, boost::ref(ec), net_interface, flags); } unsigned short session::listen_port() const diff --git a/src/session_impl.cpp b/src/session_impl.cpp index bfa1fe628..b63a0d88e 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -974,8 +974,8 @@ namespace aux { #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " open listen port\n"; #endif - // no reuse_address - open_listen_port(false); + // no reuse_address and allow system defined port + open_listen_port(0, ec); #if defined TORRENT_LOGGING || defined TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " done starting session\n"; #endif @@ -1706,32 +1706,36 @@ namespace aux { } session_impl::listen_socket_t session_impl::setup_listener(tcp::endpoint ep - , int retries, bool v6_only, bool reuse_address) + , int retries, bool v6_only, int flags, error_code& ec) { - error_code ec; listen_socket_t s; s.sock.reset(new socket_acceptor(m_io_service)); s.sock->open(ep.protocol(), ec); -#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING if (ec) { +#if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING (*m_logger) << "failed to open socket: " << print_endpoint(ep) << ": " << ec.message() << "\n" << "\n"; - } #endif - if (reuse_address) - s.sock->set_option(socket_acceptor::reuse_address(true), ec); + return listen_socket_t(); + } + if (flags & session::listen_reuse_address) + { + error_code err; // ignore errors here + s.sock->set_option(socket_acceptor::reuse_address(true), err); + } #if TORRENT_USE_IPV6 if (ep.protocol() == tcp::v6()) { - s.sock->set_option(v6only(v6_only), ec); + error_code err; // ignore errors here + s.sock->set_option(v6only(v6_only), err); #ifdef TORRENT_WINDOWS #ifndef PROTECTION_LEVEL_UNRESTRICTED #define PROTECTION_LEVEL_UNRESTRICTED 10 #endif // enable Teredo on windows - s.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), ec); + s.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); #endif } #endif @@ -1744,15 +1748,15 @@ namespace aux { , print_endpoint(ep).c_str(), ec.message().c_str()); (*m_logger) << time_now_string() << " " << msg << "\n"; #endif - ec = error_code(); + ec.clear(); TORRENT_ASSERT_VAL(!ec, ec); --retries; ep.port(ep.port() + 1); s.sock->bind(ep, ec); } - if (ec) + if (ec && !(flags & session::listen_no_system_port)) { - // instead of giving up, try + // instead of giving up, trying // let the OS pick a port ep.port(0); ec = error_code(); @@ -1772,7 +1776,7 @@ namespace aux { return listen_socket_t(); } s.external_port = s.sock->local_endpoint(ec).port(); - s.sock->listen(m_settings.listen_queue_size, ec); + if (!ec) s.sock->listen(m_settings.listen_queue_size, ec); if (ec) { if (m_alerts.should_post()) @@ -1796,7 +1800,7 @@ namespace aux { return s; } - void session_impl::open_listen_port(bool reuse_address) + void session_impl::open_listen_port(int flags, error_code& ec) { TORRENT_ASSERT(is_network_thread()); @@ -1814,14 +1818,13 @@ namespace aux { listen_socket_t s = setup_listener( tcp::endpoint(address_v4::any(), m_listen_interface.port()) - , m_listen_port_retries, false, reuse_address); + , m_listen_port_retries, false, flags, ec); if (s.sock) { // update the listen_interface member with the // actual port we ended up listening on, so that the other // sockets can be bound to the same one - error_code ec; m_listen_interface.port(s.sock->local_endpoint(ec).port()); m_listen_sockets.push_back(s); @@ -1834,7 +1837,7 @@ namespace aux { { s = setup_listener( tcp::endpoint(address_v6::any(), m_listen_interface.port()) - , m_listen_port_retries, true, reuse_address); + , m_listen_port_retries, true, flags, ec); if (s.sock) { @@ -1864,7 +1867,7 @@ namespace aux { // binds to the given interface listen_socket_t s = setup_listener( - m_listen_interface, m_listen_port_retries, false, reuse_address); + m_listen_interface, m_listen_port_retries, false, flags, ec); if (s.sock) { @@ -1876,10 +1879,8 @@ namespace aux { else m_ipv4_interface = m_listen_interface; } - } - error_code ec; m_udp_socket.bind(udp::endpoint(m_listen_interface.address(), m_listen_interface.port()), ec); if (ec) { @@ -4097,8 +4098,9 @@ namespace aux { TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); } - bool session_impl::listen_on( + void session_impl::listen_on( std::pair const& port_range + , error_code& ec , const char* net_interface, int flags) { INVARIANT_CHECK; @@ -4106,7 +4108,6 @@ namespace aux { tcp::endpoint new_interface; if (net_interface && std::strlen(net_interface) > 0) { - error_code ec; new_interface = tcp::endpoint(address::from_string(net_interface, ec), port_range.first); if (ec) { @@ -4114,29 +4115,33 @@ namespace aux { (*m_logger) << time_now_string() << "listen_on: " << net_interface << " failed: " << ec.message() << "\n"; #endif - return false; + return; } } else + { new_interface = tcp::endpoint(address_v4::any(), port_range.first); + } m_listen_port_retries = port_range.second - port_range.first; // if the interface is the same and the socket is open // don't do anything if (new_interface == m_listen_interface - && !m_listen_sockets.empty()) return true; + && !m_listen_sockets.empty()) + { + TORRENT_ASSERT(ec); + return; + } m_listen_interface = new_interface; - open_listen_port(flags & session::listen_reuse_address); + open_listen_port(flags, ec); #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING m_logger = create_log("main_session", listen_port(), false); (*m_logger) << time_now_string() << "\n"; #endif - - return !m_listen_sockets.empty(); } address session_impl::listen_address() const @@ -4373,7 +4378,10 @@ namespace aux { INVARIANT_CHECK; if (m_listen_interface.port() != 0) - open_listen_port(false); + { + error_code ec; + open_listen_port(0, ec); + } if (m_dht) {