From 887e79eb22d8f52b0ae91d19cb75d1c84d33100e Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 7 Feb 2016 02:09:19 -0500 Subject: [PATCH 1/5] improve support for listening on multiple sockets and interfaces, with the listen_interfaces setting --- ChangeLog | 1 + include/libtorrent/alert_types.hpp | 6 +- include/libtorrent/aux_/session_impl.hpp | 48 +- include/libtorrent/aux_/session_interface.hpp | 1 + include/libtorrent/torrent.hpp | 3 +- include/libtorrent/upnp.hpp | 2 +- src/alert.cpp | 19 +- src/session_impl.cpp | 758 +++++++++--------- src/settings_pack.cpp | 10 +- src/torrent.cpp | 7 +- src/upnp.cpp | 11 +- 11 files changed, 414 insertions(+), 452 deletions(-) diff --git a/ChangeLog b/ChangeLog index 2dd8ac7e5..8b516ecb0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * improved support for listening on multiple sockets and interfaces * resume data no longer has timestamps of files 1.1.0 release diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index f233d185b..8298f88b3 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1268,7 +1268,7 @@ namespace libtorrent listen_failed_alert( aux::stack_allocator& alloc , std::string const& iface - , int port + , tcp::endpoint const& ep , int op , error_code const& ec , socket_type_t t); @@ -1278,7 +1278,7 @@ namespace libtorrent static const int static_category = alert::status_notification | alert::error_notification; virtual std::string message() const TORRENT_OVERRIDE; - // the interface libtorrent attempted to listen on that failed. + // the network device libtorrent attempted to listen on, or the IP address char const* listen_interface() const; // the error the system returned @@ -1286,7 +1286,7 @@ namespace libtorrent enum op_t { - parse_addr, open, bind, listen, get_peer_name, accept + parse_addr, open, bind, listen, get_socket_name, accept, enum_if }; // the specific low level operation that failed. See op_t. diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index e2e176275..d05db0118 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -124,12 +124,21 @@ namespace libtorrent struct listen_socket_t { - listen_socket_t(): external_port(0), ssl(false) {} + listen_socket_t() + : tcp_external_port(0) + , ssl(false) + { + tcp_port_mapping[0] = -1; + tcp_port_mapping[1] = -1; + } // this is typically empty but can be set // to the WAN IP address of NAT-PMP or UPnP router address external_address; + // this is a cached local endpoint for the listen socket + tcp::endpoint local_endpoint; + // this is typically set to the same as the local // listen port. In case a NAT port forward was // successfully opened, this will be set to the @@ -137,7 +146,10 @@ namespace libtorrent // on the NAT box itself. This is the port that has // to be published to peers, since this is the port // the client is reachable through. - int external_port; + int tcp_external_port; + + // 0 is natpmp 1 is upnp + int tcp_port_mapping[2]; // set to true if this is an SSL listen socket bool ssl; @@ -837,10 +849,6 @@ namespace libtorrent // which interface to listen on std::vector > m_listen_interfaces; - // keep this around until everything uses the list of interfaces - // instead. - tcp::endpoint m_listen_interface; - // the network interfaces outgoing connections are opened through. If // there is more then one, they are used in a round-robin fashion // each element is a device name or IP address (in string form) and @@ -850,12 +858,6 @@ namespace libtorrent // socket fails. std::vector m_outgoing_interfaces; - // if we're listening on an IPv6 interface - // this is one of the non local IPv6 interfaces - // on this machine - tcp::endpoint m_ipv6_interface; - tcp::endpoint m_ipv4_interface; - // since we might be listening on multiple interfaces // we might need more than one listen socket std::list m_listen_sockets; @@ -887,8 +889,7 @@ namespace libtorrent }; listen_socket_t setup_listener(std::string const& device - , boost::asio::ip::tcp const& protocol, int port, int flags - , error_code& ec); + , tcp::endpoint bind_ep, int flags, error_code& ec); #ifndef TORRENT_DISABLE_DHT entry m_dht_state; @@ -1026,6 +1027,7 @@ namespace libtorrent // see m_external_listen_port. This is the same // but for the udp port used by the DHT. + // TODO: 3 once udp sockets are part of m_listen_sockets, remove this int m_external_udp_port; udp_socket m_udp_socket; @@ -1046,18 +1048,18 @@ namespace libtorrent boost::shared_ptr m_upnp; boost::shared_ptr m_lsd; + // TODO: 3 once the udp socket is in listen_socket_t, these should + // move in there too + // 0 is natpmp 1 is upnp + int m_udp_mapping[2]; +#ifdef TORRENT_USE_OPENSSL + int m_ssl_udp_mapping[2]; +#endif + // mask is a bitmask of which protocols to remap on: // 1: NAT-PMP // 2: UPnP - void remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port); - - // 0 is natpmp 1 is upnp - int m_tcp_mapping[2]; - int m_udp_mapping[2]; -#ifdef TORRENT_USE_OPENSSL - int m_ssl_tcp_mapping[2]; - int m_ssl_udp_mapping[2]; -#endif + void remap_ports(boost::uint32_t mask, listen_socket_t& s); // the timer used to fire the tick deadline_timer m_timer; diff --git a/include/libtorrent/aux_/session_interface.hpp b/include/libtorrent/aux_/session_interface.hpp index 74b8b180c..480d51e64 100644 --- a/include/libtorrent/aux_/session_interface.hpp +++ b/include/libtorrent/aux_/session_interface.hpp @@ -246,6 +246,7 @@ namespace libtorrent { namespace aux virtual void prioritize_connections(boost::weak_ptr t) = 0; + // TODO: 3 these should go away! virtual tcp::endpoint get_ipv6_interface() const = 0; virtual tcp::endpoint get_ipv4_interface() const = 0; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 256a978d6..1eb88126a 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -723,8 +723,7 @@ namespace libtorrent void force_tracker_request(time_point, int tracker_idx); void scrape_tracker(int idx, bool user_triggered); void announce_with_tracker(boost::uint8_t e - = tracker_request::none - , address const& bind_interface = address_v4::any()); + = tracker_request::none); int seconds_since_last_scrape() const { return m_last_scrape == (std::numeric_limits::min)() diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index 42ce868ec..90e0e4e5b 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -134,7 +134,7 @@ class TORRENT_EXTRA_EXPORT upnp : public boost::enable_shared_from_this { public: upnp(io_service& ios - , address const& listen_interface, std::string const& user_agent + , std::string const& user_agent , portmap_callback_t const& cb, log_callback_t const& lcb , bool ignore_nonrouters); ~upnp(); diff --git a/src/alert.cpp b/src/alert.cpp index 4bd7bc9ed..3df130dda 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -786,26 +786,19 @@ namespace libtorrent { "HTTPS", "SSL/uTP" }; - - tcp::endpoint parse_interface(std::string const& iface, int port) - { - // ignore errors - error_code ec; - return tcp::endpoint(address::from_string(iface, ec), port); - } } listen_failed_alert::listen_failed_alert( aux::stack_allocator& alloc , std::string const& iface - , int prt + , tcp::endpoint const& ep , int op , error_code const& ec , socket_type_t t) : error(ec) , operation(op) , sock_type(t) - , endpoint(parse_interface(iface, prt)) + , endpoint(ep) , m_alloc(alloc) , m_interface_idx(alloc.copy_string(iface)) {} @@ -823,11 +816,13 @@ namespace libtorrent { "open", "bind", "listen", - "get_peer_name", - "accept" + "get_socket_name", + "accept", + "enum_if" }; char ret[300]; - snprintf(ret, sizeof(ret), "listening on %s failed: [%s] [%s] %s" + snprintf(ret, sizeof(ret), "listening on %s (device: %s) failed: [%s] [%s] %s" + , print_endpoint(endpoint).c_str() , listen_interface() , op_str[operation] , sock_type_str[sock_type] diff --git a/src/session_impl.cpp b/src/session_impl.cpp index ec0efe551..da4f388c0 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -452,7 +452,6 @@ namespace aux { #endif error_code ec; - m_listen_interface = tcp::endpoint(address_v4::any(), 0); TORRENT_ASSERT_VAL(!ec, ec); } @@ -487,13 +486,9 @@ namespace aux { m_next_dht_torrent = m_torrents.begin(); #endif m_next_lsd_torrent = m_torrents.begin(); - m_tcp_mapping[0] = -1; - m_tcp_mapping[1] = -1; m_udp_mapping[0] = -1; m_udp_mapping[1] = -1; #ifdef TORRENT_USE_OPENSSL - m_ssl_tcp_mapping[0] = -1; - m_ssl_tcp_mapping[1] = -1; m_ssl_udp_mapping[0] = -1; m_ssl_udp_mapping[1] = -1; #endif @@ -1078,7 +1073,7 @@ namespace aux { m_download_rate.close(); m_upload_rate.close(); - // #error closing the udp socket here means that + // TODO: 3 closing the udp socket here means that // the uTP connections cannot be closed gracefully m_udp_socket.close(); m_external_udp_port = 0; @@ -1271,7 +1266,6 @@ namespace aux { } #endif - if (is_any(req.bind_ip)) req.bind_ip = m_listen_interface.address(); m_tracker_manager.queue_request(get_io_service(), req, c); } @@ -1625,151 +1619,206 @@ namespace aux { } #endif + // TODO: 2 remove this function tcp::endpoint session_impl::get_ipv6_interface() const { - return m_ipv6_interface; + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->local_endpoint.address().is_v6()) return i->local_endpoint; + } + return tcp::endpoint(); } + // TODO: 2 remove this function tcp::endpoint session_impl::get_ipv4_interface() const { - return m_ipv4_interface; + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + if (i->local_endpoint.address().is_v4()) return i->local_endpoint; + } + return tcp::endpoint(); } enum { listen_no_system_port = 0x02 }; listen_socket_t session_impl::setup_listener(std::string const& device - , boost::asio::ip::tcp const& protocol, int port, int flags, error_code& ec) + , tcp::endpoint bind_ep, int flags, error_code& ec) { int retries = m_settings.get_int(settings_pack::max_retry_port_bind); +#ifndef TORRENT_DISABLE_LOGGING + session_log("attempting to to open listen socket to: %s on device: %s flags: %x" + , print_endpoint(bind_ep).c_str(), device.c_str(), flags); +#endif + listen_socket_t ret; ret.ssl = flags & open_ssl_socket; int last_op = 0; - listen_failed_alert::socket_type_t sock_type = (flags & open_ssl_socket) - ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp; + listen_failed_alert::socket_type_t const sock_type + = (flags & open_ssl_socket) + ? listen_failed_alert::tcp_ssl + : listen_failed_alert::tcp; ret.sock.reset(new tcp::acceptor(m_io_service)); - ret.sock->open(protocol, ec); + ret.sock->open(bind_ep.address().is_v4() ? tcp::v4() : tcp::v6(), ec); last_op = listen_failed_alert::open; if (ec) { - if (m_alerts.should_post()) - m_alerts.emplace_alert(device, port, last_op - , ec, sock_type); - #ifndef TORRENT_DISABLE_LOGGING - session_log("failed to open socket: %s: %s" - , device.c_str(), ec.message().c_str()); + session_log("failed to open socket: %s" + , ec.message().c_str()); #endif + + if (m_alerts.should_post()) + m_alerts.emplace_alert(device, bind_ep, last_op + , ec, sock_type); return ret; } +#ifdef TORRENT_WINDOWS + { + // this is best-effort. ignore errors + error_code err; + ret.sock->set_option(exclusive_address_use(true), err); +#ifndef TORRENT_DISABLE_LOGGING + if (err) + { + session_log("failed enable exclusive address use on listen socket: %s" + , err.message().c_str()); + } +#endif // TORRENT_DISABLE_LOGGING + } +#endif // TORRENT_WINDOWS + { // 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); +#ifndef TORRENT_DISABLE_LOGGING + if (err) + { + session_log("failed enable reuse-address on listen socket: %s" + , err.message().c_str()); + } +#endif // TORRENT_DISABLE_LOGGING } #if TORRENT_USE_IPV6 - if (protocol == boost::asio::ip::tcp::v6()) + if (bind_ep.address().is_v6()) { error_code err; // ignore errors here ret.sock->set_option(boost::asio::ip::v6_only(true), err); +#ifndef TORRENT_DISABLE_LOGGING + if (err) + { + session_log("failed enable v6 only on listen socket: %s" + , err.message().c_str()); + } +#endif // LOGGING + #ifdef TORRENT_WINDOWS // enable Teredo on windows ret.sock->set_option(v6_protection_level(PROTECTION_LEVEL_UNRESTRICTED), err); +#ifndef TORRENT_DISABLE_LOGGING + if (err) + { + session_log("failed enable IPv6 unrestricted protection level on " + "listen socket: %s", err.message().c_str()); + } +#endif // TORRENT_DISABLE_LOGGING #endif // TORRENT_WINDOWS } #endif // TORRENT_USE_IPV6 - address bind_ip = bind_to_device(m_io_service, *ret.sock, protocol - , device.c_str(), port, ec); + ret.sock->bind(bind_ep, ec); + last_op = listen_failed_alert::bind; while (ec == error_code(error::address_in_use) && retries > 0) { TORRENT_ASSERT_VAL(ec, ec); #ifndef TORRENT_DISABLE_LOGGING error_code ignore; - session_log("failed to bind to interface [%s %d] \"%s\" : %s (%d) : %s " - "(retries: %d)" - , device.c_str(), port, bind_ip.to_string(ignore).c_str() - , ec.category().name(), ec.value(), ec.message().c_str(), retries); + session_log("failed to bind listen socket to: %s on device: %s :" + " [%s] (%d) %s (retries: %d)" + , print_endpoint(bind_ep).c_str() + , device.c_str() + , ec.category().name(), ec.value(), ec.message().c_str() + , retries); #endif ec.clear(); - TORRENT_ASSERT_VAL(!ec, ec); --retries; - port += 1; - bind_ip = bind_to_device(m_io_service, *ret.sock, protocol - , device.c_str(), port, ec); - last_op = listen_failed_alert::bind; + bind_ep.port(bind_ep.port() + 1); + ret.sock->bind(bind_ep, ec); } + if (ec == error_code(error::address_in_use) && !(flags & listen_no_system_port)) { // instead of giving up, try let the OS pick a port - port = 0; + bind_ep.port(0); ec.clear(); - bind_ip = bind_to_device(m_io_service, *ret.sock, protocol - , device.c_str(), port, ec); + ret.sock->bind(bind_ep, ec); last_op = listen_failed_alert::bind; } + if (ec) { - TORRENT_ASSERT_VAL(ec.value() != 0, ec); - // not even that worked, give up - if (m_alerts.should_post()) - m_alerts.emplace_alert(device, port, last_op, ec, sock_type); + #ifndef TORRENT_DISABLE_LOGGING - error_code err; - session_log("cannot to bind to interface [%s %d] \"%s : %s\": %s" - , device.c_str(), port, bind_ip.to_string(err).c_str() - , ec.category().name(), ec.message().c_str()); + error_code ignore; + session_log("failed to bind listen socket to: %s on device: %s :" + " [%s] (%d) %s (giving up)" + , print_endpoint(bind_ep).c_str() + , device.c_str() + , ec.category().name(), ec.value(), ec.message().c_str()); #endif + if (m_alerts.should_post()) + { + m_alerts.emplace_alert(device, bind_ep + , last_op, ec, sock_type); + } return ret; } - ret.external_port = ret.sock->local_endpoint(ec).port(); - TORRENT_ASSERT(ret.external_port == port || port == 0); - last_op = listen_failed_alert::get_peer_name; - if (!ec) - { - ret.sock->listen(m_settings.get_int(settings_pack::listen_queue_size), ec); - last_op = listen_failed_alert::listen; - } + ret.local_endpoint = ret.sock->local_endpoint(ec); + last_op = listen_failed_alert::get_socket_name; if (ec) { +#ifndef TORRENT_DISABLE_LOGGING + session_log("get_sockname failed on listen socket: %s" + , ec.message().c_str()); +#endif if (m_alerts.should_post()) - m_alerts.emplace_alert(device, port, last_op, ec, sock_type); + { + m_alerts.emplace_alert(device, bind_ep + , last_op, ec, sock_type); + } + return ret; + } + ret.tcp_external_port = ret.local_endpoint.port(); + + ret.sock->listen(m_settings.get_int(settings_pack::listen_queue_size), ec); + last_op = listen_failed_alert::listen; + + if (ec) + { #ifndef TORRENT_DISABLE_LOGGING session_log("cannot listen on interface \"%s\": %s" , device.c_str(), ec.message().c_str()); #endif + if (m_alerts.should_post()) + { + m_alerts.emplace_alert(device, bind_ep + , last_op, ec, sock_type); + } return ret; } - // if we asked the system to listen on port 0, which - // socket did it end up choosing? - if (port == 0) - { - port = ret.sock->local_endpoint(ec).port(); - last_op = listen_failed_alert::get_peer_name; - if (ec) - { - if (m_alerts.should_post()) - m_alerts.emplace_alert(device, port, last_op, ec, sock_type); #ifndef TORRENT_DISABLE_LOGGING - session_log("failed to get peer name \"%s\": %s" - , device.c_str(), ec.message().c_str()); -#endif - return ret; - } - } - -#ifndef TORRENT_DISABLE_LOGGING - session_log(" listening on: %s external port: %d" - , print_endpoint(tcp::endpoint(bind_ip, port)).c_str(), ret.external_port); + session_log(" listening on: %s TCP port: %d" + , print_endpoint(bind_ep).c_str() + , ret.tcp_external_port); #endif return ret; } @@ -1787,67 +1836,39 @@ namespace aux { ? 0 : listen_no_system_port; error_code ec; - int listen_port_retries = m_settings.get_int(settings_pack::max_retry_port_bind); - -retry: - // close the open listen sockets // close the listen sockets +#ifndef TORRENT_DISABLE_LOGGING + session_log("closing all listen sockets"); +#endif for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) + { i->sock->close(ec); + } + m_listen_sockets.clear(); m_stats_counters.set_value(counters::has_incoming_connections, 0); ec.clear(); if (m_abort) return; - m_ipv6_interface = tcp::endpoint(); - m_ipv4_interface = tcp::endpoint(); - - // TODO: instead of having a special case for this, just make the - // default listen interfaces be "0.0.0.0:6881,[::]:6881" and use - // the generic path. That would even allow for not listening at all. - if (m_listen_interfaces.empty()) + for (int i = 0; i < m_listen_interfaces.size(); ++i) { - // this means we should open two listen sockets - // one for IPv4 and one for IPv6 - listen_socket_t s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4() - , m_listen_interface.port() - , flags, ec); + std::string const& device = m_listen_interfaces[i].first; + int const port = m_listen_interfaces[i].second; - if (!ec && s.sock) + // now we have a device to bind to. This device may actually just be an + // IP address or a device name. In case it's a device name, we want to + // (potentially) end up binding a socket for each IP address associated + // with that device. + + // First, check to see if it's an IP address + error_code err; + address adr = address::from_string(device.c_str(), err); + if (!err) { - // 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 - m_listen_interface.port(s.external_port); - - TORRENT_ASSERT(!m_abort); - m_listen_sockets.push_back(s); - } - -#ifdef TORRENT_USE_OPENSSL - if (m_settings.get_int(settings_pack::ssl_listen)) - { - s = setup_listener("0.0.0.0", boost::asio::ip::tcp::v4() - , m_settings.get_int(settings_pack::ssl_listen) - , flags | open_ssl_socket, ec); - - if (!ec && s.sock) - { - TORRENT_ASSERT(!m_abort); - m_listen_sockets.push_back(s); - } - } -#endif - -#if TORRENT_USE_IPV6 - // only try to open the IPv6 port if IPv6 is installed - if (supports_ipv6()) - { - s = setup_listener("::", boost::asio::ip::tcp::v6() - , m_listen_interface.port() + listen_socket_t s = setup_listener(device, tcp::endpoint(adr, port) , flags, ec); if (!ec && s.sock) @@ -1857,74 +1878,52 @@ retry: } #ifdef TORRENT_USE_OPENSSL + // TODO: 3 it would probably be better to specify if we want an SSL + // port open as another entry in the listen_interfaces (like in + // mongoose) if (m_settings.get_int(settings_pack::ssl_listen)) { - s.ssl = true; - s = setup_listener("::", boost::asio::ip::tcp::v6() - , m_settings.get_int(settings_pack::ssl_listen) + listen_socket_t s = setup_listener(device + , tcp::endpoint(adr, m_settings.get_int(settings_pack::ssl_listen)) , flags | open_ssl_socket, ec); if (!ec && s.sock) { - TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } } #endif // TORRENT_USE_OPENSSL } -#endif // TORRENT_USE_IPV6 - - // set our main IPv4 and IPv6 interfaces - // used to send to the tracker - std::vector ifs = enum_net_interfaces(m_io_service, ec); - for (std::vector::const_iterator i = ifs.begin() - , end(ifs.end()); i != end; ++i) + else { - address const& addr = i->interface_address; - if (addr.is_v6() && !is_local(addr) && !is_loopback(addr)) - m_ipv6_interface = tcp::endpoint(addr, m_listen_interface.port()); - else if (addr.is_v4() && !is_local(addr) && !is_loopback(addr)) - m_ipv4_interface = tcp::endpoint(addr, m_listen_interface.port()); - } - } - else - { - // TODO: 2 the udp socket(s) should be using the same generic - // mechanism and not be restricted to a single one - // we should open a one listen socket for each entry in the - // listen_interfaces list - for (int i = 0; i < m_listen_interfaces.size(); ++i) - { - std::string const& device = m_listen_interfaces[i].first; - int port = m_listen_interfaces[i].second; + // this is the case where device names a network device. We need to + // enumerate all IPs associated with this device - int num_device_fails = 0; - -#if TORRENT_USE_IPV6 - const int first_family = 0; -#else - const int first_family = 1; -#endif - boost::asio::ip::tcp protocol[] - = { boost::asio::ip::tcp::v6(), boost::asio::ip::tcp::v4() }; - - for (int address_family = first_family; address_family < 2; ++address_family) + std::vector ifs = enum_net_interfaces(m_io_service, ec); + if (ec) { - error_code err; - address test_family = address::from_string(device.c_str(), err); - if (!err - && test_family.is_v4() != address_family - && !is_any(test_family)) - continue; - - listen_socket_t s = setup_listener(device, protocol[address_family] - , port, flags, ec); - - if (ec == error_code(boost::system::errc::no_such_device, generic_category())) +#ifndef TORRENT_DISABLE_LOGGING + session_log("failed to enumerate IPs on device: \"%s\": %s" + , device.c_str(), ec.message().c_str()); +#endif + if (m_alerts.should_post()) { - ++num_device_fails; - continue; + m_alerts.emplace_alert(device + , tcp::endpoint(), listen_failed_alert::enum_if, ec + , listen_failed_alert::tcp); } + continue; + } + + for (int k = 0; k < int(ifs.size()); ++k) + { + // we're looking for a specific interface, and its address + // (which must be of the same family as the address we're + // connecting to) + if (device != ifs[k].name) continue; + + listen_socket_t s = setup_listener(device + , tcp::endpoint(ifs[k].interface_address, port), flags, ec); if (!ec && s.sock) { @@ -1935,114 +1934,111 @@ retry: TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); - - tcp::endpoint bind_ep = s.sock->local_endpoint(ec); -#if TORRENT_USE_IPV6 - if (bind_ep.address().is_v6()) - m_ipv6_interface = bind_ep; - else -#endif - m_ipv4_interface = bind_ep; } #ifdef TORRENT_USE_OPENSSL + // TODO: 3 it would probably be better to specify if we want an SSL + // port open as another entry in the listen_interfaces (like in + // mongoose) if (m_settings.get_int(settings_pack::ssl_listen)) { - listen_socket_t ssl_s = setup_listener(device - , protocol[address_family] - , m_settings.get_int(settings_pack::ssl_listen) + int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); + listen_socket_t s = setup_listener(device + , tcp::endpoint(ifs[k].interface_address, port) , flags | open_ssl_socket, ec); - if (!ec && ssl_s.sock) + if (!ec && s.sock) { - TORRENT_ASSERT(!m_abort); - m_listen_sockets.push_back(ssl_s); + m_listen_sockets.push_back(s); } } -#endif +#endif // TORRENT_USE_OPENSSL } } } - if (m_listen_sockets.empty() && ec) + if (m_listen_sockets.empty()) { #ifndef TORRENT_DISABLE_LOGGING - session_log("cannot bind TCP listen socket to interface \"%s\": %s" - , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); + session_log("giving up on binding listen sockets"); #endif - if (listen_port_retries > 0) - { - m_listen_interface.port(m_listen_interface.port() + 1); - --listen_port_retries; - goto retry; - } - if (m_alerts.should_post()) - m_alerts.emplace_alert( - m_listen_interface.address().to_string() - , m_listen_interface.port() - , listen_failed_alert::bind - , ec, listen_failed_alert::tcp); return; } + // TODO: 3 the udp socket(s) should be using the same generic + // mechanism and not be restricted to a single one + // we should open a one listen socket for each entry in the + // listen_interfaces list + // for now, remember the first successful port, and bind the UDP socket to + // that as well + udp::endpoint udp_bind_ep(m_listen_sockets.begin()->local_endpoint.address() + , m_listen_sockets.begin()->local_endpoint.port()); + int retries = m_settings.get_int(settings_pack::max_retry_port_bind); + #ifdef TORRENT_USE_OPENSSL - int ssl_port = m_settings.get_int(settings_pack::ssl_listen); - udp::endpoint ssl_bind_if(m_listen_interface.address(), ssl_port); + // TODO: 3 remove ssl_listen setting. Instead, specify the port in the + // listen_interfaces to have an "s" suffix. Just like mongoose + int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); // if ssl port is 0, we don't want to listen on an SSL port if (ssl_port != 0) { - // TODO: 2 use bind_to_device in udp_socket - m_ssl_udp_socket.bind(ssl_bind_if, ec); + udp::endpoint ssl_bind_ep(udp_bind_ep.address(), ssl_port); + do + { + ec.clear(); + m_ssl_udp_socket.bind(ssl_bind_ep, ec); + if (ec) + { +#ifndef TORRENT_DISABLE_LOGGING + session_log("SSL: cannot bind to UDP interface \"%s\": %s" + , print_endpoint(ssl_bind_ep).c_str(), ec.message().c_str()); +#endif + if (m_alerts.should_post()) + { + error_code err; + m_alerts.emplace_alert(ssl_bind_ep.address().to_string(err) + , tcp::endpoint(ssl_bind_ep.address(), ssl_bind_ep.port()) + , listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl); + } + --retries; + ssl_bind_ep.port(ssl_bind_ep.port() + 1); + } + // TODO: 3 port map SSL udp socket here + } while (ec == error_code(error::address_in_use) && retries > 0); + } + + retries = m_settings.get_int(settings_pack::max_retry_port_bind); +#endif // TORRENT_USE_OPENSSL + + do + { + ec.clear(); + m_udp_socket.bind(udp_bind_ep, ec); if (ec) { #ifndef TORRENT_DISABLE_LOGGING - session_log("SSL: cannot bind to UDP interface \"%s\": %s" - , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); + session_log("cannot bind to UDP interface \"%s\": %s" + , print_endpoint(udp_bind_ep).c_str(), ec.message().c_str()); #endif if (m_alerts.should_post()) { error_code err; - m_alerts.emplace_alert(ssl_bind_if.address().to_string() - , ssl_port, listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl); + m_alerts.emplace_alert(udp_bind_ep.address().to_string(err) + , tcp::endpoint(udp_bind_ep.address(), udp_bind_ep.port()) + , listen_failed_alert::bind + , ec, listen_failed_alert::udp); } - ec.clear(); + --retries; + udp_bind_ep.port(udp_bind_ep.port() + 1); } - // TODO: 3 port map SSL udp socket here - } -#endif // TORRENT_USE_OPENSSL - - // TODO: 2 use bind_to_device in udp_socket - m_udp_socket.bind(udp::endpoint(m_listen_interface.address() - , m_listen_interface.port()), ec); - if (ec) - { -#ifndef TORRENT_DISABLE_LOGGING - session_log("cannot bind to UDP interface \"%s\": %s" - , print_endpoint(m_listen_interface).c_str(), ec.message().c_str()); -#endif - if (listen_port_retries > 0) + else { - m_listen_interface.port(m_listen_interface.port() + 1); - --listen_port_retries; - goto retry; + m_external_udp_port = m_udp_socket.local_port(); + maybe_update_udp_mapping(0, udp_bind_ep.port(), udp_bind_ep.port()); + maybe_update_udp_mapping(1, udp_bind_ep.port(), udp_bind_ep.port()); } - if (m_alerts.should_post()) - { - error_code err; - m_alerts.emplace_alert(m_listen_interface.address().to_string() - , m_listen_interface.port() - , listen_failed_alert::bind - , ec, listen_failed_alert::udp); - } - return; - } - else - { - m_external_udp_port = m_udp_socket.local_port(); - maybe_update_udp_mapping(0, m_listen_interface.port(), m_listen_interface.port()); - maybe_update_udp_mapping(1, m_listen_interface.port(), m_listen_interface.port()); - } + } while (ec == error_code(error::address_in_use) && retries > 0); // we made it! now post all the listen_succeeded_alerts @@ -2067,7 +2063,7 @@ retry: { if (m_alerts.should_post()) m_alerts.emplace_alert( - tcp::endpoint(ssl_bind_if.address(), ssl_bind_if.port()) + tcp::endpoint(ssl_bind_ep.address(), ssl_bind_ep.port()) , listen_succeeded_alert::utp_ssl); } #endif @@ -2075,7 +2071,8 @@ retry: if (m_udp_socket.is_open()) { if (m_alerts.should_post()) - m_alerts.emplace_alert(m_listen_interface + m_alerts.emplace_alert( + tcp::endpoint(udp_bind_ep.address(), udp_bind_ep.port()) , listen_succeeded_alert::udp); } @@ -2096,44 +2093,31 @@ retry: // initiate accepting on the listen sockets for (std::list::iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) + { async_accept(i->sock, i->ssl); + remap_ports(3, *i); + } open_new_incoming_socks_connection(); #if TORRENT_USE_I2P open_new_incoming_i2p_connection(); #endif - - if (!m_listen_sockets.empty()) - { - tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec); - if (!ec) remap_tcp_ports(3, local.port(), ssl_listen_port()); - } } - void session_impl::remap_tcp_ports(boost::uint32_t mask, int tcp_port, int ssl_port) + // TODO: 3 add an enum for the mask parameter here + void session_impl::remap_ports(boost::uint32_t mask, listen_socket_t& s) { -#ifndef TORRENT_USE_OPENSSL - TORRENT_UNUSED(ssl_port); -#endif if ((mask & 1) && m_natpmp) { - if (m_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_tcp_mapping[0]); - m_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp, tcp_port, tcp_port); -#ifdef TORRENT_USE_OPENSSL - if (m_ssl_tcp_mapping[0] != -1) m_natpmp->delete_mapping(m_ssl_tcp_mapping[0]); - if (ssl_port > 0) m_ssl_tcp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp - , ssl_port, ssl_port); -#endif + if (s.tcp_port_mapping[0] != -1) m_natpmp->delete_mapping(s.tcp_port_mapping[0]); + s.tcp_port_mapping[0] = m_natpmp->add_mapping(natpmp::tcp + , s.local_endpoint.port(), s.local_endpoint.port()); } if ((mask & 2) && m_upnp) { - if (m_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_tcp_mapping[1]); - m_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp, tcp_port, tcp_port); -#ifdef TORRENT_USE_OPENSSL - if (m_ssl_tcp_mapping[1] != -1) m_upnp->delete_mapping(m_ssl_tcp_mapping[1]); - if (ssl_port > 0) m_ssl_tcp_mapping[1] = m_upnp->add_mapping(upnp::tcp - , ssl_port, ssl_port); -#endif + if (s.tcp_port_mapping[1] != -1) m_upnp->delete_mapping(s.tcp_port_mapping[1]); + s.tcp_port_mapping[1] = m_upnp->add_mapping(upnp::tcp + , s.local_endpoint.port(), s.local_endpoint.port()); } } @@ -2159,8 +2143,8 @@ retry: #endif socks5_stream& s = *m_socks_listen_socket->get(); s.set_command(2); // 2 means BIND (as opposed to CONNECT) - m_socks_listen_port = m_listen_interface.port(); - if (m_socks_listen_port == 0) m_socks_listen_port = 2000 + random() % 60000; + m_socks_listen_port = 2000 + random() % 60000; + s.async_connect(tcp::endpoint(address_v4::any(), m_socks_listen_port) , boost::bind(&session_impl::on_socks_accept, this, m_socks_listen_socket, _1)); } @@ -2232,7 +2216,8 @@ retry: i2p_stream& s = *m_i2p_listen_socket->get(); s.set_command(i2p_stream::cmd_accept); s.set_session_id(m_i2p_conn.session_id()); - s.async_connect(tcp::endpoint(address_v4::any(), m_listen_interface.port()) + + s.async_connect(tcp::endpoint() , boost::bind(&session_impl::on_i2p_accept, this, m_i2p_listen_socket, _1)); } @@ -2247,13 +2232,14 @@ retry: if (e) { if (m_alerts.should_post()) + { m_alerts.emplace_alert("i2p" - , m_listen_interface.port() + , tcp::endpoint() , listen_failed_alert::accept , e, listen_failed_alert::i2p); + } #ifndef TORRENT_DISABLE_LOGGING - session_log("cannot bind to port %d: %s" - , m_listen_interface.port(), e.message().c_str()); + session_log("i2p SAM connection failure: %s", e.message().c_str()); #endif return; } @@ -2388,8 +2374,8 @@ retry: if (m_alerts.should_post()) { error_code err; - m_alerts.emplace_alert(ep.address().to_string() - , ep.port(), listen_failed_alert::accept, e + m_alerts.emplace_alert(ep.address().to_string(err) + , ep, listen_failed_alert::accept, e , ssl ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp); } return; @@ -2726,7 +2712,7 @@ retry: { if (m_alerts.should_post()) m_alerts.emplace_alert("socks5" - , -1, listen_failed_alert::accept, e + , tcp::endpoint(), listen_failed_alert::accept, e , listen_failed_alert::socks5); return; } @@ -3356,7 +3342,7 @@ retry: 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; - v |= v >> 1; // first round down to one less than a power of 2 + v |= v >> 1; // first round down to one less than a power of 2 v |= v >> 2; v |= v >> 4; v |= v >> 8; @@ -5106,70 +5092,7 @@ retry: session_log("update listen interfaces: %s", net_interfaces.c_str()); #endif - // if the interface is the same and the socket is open - // don't do anything - if (new_listen_interfaces == m_listen_interfaces - && !m_listen_sockets.empty()) - return; - m_listen_interfaces = new_listen_interfaces; - - // for backwards compatibility. Some components still only supports - // a single listen interface - m_listen_interface.address(address_v4::any()); - m_listen_interface.port(0); - if (m_listen_interfaces.size() > 0) - { - error_code ec; - m_listen_interface.port(m_listen_interfaces[0].second); - char const* device_name = m_listen_interfaces[0].first.c_str(); - - // if the first character is [, skip it since it may be an - // IPv6 address - m_listen_interface.address(address::from_string( - device_name[0] == '[' ? device_name + 1 : device_name, ec)); - if (ec) - { -#ifndef TORRENT_DISABLE_LOGGING - session_log("failed to treat %s as an IP address [ %s ]" - , device_name, ec.message().c_str()); -#endif - // it may have been a device name. - std::vector ifs = enum_net_interfaces(m_io_service, ec); - -#ifndef TORRENT_DISABLE_LOGGING - if (ec) - session_log("failed to enumerate interfaces [ %s ]" - , ec.message().c_str()); -#endif - - bool found = false; - for (int i = 0; i < int(ifs.size()); ++i) - { - // we're looking for a specific interface, and its address - // (which must be of the same family as the address we're - // connecting to) - if (strcmp(ifs[i].name, device_name) != 0) continue; - m_listen_interface.address(ifs[i].interface_address); -#ifndef TORRENT_DISABLE_LOGGING - error_code err; - session_log("binding to %s" - , m_listen_interface.address().to_string(err).c_str()); -#endif - found = true; - break; - } - - if (!found) - { -#ifndef TORRENT_DISABLE_LOGGING - session_log("failed to find device %s", device_name); -#endif - // effectively disable whatever socket decides to bind to this - m_listen_interface.address(address_v4::loopback()); - } - } - } } void session_impl::update_privileged_ports() @@ -5299,7 +5222,7 @@ retry: // potentially identify us if it is leaked elsewere if (m_settings.get_bool(settings_pack::force_proxy)) return 0; if (m_listen_sockets.empty()) return 0; - return m_listen_sockets.front().external_port; + return m_listen_sockets.front().tcp_external_port; } boost::uint16_t session_impl::ssl_listen_port() const @@ -5318,7 +5241,7 @@ retry: for (std::list::const_iterator i = m_listen_sockets.begin() , end(m_listen_sockets.end()); i != end; ++i) { - if (i->ssl) return i->external_port; + if (i->ssl) return i->tcp_external_port; } if (m_ssl_udp_socket.is_open()) @@ -5372,6 +5295,14 @@ retry: #endif } + namespace { + bool find_tcp_port_mapping(int transport, int mapping, listen_socket_t const& ls) + { + return ls.tcp_port_mapping[transport] == mapping; + } + } + + // transport is 0 for NAT-PMP and 1 for UPnP void session_impl::on_port_mapping(int mapping, address const& ip, int port , error_code const& ec, int map_transport) { @@ -5379,6 +5310,12 @@ retry: TORRENT_ASSERT(map_transport >= 0 && map_transport <= 1); + if (ec && m_alerts.should_post()) + { + m_alerts.emplace_alert(mapping + , map_transport, ec); + } + if (mapping == m_udp_mapping[map_transport] && port != 0) { m_external_udp_port = port; @@ -5388,36 +5325,30 @@ retry: return; } - if (mapping == m_tcp_mapping[map_transport] && port != 0) + // look through our listen sockets to see if this mapping is for one of + // them (it could also be a user mapping) + + std::list::iterator ls + = std::find_if(m_listen_sockets.begin(), m_listen_sockets.end() + , boost::bind(find_tcp_port_mapping, map_transport, mapping, _1)); + + if (ls != m_listen_sockets.end()) { if (ip != address()) { // TODO: 1 report the proper address of the router as the source IP of - // this understanding of our external address, instead of the empty address + // this vote of our external address, instead of the empty address set_external_address(ip, source_router, address()); } - if (!m_listen_sockets.empty()) { - m_listen_sockets.front().external_address = ip; - m_listen_sockets.front().external_port = port; - } - if (m_alerts.should_post()) - m_alerts.emplace_alert(mapping, port - , map_transport); - return; + ls->external_address = ip; + ls->tcp_external_port = port; } - if (ec) + if (!ec && m_alerts.should_post()) { - if (m_alerts.should_post()) - m_alerts.emplace_alert(mapping - , map_transport, ec); - } - else - { - if (m_alerts.should_post()) - m_alerts.emplace_alert(mapping, port - , map_transport); + m_alerts.emplace_alert(mapping, port + , map_transport); } } @@ -6550,22 +6481,34 @@ retry: , this, _1, 0)); m_natpmp->start(); - int ssl_port = ssl_listen_port(); - - if (m_listen_interface.port() > 0) + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) { - remap_tcp_ports(1, m_listen_interface.port(), ssl_port); + remap_ports(1, *i); } + + // TODO: 3 once UDP sockets are part of m_listen_sockets, this is not + // necesarry! if (m_udp_socket.is_open()) { - m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp - , m_listen_interface.port(), m_listen_interface.port()); + error_code ec; + tcp::endpoint ep = m_udp_socket.local_endpoint(ec); + if (!ec) { + if (m_udp_mapping[0] != -1) m_natpmp->delete_mapping(m_udp_mapping[0]); + m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp + , ep.port(), ep.port()); + } } #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open() && ssl_port > 0) { - m_ssl_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp - , ssl_port, ssl_port); + error_code ec; + tcp::endpoint ep = m_ssl_udp_socket.local_endpoint(ec); + if (!ec) { + if (m_ssl_udp_mapping[0] != -1) m_natpmp->delete_mapping(m_ssl_udp_mapping[0]); + m_ssl_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp + , ep.port(), ep.port()); + } } #endif return m_natpmp.get(); @@ -6579,7 +6522,6 @@ retry: // the upnp constructor may fail and call the callbacks m_upnp = boost::make_shared(boost::ref(m_io_service) - , m_listen_interface.address() , m_settings.get_str(settings_pack::user_agent) , boost::bind(&session_impl::on_port_mapping , this, _1, _2, _3, _4, 1) @@ -6588,23 +6530,36 @@ retry: , m_settings.get_bool(settings_pack::upnp_ignore_nonrouters)); m_upnp->start(); - int ssl_port = ssl_listen_port(); - m_upnp->discover_device(); - if (m_listen_interface.port() > 0 || ssl_port > 0) + + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) { - remap_tcp_ports(2, m_listen_interface.port(), ssl_port); + remap_ports(1, *i); } + + // TODO: 3 once the UDP sockets are part of m_listen_sockets this won't be + // necessary! if (m_udp_socket.is_open()) { - m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp - , m_listen_interface.port(), m_listen_interface.port()); + error_code ec; + tcp::endpoint ep = m_udp_socket.local_endpoint(ec); + if (!ec) { + if (m_udp_mapping[1] != -1) m_upnp->delete_mapping(m_udp_mapping[0]); + m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp + , ep.port(), ep.port()); + } } #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open() && ssl_port > 0) { - m_ssl_udp_mapping[1] = m_upnp->add_mapping(upnp::udp - , ssl_port, ssl_port); + error_code ec; + tcp::endpoint ep = m_ssl_udp_socket.local_endpoint(ec); + if (!ec) { + if (m_ssl_udp_mapping[1] != -1) m_upnp->delete_mapping(m_ssl_udp_mapping[0]); + m_ssl_udp_mapping[1] = m_upnp->add_mapping(upnp::udp + , ep.port(), ep.port()); + } } #endif return m_upnp.get(); @@ -6636,31 +6591,36 @@ retry: void session_impl::stop_natpmp() { - if (m_natpmp) + if (!m_natpmp) return; + + m_natpmp->close(); + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) { - m_natpmp->close(); - m_udp_mapping[0] = -1; - m_tcp_mapping[0] = -1; -#ifdef TORRENT_USE_OPENSSL - m_ssl_tcp_mapping[0] = -1; - m_ssl_udp_mapping[0] = -1; -#endif + i->tcp_port_mapping[0] = -1; } + + m_udp_mapping[0] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_udp_mapping[0] = -1; +#endif m_natpmp.reset(); } void session_impl::stop_upnp() { - if (m_upnp) + if (!m_upnp) return; + + m_upnp->close(); + for (std::list::iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) { - m_upnp->close(); - m_udp_mapping[1] = -1; - m_tcp_mapping[1] = -1; -#ifdef TORRENT_USE_OPENSSL - m_ssl_tcp_mapping[1] = -1; - m_ssl_udp_mapping[1] = -1; -#endif + i->tcp_port_mapping[1] = -1; } + m_udp_mapping[1] = -1; +#ifdef TORRENT_USE_OPENSSL + m_ssl_udp_mapping[1] = -1; +#endif m_upnp.reset(); } diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index dabcb5e8b..7d5145f55 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -125,6 +125,12 @@ namespace libtorrent using aux::session_impl; +#if TORRENT_USE_IPV6 +#define DEFAULT_LISTEN_INTERFACE "0.0.0.0:6881,[::]:6881" +#else +#define DEFAULT_LISTEN_INTERFACE "0.0.0.0:6881" +#endif + str_setting_entry_t str_settings[settings_pack::num_string_settings] = { SET(user_agent, "libtorrent/" LIBTORRENT_VERSION, &session_impl::update_user_agent), @@ -132,7 +138,7 @@ namespace libtorrent SET(mmap_cache, 0, 0), SET(handshake_client_version, 0, 0), SET_NOPREV(outgoing_interfaces, "", &session_impl::update_outgoing_interfaces), - SET_NOPREV(listen_interfaces, "0.0.0.0:6881", &session_impl::update_listen_interfaces), + SET_NOPREV(listen_interfaces, DEFAULT_LISTEN_INTERFACE, &session_impl::update_listen_interfaces), SET_NOPREV(proxy_hostname, "", &session_impl::update_proxy), SET_NOPREV(proxy_username, "", &session_impl::update_proxy), SET_NOPREV(proxy_password, "", &session_impl::update_proxy), @@ -140,6 +146,8 @@ namespace libtorrent SET_NOPREV(peer_fingerprint, "-LT1100-", &session_impl::update_peer_fingerprint) }; +#undef DEFAULT_LISTEN_INTERFACE + bool_setting_entry_t bool_settings[settings_pack::num_bool_settings] = { SET(allow_multiple_connections_per_ip, false, 0), diff --git a/src/torrent.cpp b/src/torrent.cpp index 2a85cac98..81115d2ac 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -3058,8 +3058,7 @@ namespace libtorrent #endif - void torrent::announce_with_tracker(boost::uint8_t e - , address const& bind_interface) + void torrent::announce_with_tracker(boost::uint8_t e) { TORRENT_ASSERT(is_single_thread()); INVARIANT_CHECK; @@ -3183,8 +3182,6 @@ namespace libtorrent req.triggered_manually = ae.triggered_manually; ae.triggered_manually = false; - req.bind_ip = bind_interface; - if (settings().get_bool(settings_pack::force_proxy)) { // in force_proxy mode we don't talk directly to trackers @@ -3574,6 +3571,8 @@ namespace libtorrent // matches one of the listen interfaces, since that means this // announce was the second one + // TODO: 3 instead of announcing once per IP version, announce once per + // listen interface (i.e. m_listen_sockets) if (((!is_any(m_ses.get_ipv6_interface().address()) && tracker_ip.is_v4()) || (!is_any(m_ses.get_ipv4_interface().address()) && tracker_ip.is_v6())) && r.bind_ip != m_ses.get_ipv4_interface().address() diff --git a/src/upnp.cpp b/src/upnp.cpp index c769476f6..10aa28458 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -69,8 +69,11 @@ namespace upnp_errors static error_code ignore_error; +// TODO: 3 bind the broadcast socket. it would probably have to be changed to a vector of interfaces to +// bind to, since the broadcast socket opens one socket per local +// interface by default upnp::upnp(io_service& ios - , address const& listen_interface, std::string const& user_agent + , std::string const& user_agent , portmap_callback_t const& cb, log_callback_t const& lcb , bool ignore_nonrouters) : m_user_agent(user_agent) @@ -90,12 +93,6 @@ upnp::upnp(io_service& ios , m_last_if_update(min_time()) { TORRENT_ASSERT(cb); - -// TODO: 3 listen_interface is not used. It's meant to bind the broadcast -// socket. it would probably have to be changed to a vector of interfaces to -// bind to though, since the broadcast socket opens one socket per local -// interface by default - TORRENT_UNUSED(listen_interface); } void upnp::start() From 6d77000ab02255a2c57e43ccea6d6478df742fef Mon Sep 17 00:00:00 2001 From: arvidn Date: Mon, 8 Feb 2016 02:01:25 -0500 Subject: [PATCH 2/5] deprecate ssl_listen setting. instead ssl sockets are specified by an 's' suffix of the port in listen_interfaces. --- include/libtorrent/aux_/session_impl.hpp | 23 +- include/libtorrent/aux_/session_interface.hpp | 1 + include/libtorrent/settings_pack.hpp | 42 ++- include/libtorrent/string_util.hpp | 14 +- src/session_impl.cpp | 273 ++++++++++-------- src/settings_pack.cpp | 2 +- src/string_util.cpp | 43 ++- src/torrent.cpp | 3 + test/test_ssl.cpp | 48 +-- test/test_string.cpp | 42 ++- 10 files changed, 306 insertions(+), 185 deletions(-) diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index d05db0118..263cbd9d4 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -628,6 +628,11 @@ namespace libtorrent libtorrent::utp_socket_manager* utp_socket_manager() TORRENT_OVERRIDE { return &m_utp_socket_manager; } +#ifdef TORRENT_USE_OPENSSL + libtorrent::utp_socket_manager* ssl_utp_socket_manager() TORRENT_OVERRIDE + { return &m_ssl_utp_socket_manager; } +#endif + void inc_boost_connections() TORRENT_OVERRIDE { ++m_boost_connections; } #ifndef TORRENT_NO_DEPRECATE @@ -637,6 +642,13 @@ namespace libtorrent // update any rss feeds that need updating and // recalculate m_next_rss_update void update_rss_feeds(); + + void update_ssl_listen(); + void update_dht_upload_rate_limit(); + void update_local_download_rate(); + void update_local_upload_rate(); + void update_rate_limit_utp(); + void update_ignore_rate_limits_on_local_network(); #endif void update_proxy(); @@ -647,9 +659,6 @@ namespace libtorrent void update_connection_speed(); void update_queued_disk_bytes(); void update_alert_queue_size(); -#ifndef TORRENT_NO_DEPRECATE - void update_dht_upload_rate_limit(); -#endif void update_disk_threads(); void update_network_threads(); void update_cache_buffer_chunk_size(); @@ -674,12 +683,6 @@ namespace libtorrent void update_download_rate(); void update_upload_rate(); void update_connections_limit(); -#ifndef TORRENT_NO_DEPRECATE - void update_local_download_rate(); - void update_local_upload_rate(); - void update_rate_limit_utp(); - void update_ignore_rate_limits_on_local_network(); -#endif void update_alert_mask(); void trigger_auto_manage() TORRENT_OVERRIDE; @@ -847,7 +850,7 @@ namespace libtorrent // the addresses or device names of the interfaces we are supposed to // listen on. if empty, it means that we should let the os decide // which interface to listen on - std::vector > m_listen_interfaces; + std::vector m_listen_interfaces; // the network interfaces outgoing connections are opened through. If // there is more then one, they are used in a round-robin fashion diff --git a/include/libtorrent/aux_/session_interface.hpp b/include/libtorrent/aux_/session_interface.hpp index 480d51e64..0d746656c 100644 --- a/include/libtorrent/aux_/session_interface.hpp +++ b/include/libtorrent/aux_/session_interface.hpp @@ -319,6 +319,7 @@ namespace libtorrent { namespace aux virtual std::vector& block_info_storage() = 0; #ifdef TORRENT_USE_OPENSSL + virtual libtorrent::utp_socket_manager* ssl_utp_socket_manager() = 0; virtual boost::asio::ssl::context* ssl_ctx() = 0 ; #endif diff --git a/include/libtorrent/settings_pack.hpp b/include/libtorrent/settings_pack.hpp index 32374ebc0..c2fdd0309 100644 --- a/include/libtorrent/settings_pack.hpp +++ b/include/libtorrent/settings_pack.hpp @@ -163,20 +163,29 @@ namespace libtorrent // connections. outgoing_interfaces, - // a comma-separated list of IP port-pairs. These - // are the listen ports that will be opened for accepting incoming uTP - // and TCP connections. It is possible to listen on multiple - // IPs and multiple ports. Binding to port 0 will make the - // operating system pick the port. The default is "0.0.0.0:6881", which - // binds to all interfaces on port 6881. - // - // if binding fails, the listen_failed_alert is posted, potentially - // more than once. Once/if binding the listen socket(s) succeed, - // listen_succeeded_alert is posted. - // - // Each port will attempt to open both a UDP and a TCP listen socket, - // to allow accepting uTP connections as well as TCP. If using the DHT, - // this will also make the DHT use the same UDP ports. + // a comma-separated list of (IP or device name, port) pairs. These are + // the listen ports that will be opened for accepting incoming uTP and + // TCP connections. It is possible to listen on multiple interfaces and + // multiple ports. Binding to port 0 will make the operating system + // pick the port. The default is "0.0.0.0:6881,[::]:6881", which binds + // to all interfaces on port 6881. + // + // a port that has an "s" suffix will accept SSL connections. (note + // that SSL sockets are not enabled by default). + // + // if binding fails, the listen_failed_alert is posted. If or once a + // socket binding succeeds, the listen_succeeded_alert is posted. There + // may be multiple failures before a success. + // + // For example: + // ``[::1]:8888`` - will only accept connections on the IPv6 loopback + // address on port 8888. + // + // ``eth0:4444,eth1:4444`` - will accept connections on port 4444 on + // any IP address bound to device ``eth0`` or ``eth1``. + // + // ``[::]:0s`` - will accept SSL connections on a port chosen by the + // OS. And not accept non-SSL connections at all. // // .. note:: // The current support for opening arbitrary UDP sockets is limited. @@ -1418,12 +1427,17 @@ namespace libtorrent // SSL encryption as well. network_threads, +#ifndef TORRENT_NO_DEPRECATE // ``ssl_listen`` sets the listen port for SSL connections. If this is // set to 0, no SSL listen port is opened. Otherwise a socket is // opened on this port. This setting is only taken into account when // opening the regular listen port, and won't re-open the listen // socket simply by changing this setting. ssl_listen, +#else + // hidden + deprecated9, +#endif // ``tracker_backoff`` determines how aggressively to back off from // retrying failing trackers. This value determines *x* in the diff --git a/include/libtorrent/string_util.hpp b/include/libtorrent/string_util.hpp index e8d90d427..a2fd24b16 100644 --- a/include/libtorrent/string_util.hpp +++ b/include/libtorrent/string_util.hpp @@ -67,11 +67,21 @@ namespace libtorrent TORRENT_EXTRA_EXPORT void url_random(char* begin, char* end); + struct listen_interface_t + { + std::string device; + int port; + bool ssl; + }; + // this parses the string that's used as the liste_interfaces setting. // it is a comma-separated list of IP or device names with ports. For // example: "eth0:6881,eth1:6881" or "127.0.0.1:6881" - TORRENT_EXTRA_EXPORT void parse_comma_separated_string_port( - std::string const& in, std::vector >& out); + TORRENT_EXTRA_EXPORT void parse_listen_interfaces( + std::string const& in, std::vector& out); + + TORRENT_EXTRA_EXPORT std::string print_listen_interfaces( + std::vector const& in); // this parses the string that's used as the outgoing_interfaces setting. // it is a comma separated list of IPs and device names. For example: diff --git a/src/session_impl.cpp b/src/session_impl.cpp index da4f388c0..07fc7de7d 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1256,6 +1256,9 @@ namespace aux { #ifdef TORRENT_USE_OPENSSL // SSL torrents use the SSL listen port + // TODO: 2 this need to be more thought through. There isn't necessarily + // just _one_ SSL listen port, which one we use depends on which interface + // we announce from. if (req.ssl_ctx) req.listen_port = ssl_listen_port(); req.ssl_ctx = &m_ssl_ctx; #endif @@ -1584,10 +1587,13 @@ namespace aux { void session_impl::apply_settings_pack_impl(settings_pack const& pack) { bool reopen_listen_port = +#ifndef TORRENT_NO_DEPRECATE (pack.has_val(settings_pack::ssl_listen) && pack.get_int(settings_pack::ssl_listen) != m_settings.get_int(settings_pack::ssl_listen)) - || (pack.has_val(settings_pack::listen_interfaces) + || +#endif + (pack.has_val(settings_pack::listen_interfaces) && pack.get_str(settings_pack::listen_interfaces) != m_settings.get_str(settings_pack::listen_interfaces)); @@ -1661,7 +1667,7 @@ namespace aux { ? listen_failed_alert::tcp_ssl : listen_failed_alert::tcp; ret.sock.reset(new tcp::acceptor(m_io_service)); - ret.sock->open(bind_ep.address().is_v4() ? tcp::v4() : tcp::v6(), ec); + ret.sock->open(bind_ep.protocol(), ec); last_op = listen_failed_alert::open; if (ec) { @@ -1779,6 +1785,7 @@ namespace aux { m_alerts.emplace_alert(device, bind_ep , last_op, ec, sock_type); } + ret.sock.reset(); return ret; } ret.local_endpoint = ret.sock->local_endpoint(ec); @@ -1855,44 +1862,27 @@ namespace aux { for (int i = 0; i < m_listen_interfaces.size(); ++i) { - std::string const& device = m_listen_interfaces[i].first; - int const port = m_listen_interfaces[i].second; + std::string const& device = m_listen_interfaces[i].device; + int const port = m_listen_interfaces[i].port; + bool const ssl = m_listen_interfaces[i].ssl; // now we have a device to bind to. This device may actually just be an // IP address or a device name. In case it's a device name, we want to // (potentially) end up binding a socket for each IP address associated // with that device. - + // First, check to see if it's an IP address error_code err; address adr = address::from_string(device.c_str(), err); if (!err) { listen_socket_t s = setup_listener(device, tcp::endpoint(adr, port) - , flags, ec); + , flags | (ssl ? open_ssl_socket : 0), ec); if (!ec && s.sock) { - TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } - -#ifdef TORRENT_USE_OPENSSL - // TODO: 3 it would probably be better to specify if we want an SSL - // port open as another entry in the listen_interfaces (like in - // mongoose) - if (m_settings.get_int(settings_pack::ssl_listen)) - { - listen_socket_t s = setup_listener(device - , tcp::endpoint(adr, m_settings.get_int(settings_pack::ssl_listen)) - , flags | open_ssl_socket, ec); - - if (!ec && s.sock) - { - m_listen_sockets.push_back(s); - } - } -#endif // TORRENT_USE_OPENSSL } else { @@ -1923,36 +1913,13 @@ namespace aux { if (device != ifs[k].name) continue; listen_socket_t s = setup_listener(device - , tcp::endpoint(ifs[k].interface_address, port), flags, ec); + , tcp::endpoint(ifs[k].interface_address, port) + , flags | (ssl ? open_ssl_socket : 0), ec); if (!ec && 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 - m_listen_interface.port(s.external_port); - - TORRENT_ASSERT(!m_abort); m_listen_sockets.push_back(s); } - -#ifdef TORRENT_USE_OPENSSL - // TODO: 3 it would probably be better to specify if we want an SSL - // port open as another entry in the listen_interfaces (like in - // mongoose) - if (m_settings.get_int(settings_pack::ssl_listen)) - { - int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); - listen_socket_t s = setup_listener(device - , tcp::endpoint(ifs[k].interface_address, port) - , flags | open_ssl_socket, ec); - - if (!ec && s.sock) - { - m_listen_sockets.push_back(s); - } - } -#endif // TORRENT_USE_OPENSSL } } } @@ -1965,80 +1932,99 @@ namespace aux { return; } - // TODO: 3 the udp socket(s) should be using the same generic - // mechanism and not be restricted to a single one - // we should open a one listen socket for each entry in the - // listen_interfaces list - // for now, remember the first successful port, and bind the UDP socket to - // that as well - udp::endpoint udp_bind_ep(m_listen_sockets.begin()->local_endpoint.address() - , m_listen_sockets.begin()->local_endpoint.port()); - int retries = m_settings.get_int(settings_pack::max_retry_port_bind); + // TODO: 3 this loop should be entirely merged with the one above and the + // udp sockets should be opened in parallel with the TCP ones, being held + // by listen_socket_t. + // until the UDP sockets fully honor the listen_interfaces setting, just + // create the two sockets based on the first matching (ssl vs. non-ssl) + // TCP socket +#ifdef TORRENT_USE_OPENSSL + bool created_ssl_udp_socket = false; +#endif + bool created_udp_socket = false; + for (std::list::const_iterator i = m_listen_sockets.begin() + , end(m_listen_sockets.end()); i != end; ++i) + { + listen_socket_t const& s = *i; #ifdef TORRENT_USE_OPENSSL - // TODO: 3 remove ssl_listen setting. Instead, specify the port in the - // listen_interfaces to have an "s" suffix. Just like mongoose - int const ssl_port = m_settings.get_int(settings_pack::ssl_listen); - - // if ssl port is 0, we don't want to listen on an SSL port - if (ssl_port != 0) - { - udp::endpoint ssl_bind_ep(udp_bind_ep.address(), ssl_port); - do + if (!created_ssl_udp_socket && s.ssl) { - ec.clear(); - m_ssl_udp_socket.bind(ssl_bind_ep, ec); - if (ec) + int retries = m_settings.get_int(settings_pack::max_retry_port_bind); + udp::endpoint bind_ep(s.local_endpoint.address(), s.local_endpoint.port()); + do { -#ifndef TORRENT_DISABLE_LOGGING - session_log("SSL: cannot bind to UDP interface \"%s\": %s" - , print_endpoint(ssl_bind_ep).c_str(), ec.message().c_str()); -#endif - if (m_alerts.should_post()) + ec.clear(); + m_ssl_udp_socket.bind(bind_ep, ec); + if (ec) { - error_code err; - m_alerts.emplace_alert(ssl_bind_ep.address().to_string(err) - , tcp::endpoint(ssl_bind_ep.address(), ssl_bind_ep.port()) - , listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl); +#ifndef TORRENT_DISABLE_LOGGING + session_log("SSL: cannot bind to UDP interface \"%s\": %s" + , print_endpoint(bind_ep).c_str(), ec.message().c_str()); +#endif + if (m_alerts.should_post()) + { + error_code err; + m_alerts.emplace_alert(bind_ep.address().to_string(err) + , tcp::endpoint(bind_ep.address(), bind_ep.port()) + , listen_failed_alert::bind, ec, listen_failed_alert::utp_ssl); + } + --retries; + bind_ep.port(bind_ep.port() + 1); } - --retries; - ssl_bind_ep.port(ssl_bind_ep.port() + 1); - } - // TODO: 3 port map SSL udp socket here - } while (ec == error_code(error::address_in_use) && retries > 0); - } - - retries = m_settings.get_int(settings_pack::max_retry_port_bind); + else + { + created_ssl_udp_socket = true; + // TODO: 3 port map SSL udp socket here + } + } while (ec == error_code(error::address_in_use) && retries > 0); + } #endif // TORRENT_USE_OPENSSL - do - { - ec.clear(); - m_udp_socket.bind(udp_bind_ep, ec); - if (ec) + if (!created_udp_socket && !s.ssl) { -#ifndef TORRENT_DISABLE_LOGGING - session_log("cannot bind to UDP interface \"%s\": %s" - , print_endpoint(udp_bind_ep).c_str(), ec.message().c_str()); -#endif - if (m_alerts.should_post()) + int retries = m_settings.get_int(settings_pack::max_retry_port_bind); + udp::endpoint bind_ep(s.local_endpoint.address(), s.local_endpoint.port()); + do { - error_code err; - m_alerts.emplace_alert(udp_bind_ep.address().to_string(err) - , tcp::endpoint(udp_bind_ep.address(), udp_bind_ep.port()) - , listen_failed_alert::bind - , ec, listen_failed_alert::udp); - } - --retries; - udp_bind_ep.port(udp_bind_ep.port() + 1); + ec.clear(); + m_udp_socket.bind(bind_ep, ec); + if (ec) + { +#ifndef TORRENT_DISABLE_LOGGING + session_log("cannot bind to UDP interface \"%s\": %s" + , print_endpoint(bind_ep).c_str(), ec.message().c_str()); +#endif + if (m_alerts.should_post()) + { + error_code err; + m_alerts.emplace_alert(bind_ep.address().to_string(err) + , tcp::endpoint(bind_ep.address(), bind_ep.port()) + , listen_failed_alert::bind + , ec, listen_failed_alert::udp); + } + --retries; + bind_ep.port(bind_ep.port() + 1); + } + else + { + created_udp_socket = true; + m_external_udp_port = m_udp_socket.local_port(); + maybe_update_udp_mapping(0, bind_ep.port(), bind_ep.port()); + maybe_update_udp_mapping(1, bind_ep.port(), bind_ep.port()); + } + } while (ec == error_code(error::address_in_use) && retries > 0); } - else - { - m_external_udp_port = m_udp_socket.local_port(); - maybe_update_udp_mapping(0, udp_bind_ep.port(), udp_bind_ep.port()); - maybe_update_udp_mapping(1, udp_bind_ep.port(), udp_bind_ep.port()); - } - } while (ec == error_code(error::address_in_use) && retries > 0); + } + + // if we did not end up opening a udp socket, make sure we close any + // previous one +#ifdef TORRENT_USE_OPENSSL + if (!created_ssl_udp_socket) + m_ssl_udp_socket.close(); +#endif + if (!created_udp_socket) + m_udp_socket.close(); // we made it! now post all the listen_succeeded_alerts @@ -2061,18 +2047,20 @@ namespace aux { #ifdef TORRENT_USE_OPENSSL if (m_ssl_udp_socket.is_open()) { + error_code err; if (m_alerts.should_post()) m_alerts.emplace_alert( - tcp::endpoint(ssl_bind_ep.address(), ssl_bind_ep.port()) + m_ssl_udp_socket.local_endpoint(err) , listen_succeeded_alert::utp_ssl); } #endif if (m_udp_socket.is_open()) { + error_code err; if (m_alerts.should_post()) m_alerts.emplace_alert( - tcp::endpoint(udp_bind_ep.address(), udp_bind_ep.port()) + m_udp_socket.local_endpoint(err) , listen_succeeded_alert::udp); } @@ -5078,15 +5066,62 @@ namespace aux { TORRENT_ASSERT(m_torrents.find(i_hash) == m_torrents.end()); } +#ifndef TORRENT_NO_DEPRECATE + namespace + { + listen_interface_t set_ssl_flag(listen_interface_t in) + { + in.ssl = true; + return in; + } + } + + void session_impl::update_ssl_listen() + { + INVARIANT_CHECK; + + // this function maps the previous functionality of just setting the ssl + // listen port in order to enable the ssl listen sockets, to the new + // mechanism where SSL sockets are specified in listen_interfaces. + std::vector current_ifaces; + parse_listen_interfaces(m_settings.get_str(settings_pack::listen_interfaces) + , current_ifaces); + // these are the current interfaces we have, first remove all the SSL + // interfaces + current_ifaces.erase(std::remove_if(current_ifaces.begin(), current_ifaces.end() + , boost::bind(&listen_interface_t::ssl, _1)), current_ifaces.end()); + + int const ssl_listen_port = m_settings.get_int(settings_pack::ssl_listen); + + // setting a port of 0 means to disable listening on SSL, so just update + // the interface list with the new list, and we're done + if (ssl_listen_port == 0) + { + m_settings.set_str(settings_pack::listen_interfaces + , print_listen_interfaces(current_ifaces)); + return; + } + + std::vector new_ifaces; + std::transform(current_ifaces.begin(), current_ifaces.end() + , std::back_inserter(new_ifaces), &set_ssl_flag); + + current_ifaces.insert(current_ifaces.end(), new_ifaces.begin(), new_ifaces.end()); + + m_settings.set_str(settings_pack::listen_interfaces + , print_listen_interfaces(current_ifaces)); + } +#endif // TORRENT_NO_DEPRECATE + void session_impl::update_listen_interfaces() { INVARIANT_CHECK; std::string net_interfaces = m_settings.get_str(settings_pack::listen_interfaces); - std::vector > new_listen_interfaces; + std::vector new_listen_interfaces; // declared in string_util.hpp - parse_comma_separated_string_port(net_interfaces, new_listen_interfaces); + parse_listen_interfaces(net_interfaces, new_listen_interfaces); #ifndef TORRENT_DISABLE_LOGGING session_log("update listen interfaces: %s", net_interfaces.c_str()); @@ -5225,6 +5260,8 @@ namespace aux { return m_listen_sockets.front().tcp_external_port; } + // TODO: 2 this function should be removed and users need to deal with the + // more generic case of having multiple ssl ports boost::uint16_t session_impl::ssl_listen_port() const { #ifdef TORRENT_USE_OPENSSL @@ -6500,7 +6537,7 @@ namespace aux { } } #ifdef TORRENT_USE_OPENSSL - if (m_ssl_udp_socket.is_open() && ssl_port > 0) + if (m_ssl_udp_socket.is_open()) { error_code ec; tcp::endpoint ep = m_ssl_udp_socket.local_endpoint(ec); @@ -6551,7 +6588,7 @@ namespace aux { } } #ifdef TORRENT_USE_OPENSSL - if (m_ssl_udp_socket.is_open() && ssl_port > 0) + if (m_ssl_udp_socket.is_open()) { error_code ec; tcp::endpoint ep = m_ssl_udp_socket.local_endpoint(ec); diff --git a/src/settings_pack.cpp b/src/settings_pack.cpp index 7d5145f55..ee640418a 100644 --- a/src/settings_pack.cpp +++ b/src/settings_pack.cpp @@ -330,7 +330,7 @@ namespace libtorrent SET(aio_threads, 4, &session_impl::update_disk_threads), SET(aio_max, 300, 0), SET(network_threads, 0, &session_impl::update_network_threads), - SET(ssl_listen, 4433, 0), + DEPRECATED_SET(ssl_listen, 4433, &session_impl::update_ssl_listen), SET(tracker_backoff, 250, 0), SET_NOPREV(share_ratio_limit, 200, 0), SET_NOPREV(seed_time_ratio_limit, 700, 0), diff --git a/src/string_util.cpp b/src/string_util.cpp index 2bea4ff41..15df6e554 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/random.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/parse_url.hpp" +#include "libtorrent/address.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" @@ -168,11 +169,40 @@ namespace libtorrent return static_cast(p) + (8 - offset); } + std::string print_listen_interfaces(std::vector const& in) + { + std::string ret; + for (std::vector::const_iterator i = in.begin() + , end(in.end()); i != end; ++i) + { + if (i != in.begin()) ret += ","; + + error_code ec; + address_v6::from_string(i->device, ec); + if (!ec) + { + // IPv6 addresses must be wrapped in square brackets + ret += "["; + ret += i->device; + ret += "]"; + } + else + { + ret += i->device; + } + ret += ":"; + ret += to_string(i->port).elems; + if (i->ssl) ret += "s"; + } + + return ret; + } + // this parses the string that's used as the liste_interfaces setting. // it is a comma-separated list of IP or device names with ports. For // example: "eth0:6881,eth1:6881" or "127.0.0.1:6881" - void parse_comma_separated_string_port(std::string const& in - , std::vector >& out) + void parse_listen_interfaces(std::string const& in + , std::vector& out) { out.clear(); @@ -193,7 +223,11 @@ namespace libtorrent if (colon != std::string::npos && colon > start) { - int port = atoi(in.substr(colon + 1, end - colon - 1).c_str()); + listen_interface_t iface; + + std::string port_string = in.substr(colon + 1, end - colon - 1); + iface.ssl = !port_string.empty() && port_string[port_string.size()-1] == 's'; + iface.port = atoi(port_string.c_str()); // skip trailing spaces std::string::size_type soft_end = colon; @@ -206,7 +240,8 @@ namespace libtorrent if (in[start] == '[') ++start; if (soft_end > start && in[soft_end-1] == ']') --soft_end; - out.push_back(std::make_pair(in.substr(start, soft_end - start), port)); + iface.device = in.substr(start, soft_end - start); + out.push_back(iface); } start = end + 1; diff --git a/src/torrent.cpp b/src/torrent.cpp index 81115d2ac..e666f8dcf 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -7629,6 +7629,9 @@ namespace libtorrent if (is_ssl_torrent() && settings().get_int(settings_pack::ssl_listen) != 0) { userdata = m_ssl_ctx.get(); + // if we're creating a uTP socket, since this is SSL now, make sure + // to pass in the corresponding utp socket manager + if (sm) sm = m_ses.ssl_utp_socket_manager(); } #endif diff --git a/test/test_ssl.cpp b/test/test_ssl.cpp index 83073433d..4c3f13d06 100644 --- a/test/test_ssl.cpp +++ b/test/test_ssl.cpp @@ -100,8 +100,11 @@ bool on_alert(alert const* a) if (peer_disconnected_alert const* e = alert_cast(a)) { ++peer_disconnects; - if (e->error.category() == boost::asio::error::get_ssl_category()) + if (strcmp(e->error.category().name(), boost::asio::error::get_ssl_category().name()) == 0) ++ssl_peer_disconnects; + + fprintf(stderr, "--- peer_errors: %d ssl_disconnects: %d\n" + , peer_errors, ssl_peer_disconnects); } if (peer_error_alert const* e = alert_cast(a)) @@ -109,8 +112,11 @@ bool on_alert(alert const* a) ++peer_disconnects; ++peer_errors; - if (e->error.category() == boost::asio::error::get_ssl_category()) + if (strcmp(e->error.category().name(), boost::asio::error::get_ssl_category().name()) == 0) ++ssl_peer_disconnects; + + fprintf(stderr, "--- peer_errors: %d ssl_disconnects: %d\n" + , peer_errors, ssl_peer_disconnects); } return false; } @@ -132,11 +138,14 @@ void test_ssl(int test_idx, bool use_utp) remove_all("tmp1_ssl", ec); remove_all("tmp2_ssl", ec); - int ssl_port = 1024 + rand() % 50000; + int port = 1024 + rand() % 50000; settings_pack sett; sett.set_int(settings_pack::alert_mask, alert_mask); sett.set_int(settings_pack::max_retry_port_bind, 100); - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); + + char listen_iface[100]; + snprintf(listen_iface, sizeof(listen_iface), "0.0.0.0:%ds", port); + sett.set_str(settings_pack::listen_interfaces, listen_iface); sett.set_bool(settings_pack::enable_incoming_utp, use_utp); sett.set_bool(settings_pack::enable_outgoing_utp, use_utp); sett.set_bool(settings_pack::enable_incoming_tcp, !use_utp); @@ -147,14 +156,19 @@ void test_ssl(int test_idx, bool use_utp) sett.set_bool(settings_pack::enable_natpmp, false); // if a peer fails once, don't try it again sett.set_int(settings_pack::max_failcount, 1); - sett.set_int(settings_pack::ssl_listen, ssl_port); libtorrent::session ses1(sett, 0); + // this +20 is here to use a different port as ses1 + port += 20; + + // the +20 below is the port we use for non-SSL connections if (test.downloader_has_ssl_listen_port) - sett.set_int(settings_pack::ssl_listen, ssl_port + 20); + snprintf(listen_iface, sizeof(listen_iface), "0.0.0.0:%d,0.0.0.0:%ds", port + 20, port); else - sett.set_int(settings_pack::ssl_listen, 0); + snprintf(listen_iface, sizeof(listen_iface), "0.0.0.0:%d", port + 20); + + sett.set_str(settings_pack::listen_interfaces, listen_iface); libtorrent::session ses2(sett, 0); @@ -213,15 +227,7 @@ void test_ssl(int test_idx, bool use_utp) wait_for_downloading(ses2, "ses2"); // connect the peers after setting the certificates - int port = 0; - if (test.use_ssl_ports) - if (test.downloader_has_ssl_listen_port) - port = ses2.ssl_listen_port(); - else - port = 13512; - else - port = ses2.listen_port(); - + if (test.use_ssl_ports == false) port += 20; fprintf(stderr, "\n\n%s: ses1: connecting peer port: %d\n\n\n" , time_now_string(), port); tor1.connect_peer(tcp::endpoint(address::from_string("127.0.0.1", ec) @@ -541,12 +547,14 @@ void test_malicious_peer() remove_all("tmp3_ssl", ec); // set up session - int ssl_port = 1024 + rand() % 50000; + int port = 1024 + rand() % 50000; settings_pack sett; sett.set_int(settings_pack::alert_mask, alert_mask); sett.set_int(settings_pack::max_retry_port_bind, 100); - sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:48075"); - sett.set_int(settings_pack::ssl_listen, ssl_port); + + char listen_iface[100]; + snprintf(listen_iface, sizeof(listen_iface), "0.0.0.0:%ds", port); + sett.set_str(settings_pack::listen_interfaces, listen_iface); sett.set_bool(settings_pack::enable_dht, false); sett.set_bool(settings_pack::enable_lsd, false); sett.set_bool(settings_pack::enable_upnp, false); @@ -588,7 +596,7 @@ void test_malicious_peer() for (int i = 0; i < num_attacks; ++i) { - bool success = try_connect(ses1, ssl_port, t, attacks[i].flags); + bool const success = try_connect(ses1, port, t, attacks[i].flags); TEST_EQUAL(success, attacks[i].expect); } } diff --git a/test/test_string.cpp b/test/test_string.cpp index 6c74457dd..5795158f0 100644 --- a/test/test_string.cpp +++ b/test/test_string.cpp @@ -257,24 +257,34 @@ TORRENT_TEST(string) TEST_EQUAL(list[5], "foobar"); TEST_EQUAL(list[6], "[::1]"); - std::vector > list2; - parse_comma_separated_string_port(" a:4,b:35, c : 1000, d: 351 ,e \t:42,foobar:1337\n\r,[2001::1]:6881", list2); + std::vector list2; + parse_listen_interfaces(" a:4,b:35, c : 1000s, d: 351 ,e \t:42,foobar:1337s\n\r,[2001::1]:6881", list2); TEST_EQUAL(list2.size(), 7); - TEST_EQUAL(list2[0].first, "a"); - TEST_EQUAL(list2[1].first, "b"); - TEST_EQUAL(list2[2].first, "c"); - TEST_EQUAL(list2[3].first, "d"); - TEST_EQUAL(list2[4].first, "e"); - TEST_EQUAL(list2[5].first, "foobar"); - TEST_EQUAL(list2[6].first, "2001::1"); + TEST_EQUAL(list2[0].device, "a"); + TEST_EQUAL(list2[1].device, "b"); + TEST_EQUAL(list2[2].device, "c"); + TEST_EQUAL(list2[3].device, "d"); + TEST_EQUAL(list2[4].device, "e"); + TEST_EQUAL(list2[5].device, "foobar"); + TEST_EQUAL(list2[6].device, "2001::1"); - TEST_EQUAL(list2[0].second, 4); - TEST_EQUAL(list2[1].second, 35); - TEST_EQUAL(list2[2].second, 1000); - TEST_EQUAL(list2[3].second, 351); - TEST_EQUAL(list2[4].second, 42); - TEST_EQUAL(list2[5].second, 1337); - TEST_EQUAL(list2[6].second, 6881); + TEST_EQUAL(list2[0].port, 4); + TEST_EQUAL(list2[1].port, 35); + TEST_EQUAL(list2[2].port, 1000); + TEST_EQUAL(list2[3].port, 351); + TEST_EQUAL(list2[4].port, 42); + TEST_EQUAL(list2[5].port, 1337); + TEST_EQUAL(list2[6].port, 6881); + + TEST_EQUAL(list2[0].ssl, false); + TEST_EQUAL(list2[1].ssl, false); + TEST_EQUAL(list2[2].ssl, true); + TEST_EQUAL(list2[3].ssl, false); + TEST_EQUAL(list2[4].ssl, false); + TEST_EQUAL(list2[5].ssl, true); + TEST_EQUAL(list2[6].ssl, false); + + TEST_EQUAL(print_listen_interfaces(list2), "a:4,b:35,c:1000s,d:351,e:42,foobar:1337s,[2001::1]:6881"); // test string_tokenize From 72a24b63baa3e00afc1fb702ca505a181836952c Mon Sep 17 00:00:00 2001 From: arvidn Date: Mon, 8 Feb 2016 18:11:00 -0500 Subject: [PATCH 3/5] use bind_to_device for listen sockets --- include/libtorrent/Makefile.am | 1 + include/libtorrent/alert_types.hpp | 2 +- include/libtorrent/aux_/bind_to_device.hpp | 112 +++++++++++++++++++++ include/libtorrent/enum_net.hpp | 23 +---- src/alert.cpp | 3 +- src/session_impl.cpp | 29 +++++- src/string_util.cpp | 2 + 7 files changed, 149 insertions(+), 23 deletions(-) create mode 100644 include/libtorrent/aux_/bind_to_device.hpp diff --git a/include/libtorrent/Makefile.am b/include/libtorrent/Makefile.am index b6918ae8e..97bd6d182 100644 --- a/include/libtorrent/Makefile.am +++ b/include/libtorrent/Makefile.am @@ -17,6 +17,7 @@ nobase_include_HEADERS = \ bandwidth_queue_entry.hpp \ bencode.hpp \ bdecode.hpp \ + bind_to_device.hpp \ bitfield.hpp \ block_cache.hpp \ bloom_filter.hpp \ diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index 8298f88b3..c6094bd81 100644 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -1286,7 +1286,7 @@ namespace libtorrent enum op_t { - parse_addr, open, bind, listen, get_socket_name, accept, enum_if + parse_addr, open, bind, listen, get_socket_name, accept, enum_if, bind_to_device }; // the specific low level operation that failed. See op_t. diff --git a/include/libtorrent/aux_/bind_to_device.hpp b/include/libtorrent/aux_/bind_to_device.hpp new file mode 100644 index 000000000..2a1742831 --- /dev/null +++ b/include/libtorrent/aux_/bind_to_device.hpp @@ -0,0 +1,112 @@ +/* + +Copyright (c) 2016, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_BIND_TO_DEVICE_HPP_INCLUDED +#define TORRENT_BIND_TO_DEVICE_HPP_INCLUDED + +#include "libtorrent/config.hpp" +#include "libtorrent/socket.hpp" + +#if TORRENT_USE_IFCONF || TORRENT_USE_NETLINK || TORRENT_USE_SYSCTL +#include // for SO_BINDTODEVICE +#include +#endif + +namespace libtorrent { namespace aux { + +#if defined SO_BINDTODEVICE + + struct bind_to_device + { + bind_to_device(char const* device): m_value(device) {} + template + int level(Protocol const&) const { return SOL_SOCKET; } + template + int name(Protocol const&) const { return SO_BINDTODEVICE; } + template + char const* data(Protocol const&) const { return m_value; } + template + size_t size(Protocol const&) const { return strlen(m_value) + 1; } + private: + char const* m_value; + }; + +#define TORRENT_HAS_BINDTODEVICE 1 + +#elif defined IP_BOUND_IF + + struct bind_to_device + { + bind_to_device(char const* device): m_value(if_nametoindex(device)) {} + template + int level(Protocol const&) const { return IPPROTO_IP; } + template + int name(Protocol const&) const { return IP_BOUND_IF; } + template + char const* data(Protocol const&) const { return reinterpret_cast(&m_value); } + template + size_t size(Protocol const&) const { return sizeof(m_value); } + private: + unsigned int m_value; + }; + +#define TORRENT_HAS_BINDTODEVICE 1 + +#elif defined IP_FORCE_OUT_IFP + + struct bind_to_device + { + bind_to_device(char const* device): m_value(device) {} + template + int level(Protocol const&) const { return SOL_SOCKET; } + template + int name(Protocol const&) const { return IP_FORCE_OUT_IFP; } + template + char const* data(Protocol const&) const { return m_value; } + template + size_t size(Protocol const&) const { return strlen(m_value) + 1; } + private: + char const* m_value; + }; + +#define TORRENT_HAS_BINDTODEVICE 1 + +#else + +#define TORRENT_HAS_BINDTODEVICE 0 + +#endif + +} } + +#endif + diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index c82c8e187..af59ff3c4 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/address.hpp" #include "libtorrent/error_code.hpp" #include "libtorrent/socket.hpp" +#include "libtorrent/aux_/bind_to_device.hpp" namespace libtorrent { @@ -93,22 +94,6 @@ namespace libtorrent TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios, error_code& ec); -#ifdef SO_BINDTODEVICE - struct bind_to_device_opt - { - bind_to_device_opt(char const* device): m_value(device) {} - template - int level(Protocol const&) const { return SOL_SOCKET; } - template - int name(Protocol const&) const { return SO_BINDTODEVICE; } - template - const char* data(Protocol const&) const { return m_value; } - template - size_t size(Protocol const&) const { return IFNAMSIZ; } - char const* m_value; - }; -#endif - // attempt to bind socket to the device with the specified name. For systems // that don't support SO_BINDTODEVICE the socket will be bound to one of the // IP addresses of the specified device. In this case it is necessary to @@ -116,7 +101,7 @@ namespace libtorrent // the returned address is the ip the socket was bound to (or address_v4::any() // in case SO_BINDTODEVICE succeeded and we don't need to verify it). template - address bind_to_device(io_service& ios, Socket& sock + address bind_socket_to_device(io_service& ios, Socket& sock , boost::asio::ip::tcp const& protocol , char const* device_name, int port, error_code& ec) { @@ -140,10 +125,10 @@ namespace libtorrent ec.clear(); -#ifdef SO_BINDTODEVICE +#if TORRENT_HAS_BINDTODEVICE // try to use SO_BINDTODEVICE here, if that exists. If it fails, // fall back to the mechanism we have below - sock.set_option(bind_to_device_opt(device_name), ec); + sock.set_option(aux::bind_to_device(device_name), ec); if (ec) #endif { diff --git a/src/alert.cpp b/src/alert.cpp index 3df130dda..e11827866 100644 --- a/src/alert.cpp +++ b/src/alert.cpp @@ -818,7 +818,8 @@ namespace libtorrent { "listen", "get_socket_name", "accept", - "enum_if" + "enum_if", + "bind_to_device" }; char ret[300]; snprintf(ret, sizeof(ret), "listening on %s (device: %s) failed: [%s] [%s] %s" diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 07fc7de7d..4fa5ae351 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -117,6 +117,7 @@ const rlim_t rlim_infinity = RLIM_INFINITY; #include "libtorrent/choker.hpp" #include "libtorrent/error.hpp" #include "libtorrent/platform_util.hpp" +#include "libtorrent/aux_/bind_to_device.hpp" #ifndef TORRENT_DISABLE_LOGGING @@ -1737,6 +1738,30 @@ namespace aux { } #endif // TORRENT_USE_IPV6 + if (!device.empty()) + { + // we have an actual device we're interested in listening on, if we + // have SO_BINDTODEVICE functionality, use it now. +#if TORRENT_HAS_BINDTODEVICE + ret.sock->set_option(bind_to_device(device.c_str()), ec); + if (ec) + { +#ifndef TORRENT_DISABLE_LOGGING + session_log("bind to device failed (device: %s): %s" + , device.c_str(), ec.message().c_str()); +#endif // TORRENT_DISABLE_LOGGING + + last_op = listen_failed_alert::bind_to_device; + if (m_alerts.should_post()) + { + m_alerts.emplace_alert(device, bind_ep + , last_op, ec, sock_type); + } + return ret; + } +#endif + } + ret.sock->bind(bind_ep, ec); last_op = listen_failed_alert::bind; @@ -1876,7 +1901,7 @@ namespace aux { address adr = address::from_string(device.c_str(), err); if (!err) { - listen_socket_t s = setup_listener(device, tcp::endpoint(adr, port) + listen_socket_t s = setup_listener("", tcp::endpoint(adr, port) , flags | (ssl ? open_ssl_socket : 0), ec); if (!ec && s.sock) @@ -4910,7 +4935,7 @@ namespace aux { if (ec) return bind_ep; - bind_ep.address(bind_to_device(m_io_service, s + bind_ep.address(bind_socket_to_device(m_io_service, s , remote_address.is_v4() ? boost::asio::ip::tcp::v4() : boost::asio::ip::tcp::v6() diff --git a/src/string_util.cpp b/src/string_util.cpp index 15df6e554..38eded3c7 100644 --- a/src/string_util.cpp +++ b/src/string_util.cpp @@ -177,6 +177,7 @@ namespace libtorrent { if (i != in.begin()) ret += ","; +#if TORRENT_USE_IPV6 error_code ec; address_v6::from_string(i->device, ec); if (!ec) @@ -187,6 +188,7 @@ namespace libtorrent ret += "]"; } else +#endif { ret += i->device; } From 0ef28ae1a3ee2a962a5dc63e9fee011c6f53c117 Mon Sep 17 00:00:00 2001 From: arvidn Date: Mon, 8 Feb 2016 23:43:20 -0500 Subject: [PATCH 4/5] updating changelog --- ChangeLog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog b/ChangeLog index 8b516ecb0..7d06232cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * improved support for bind-to-device + * deprecated ssl_listen, SSL sockets are specified in listen_interfaces now * improved support for listening on multiple sockets and interfaces * resume data no longer has timestamps of files From b2a6e8bf06a71aa4b5328ed1e1371c5f63c521f5 Mon Sep 17 00:00:00 2001 From: arvidn Date: Thu, 11 Feb 2016 00:39:59 -0500 Subject: [PATCH 5/5] make client_test expose the new binding options --- examples/client_test.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/examples/client_test.cpp b/examples/client_test.cpp index 12a00b6de..8090516f3 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -588,13 +588,11 @@ int print_peer_info(std::string& out return pos; } -int listen_port = 6881; int allocation_mode = libtorrent::storage_mode_sparse; std::string save_path("."); int torrent_upload_limit = 0; int torrent_download_limit = 0; std::string monitor_dir; -std::string bind_to_interface = ""; int poll_interval = 5; int max_connections_per_torrent = 50; bool seed_mode = false; @@ -1238,7 +1236,6 @@ int main(int argc, char* argv[]) " -v Set the max number of active downloads\n" " -^ Set the max number of active seeds\n" "\n NETWORK OPTIONS\n" - " -p sets the listen port\n" #ifndef TORRENT_NO_DEPRECATE " -o limits the number of simultaneous\n" " half-open TCP connections to the\n" @@ -1257,10 +1254,14 @@ int main(int argc, char* argv[]) " incoming TCP connections)\n" " -J Disable uTP connections (disable outgoing uTP and reject\n" " incoming uTP connections)\n" - " -b sets IP of the interface to bind the\n" + " -b sets the listen interfaces string. This is a\n" + " comma separated list of IP:port pairs. Instead\n" + " of an IP, a network interface device name can\n" + " be specified\n" " listen socket to\n" - " -I sets the IP of the interface to bind\n" - " outgoing peer connections to\n" + " -I sets the IPs or network interface devices to bind\n" + " outgoing peer connections to. This can be a\n" + " comma-separated list" #if TORRENT_USE_I2P " -i the hostname to an I2P SAM bridge to use\n" #endif @@ -1361,7 +1362,6 @@ int main(int argc, char* argv[]) case 'o': settings.set_int(settings_pack::half_open_limit, atoi(arg)); break; #endif case 'h': settings.set_bool(settings_pack::allow_multiple_connections_per_ip, true); --i; break; - case 'p': listen_port = atoi(arg); break; case 'k': high_performance_seed(settings); --i; break; case 'j': settings.set_bool(settings_pack::use_disk_read_ahead, false); --i; break; case 'z': settings.set_bool(settings_pack::disable_hash_checks, true); --i; break; @@ -1386,7 +1386,7 @@ int main(int argc, char* argv[]) case 'D': torrent_download_limit = atoi(arg) * 1000; break; case 'm': monitor_dir = arg; break; case 'Q': share_mode = true; --i; break; - case 'b': bind_to_interface = arg; break; + case 'b': settings.set_str(settings_pack::listen_interfaces, arg); break; case 'w': settings.set_int(settings_pack::urlseed_wait_retry, atoi(arg)); break; case 't': poll_interval = atoi(arg); break; case 'F': refresh_delay = atoi(arg); break; @@ -1533,12 +1533,6 @@ int main(int argc, char* argv[]) fprintf(stderr, "failed to create resume file directory: (%d) %s\n" , errno, strerror(errno)); - if (bind_to_interface.empty()) bind_to_interface = "0.0.0.0"; - char iface_str[100]; - snprintf(iface_str, sizeof(iface_str), "%s:%d", bind_to_interface.c_str() - , listen_port); - settings.set_str(settings_pack::listen_interfaces, iface_str); - settings.set_str(settings_pack::user_agent, "client_test/" LIBTORRENT_VERSION); settings.set_int(settings_pack::alert_mask, alert::all_categories & ~(alert::dht_notification