added session::listen_no_system_port flag to prevent libtorrent from ever binding the listen socket to port 0

This commit is contained in:
Arvid Norberg 2011-02-16 06:35:53 +00:00
parent a86ad2f7b9
commit cb6f38f056
6 changed files with 103 additions and 44 deletions

View File

@ -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

View File

@ -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<int, int> 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<int, int> 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

View File

@ -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<int, int> 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;

View File

@ -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<int, int> const& port_range
, const char* net_interface = 0
, int flags = 0) TORRENT_DEPRECATED;
#endif
void listen_on(
std::pair<int, int> const& port_range
, error_code& ec
, const char* net_interface = 0
, int flags = 0);
// returns the port we ended up listening on

View File

@ -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<void(void)>(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<void(void)>(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<int, int> 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<int, int> 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

View File

@ -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<listen_failed_alert>())
@ -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<int, int> 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)
{