diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index cd148c020..96795c0d9 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -176,6 +176,7 @@ namespace libtorrent void start_dht(entry const& startup_state); void stop_dht(); entry dht_state() const; + void maybe_update_udp_mapping(int nat, int local_port, int external_port); #endif #ifndef TORRENT_DISABLE_ENCRYPTION diff --git a/include/libtorrent/natpmp.hpp b/include/libtorrent/natpmp.hpp index eac46b3d0..000c1aa99 100644 --- a/include/libtorrent/natpmp.hpp +++ b/include/libtorrent/natpmp.hpp @@ -63,6 +63,7 @@ public: enum protocol_type { none = 0, udp = 1, tcp = 2 }; int add_mapping(protocol_type p, int external_port, int local_port); void delete_mapping(int mapping_index); + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; void close(); @@ -88,6 +89,7 @@ private: , local_port(0) , external_port(0) , protocol(none) + , map_sent(false) {} // indicates that the mapping has changed @@ -107,6 +109,9 @@ private: int external_port; int protocol; + + // set to true when the first map request is sent + bool map_sent; }; portmap_callback_t m_callback; diff --git a/include/libtorrent/upnp.hpp b/include/libtorrent/upnp.hpp index c82bb3232..f0368196c 100644 --- a/include/libtorrent/upnp.hpp +++ b/include/libtorrent/upnp.hpp @@ -79,6 +79,7 @@ public: enum protocol_type { none = 0, udp = 1, tcp = 2 }; int add_mapping(protocol_type p, int external_port, int local_port); void delete_mapping(int mapping_index); + bool get_mapping(int mapping_index, int& local_port, int& external_port, int& protocol) const; void discover_device(); void close(); diff --git a/src/natpmp.cpp b/src/natpmp.cpp index 77b03e562..80af9af8a 100644 --- a/src/natpmp.cpp +++ b/src/natpmp.cpp @@ -114,6 +114,18 @@ void natpmp::rebind(address const& listen_interface) } } +bool natpmp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + void natpmp::log(std::string const& msg) { m_callback(-1, 0, msg); @@ -132,6 +144,7 @@ void natpmp::disable(char const* message) } close(); } + void natpmp::delete_mapping(int index) { TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); @@ -139,6 +152,12 @@ void natpmp::delete_mapping(int index) mapping_t& m = m_mappings[index]; if (m.protocol == none) return; + if (!m.map_sent) + { + m.action = mapping_t::action_none; + m.protocol = none; + return; + } m.action = mapping_t::action_delete; update_mapping(index); @@ -270,7 +289,7 @@ void natpmp::send_map_request(int i) write_uint32(ttl, out); // port mapping lifetime std::stringstream msg; - msg << "port map [ action: " << (m.action == mapping_t::action_add ? "add" : "delete") + msg << "==> port map [ action: " << (m.action == mapping_t::action_add ? "add" : "delete") << " proto: " << (m.protocol == udp ? "udp" : "tcp") << " local: " << m.local_port << " external: " << m.external_port << " ttl: " << ttl << " ]"; @@ -278,6 +297,7 @@ void natpmp::send_map_request(int i) error_code ec; m_socket.send_to(asio::buffer(buf, 12), m_nat_endpoint, 0, ec); + m.map_sent = true; // linear back-off instead of exponential ++m_retry_count; m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec); @@ -348,7 +368,7 @@ void natpmp::on_reply(error_code const& e (void)time; // to remove warning std::stringstream msg; - msg << "port map [" + msg << "<== port map [" << " protocol: " << (cmd - 128 == 1 ? "udp" : "tcp") << " local: " << private_port << " external: " << public_port << " ttl: " << lifetime << " ]"; @@ -486,7 +506,7 @@ void natpmp::close() { mutex_t::scoped_lock l(m_mutex); m_abort = true; - error_code ec; + log("closing"); /* #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) m_log << time_now_string() << " close" << std::endl; @@ -511,6 +531,7 @@ void natpmp::close() if (i->protocol == none) continue; i->action = mapping_t::action_delete; } + error_code ec; m_refresh_timer.cancel(ec); update_mapping(0); } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index a3616555b..e1a479e9e 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1990,20 +1990,9 @@ namespace aux { m_dht_settings.service_port = new_interface.port(); // the listen interface changed, rebind the dht listen socket as well m_dht_socket.bind(m_dht_settings.service_port); - if (m_natpmp.get()) - { - if (m_udp_mapping[0] != -1) m_natpmp->delete_mapping(m_udp_mapping[0]); - m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::tcp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } - if (m_upnp.get()) - { - if (m_udp_mapping[1] != -1) m_upnp->delete_mapping(m_udp_mapping[1]); - m_udp_mapping[1] = m_upnp->add_mapping(upnp::tcp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } + + maybe_update_udp_mapping(0, m_dht_settings.service_port, m_dht_settings.service_port); + maybe_update_udp_mapping(1, m_dht_settings.service_port, m_dht_settings.service_port); } #endif @@ -2194,18 +2183,8 @@ namespace aux { m_dht_settings.service_port = 45000 + (rand() % 10000); } m_external_udp_port = m_dht_settings.service_port; - if (m_natpmp.get() && m_udp_mapping[0] == -1) - { - m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } - if (m_upnp.get() && m_udp_mapping[1] == -1) - { - m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } + maybe_update_udp_mapping(0, m_dht_settings.service_port, m_dht_settings.service_port); + maybe_update_udp_mapping(1, m_dht_settings.service_port, m_dht_settings.service_port); m_dht = new dht::dht_tracker(*this, m_dht_socket, m_dht_settings, &startup_state); if (!m_dht_socket.is_open() || m_dht_socket.local_port() != m_dht_settings.service_port) { @@ -2222,6 +2201,45 @@ namespace aux { m_dht->start(startup_state); } +#ifndef TORRENT_DISABLE_DHT + void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port) + { + int local, external, protocol; + if (nat == 0 && m_natpmp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_natpmp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_natpmp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_natpmp->add_mapping(natpmp::udp + , local_port, external_port); + return; + } + else if (nat == 1 && m_upnp.get()) + { + if (m_udp_mapping[nat] != -1) + { + if (m_upnp->get_mapping(m_udp_mapping[nat], local, external, protocol)) + { + // we already have a mapping. If it's the same, don't do anything + if (local == local_port && external == external_port && protocol == natpmp::udp) + return; + } + m_upnp->delete_mapping(m_udp_mapping[nat]); + } + m_udp_mapping[nat] = m_upnp->add_mapping(upnp::udp + , local_port, external_port); + return; + } + } +#endif + void session_impl::stop_dht() { mutex_t::scoped_lock l(m_mutex); @@ -2246,20 +2264,8 @@ namespace aux { { m_dht_socket.bind(settings.service_port); - if (m_natpmp.get()) - { - if (m_udp_mapping[0] != -1) m_upnp->delete_mapping(m_udp_mapping[0]); - m_udp_mapping[0] = m_natpmp->add_mapping(natpmp::udp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } - if (m_upnp.get()) - { - if (m_udp_mapping[1] != -1) m_upnp->delete_mapping(m_udp_mapping[1]); - m_udp_mapping[1] = m_upnp->add_mapping(upnp::udp - , m_dht_settings.service_port - , m_dht_settings.service_port); - } + maybe_update_udp_mapping(0, settings.service_port, settings.service_port); + maybe_update_udp_mapping(1, settings.service_port, settings.service_port); m_external_udp_port = settings.service_port; } m_dht_settings = settings; diff --git a/src/upnp.cpp b/src/upnp.cpp index 28eaae4c9..85941c7e7 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -228,6 +228,18 @@ void upnp::delete_mapping(int mapping) } } +bool upnp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const +{ + TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0); + if (index >= int(m_mappings.size()) || index < 0) return false; + global_mapping_t const& m = m_mappings[index]; + if (m.protocol == none) return false; + local_port = m.local_port; + external_port = m.external_port; + protocol = m.protocol; + return true; +} + void upnp::resend_request(error_code const& e) { if (e) return;