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()