Track the mapped port for each NAT mapping transport (#3793)

* Track the mapped port for each NAT mapping transport
Each transport needs to be tracked separately so that a failed mapping request
doesn't cause the listen port for a socket to be cleared to zero.
* add template specialization to allow enums as indexes
* use aux::array for port mappings
This commit is contained in:
Steven Siloti 2019-04-21 16:50:44 -07:00 committed by Arvid Norberg
parent 27a49523a3
commit aec5c50282
6 changed files with 74 additions and 51 deletions

View File

@ -10,6 +10,7 @@
* support magnet link parameters with number siffixes
* consistently use "lt" namespace in examples and documentation
* fix Mingw build to use native cryptoAPI
* uPnP/NAT-PMP errors no longer set the client's advertised listen port to zero
1.2 release

View File

@ -133,15 +133,15 @@ namespace aux {
only_outgoing
};
struct listen_port_mapping
{
port_mapping_t mapping = port_mapping_t{-1};
int port = 0;
};
struct listen_socket_t
{
listen_socket_t()
{
tcp_port_mapping[0] = port_mapping_t{-1};
tcp_port_mapping[1] = port_mapping_t{-1};
udp_port_mapping[0] = port_mapping_t{-1};
udp_port_mapping[1] = port_mapping_t{-1};
}
listen_socket_t() = default;
// listen_socket_t should not be copied or moved because
// references to it are held by the DHT and tracker announce
@ -169,19 +169,35 @@ namespace aux {
// had to retry binding with a higher port
int original_port = 0;
// 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
// port that is open on the external (NAT) interface
// 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 tcp_external_port = 0;
int udp_external_port = 0;
// tcp_external_port and udp_external_port return the port which
// should be published to peers/trackers for this socket
// If there are active NAT mappings the return value will be
// the external port returned by the NAT router, otherwise the
// local listen port is returned
int tcp_external_port()
{
for (auto const& m : tcp_port_mapping)
{
if (m.port != 0) return m.port;
}
return local_endpoint.port();
}
int udp_external_port()
{
for (auto const& m : udp_port_mapping)
{
if (m.port != 0) return m.port;
}
if (udp_sock) return udp_sock->sock.local_port();
return 0;
}
// 0 is natpmp 1 is upnp
port_mapping_t tcp_port_mapping[2];
port_mapping_t udp_port_mapping[2];
// the order of these arrays determines the priorty in
// which their ports will be announced to peers
aux::array<listen_port_mapping, 2, portmap_transport> tcp_port_mapping;
aux::array<listen_port_mapping, 2, portmap_transport> udp_port_mapping;
// indicates whether this is an SSL listen socket or not
transport ssl = transport::plaintext;

View File

@ -94,11 +94,15 @@ namespace libtorrent { namespace aux {
UnderlyingType m_val;
};
// meta function to return the underlying type of a strong_typedef, or the
// type itself if it isn't a strong_typedef
template <typename T>
// meta function to return the underlying type of a strong_typedef or enumeration
// , or the type itself if it isn't a strong_typedef
template <typename T, typename = void>
struct underlying_index_t { using type = T; };
template <typename T>
struct underlying_index_t<T, typename std::enable_if<std::is_enum<T>::value>::type>
{ using type = typename std::underlying_type<T>::type; };
template <typename U, typename Tag>
struct underlying_index_t<aux::strong_typedef<U, Tag>> { using type = U; };

View File

@ -61,7 +61,7 @@ struct obs : dht::dht_observer
, address const& /* source */) override
{}
int get_listen_port(lt::aux::transport, lt::aux::listen_socket_handle const& s) override
{ return s.get()->udp_external_port; }
{ return s.get()->udp_external_port(); }
void get_peers(sha1_hash const&) override {}
void outgoing_get_peers(sha1_hash const& /* target */
, sha1_hash const& /* sent_target */, udp::endpoint const& /* ep */) override {}

View File

@ -1521,8 +1521,7 @@ namespace aux {
return ret;
}
ret->tcp_external_port = ret->local_endpoint.port();
TORRENT_ASSERT(ret->tcp_external_port == bind_ep.port()
TORRENT_ASSERT(ret->local_endpoint.port() == bind_ep.port()
|| bind_ep.port() == 0);
ret->sock->listen(m_settings.get_int(settings_pack::listen_queue_size), ec);
@ -1635,7 +1634,6 @@ namespace aux {
return ret;
}
ret->udp_external_port = ret->udp_sock->sock.local_port();
// if we did not open a TCP listen socket, ret->local_endpoint was never
// initialized, so do that now, based on the UDP socket
@ -1673,7 +1671,7 @@ namespace aux {
{
session_log(" listening on: %s TCP port: %d UDP port: %d"
, bind_ep.address().to_string().c_str()
, ret->tcp_external_port, ret->udp_external_port);
, ret->tcp_external_port(), ret->udp_external_port());
}
#endif
return ret;
@ -2144,13 +2142,17 @@ namespace aux {
if ((mask & remap_natpmp) && s.natpmp_mapper)
{
map_port(*s.natpmp_mapper, portmap_protocol::tcp, tcp_ep, s.tcp_port_mapping[0]);
map_port(*s.natpmp_mapper, portmap_protocol::udp, make_tcp(udp_ep), s.udp_port_mapping[0]);
map_port(*s.natpmp_mapper, portmap_protocol::tcp, tcp_ep
, s.tcp_port_mapping[portmap_transport::natpmp].mapping);
map_port(*s.natpmp_mapper, portmap_protocol::udp, make_tcp(udp_ep)
, s.udp_port_mapping[portmap_transport::natpmp].mapping);
}
if ((mask & remap_upnp) && m_upnp)
{
map_port(*m_upnp, portmap_protocol::tcp, tcp_ep, s.tcp_port_mapping[1]);
map_port(*m_upnp, portmap_protocol::udp, make_tcp(udp_ep), s.udp_port_mapping[1]);
map_port(*m_upnp, portmap_protocol::tcp, tcp_ep
, s.tcp_port_mapping[portmap_transport::upnp].mapping);
map_port(*m_upnp, portmap_protocol::udp, make_tcp(udp_ep)
, s.udp_port_mapping[portmap_transport::upnp].mapping);
}
}
@ -2183,7 +2185,7 @@ namespace aux {
});
if (ls != m_listen_sockets.end())
return (*ls)->udp_external_port;
return (*ls)->udp_external_port();
else
return -1;
}
@ -5369,9 +5371,9 @@ namespace aux {
// connections. We may be able to accept uTP connections though, so
// announce the UDP port instead
if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none)
return std::uint16_t(sock->udp_external_port);
return std::uint16_t(sock->udp_external_port());
else
return std::uint16_t(sock->tcp_external_port);
return std::uint16_t(sock->tcp_external_port());
}
#ifdef TORRENT_USE_OPENSSL
@ -5380,14 +5382,14 @@ namespace aux {
if (s->ssl == transport::plaintext)
{
if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none)
return std::uint16_t(s->udp_external_port);
return std::uint16_t(s->udp_external_port());
else
return std::uint16_t(s->tcp_external_port);
return std::uint16_t(s->tcp_external_port());
}
}
return 0;
#else
return std::uint16_t(m_listen_sockets.front()->tcp_external_port);
return std::uint16_t(m_listen_sockets.front()->tcp_external_port());
#endif
}
@ -5408,9 +5410,9 @@ namespace aux {
// connections. We may be able to accept uTP connections though, so
// announce the UDP port instead
if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none)
return std::uint16_t(sock->udp_external_port);
return std::uint16_t(sock->udp_external_port());
else
return std::uint16_t(sock->tcp_external_port);
return std::uint16_t(sock->tcp_external_port());
}
if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none)
@ -5421,9 +5423,9 @@ namespace aux {
if (s->ssl == transport::ssl)
{
if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none)
return std::uint16_t(s->udp_external_port);
return std::uint16_t(s->udp_external_port());
else
return std::uint16_t(s->tcp_external_port);
return std::uint16_t(s->tcp_external_port());
}
}
#else
@ -5447,7 +5449,7 @@ namespace aux {
if (alt_socket != m_listen_sockets.end())
socket = alt_socket->get();
}
return socket->udp_external_port;
return socket->udp_external_port();
}
int session_impl::listen_port(transport const ssl, address const& local_addr)
@ -5461,7 +5463,7 @@ namespace aux {
|| (listen_addr.is_v4() == local_addr.is_v4() && listen_addr.is_unspecified()));
});
if (socket != m_listen_sockets.end())
return (*socket)->tcp_external_port;
return (*socket)->tcp_external_port();
return 0;
}
@ -5521,13 +5523,13 @@ namespace aux {
bool find_tcp_port_mapping(portmap_transport const transport
, port_mapping_t mapping, std::shared_ptr<listen_socket_t> const& ls)
{
return ls->tcp_port_mapping[static_cast<int>(transport)] == mapping;
return ls->tcp_port_mapping[transport].mapping == mapping;
}
bool find_udp_port_mapping(portmap_transport const transport
, port_mapping_t mapping, std::shared_ptr<listen_socket_t> const& ls)
{
return ls->udp_port_mapping[static_cast<int>(transport)] == mapping;
return ls->udp_port_mapping[transport].mapping == mapping;
}
}
@ -5572,8 +5574,8 @@ namespace aux {
(*ls)->external_address.cast_vote(ip, source_router, address());
}
if (tcp) (*ls)->tcp_external_port = port;
else (*ls)->udp_external_port = port;
if (tcp) (*ls)->tcp_port_mapping[transport].port = port;
else (*ls)->tcp_port_mapping[transport].port = port;
}
if (!ec && m_alerts.should_post<portmap_alert>())
@ -6731,8 +6733,8 @@ namespace aux {
{
for (auto& s : m_listen_sockets)
{
s->tcp_port_mapping[0] = port_mapping_t{-1};
s->udp_port_mapping[0] = port_mapping_t{-1};
s->tcp_port_mapping[portmap_transport::natpmp] = listen_port_mapping();
s->udp_port_mapping[portmap_transport::natpmp] = listen_port_mapping();
if (!s->natpmp_mapper) continue;
s->natpmp_mapper->close();
s->natpmp_mapper.reset();
@ -6746,8 +6748,8 @@ namespace aux {
m_upnp->close();
for (auto& s : m_listen_sockets)
{
s->tcp_port_mapping[1] = port_mapping_t{-1};
s->udp_port_mapping[1] = port_mapping_t{-1};
s->tcp_port_mapping[portmap_transport::upnp] = listen_port_mapping();
s->udp_port_mapping[portmap_transport::upnp] = listen_port_mapping();
}
m_upnp.reset();
}

View File

@ -525,7 +525,7 @@ struct obs : dht::dht_observer
}
int get_listen_port(aux::transport, aux::listen_socket_handle const& s) override
{ return s.get()->udp_external_port; }
{ return s.get()->udp_external_port(); }
void get_peers(sha1_hash const&) override {}
void outgoing_get_peers(sha1_hash const& /*target*/