mirror of
https://github.com/transmission/transmission.git
synced 2025-12-20 02:18:42 +00:00
fix: caching a source address doesn't imply public internet connectivity (#7520)
This commit is contained in:
@@ -63,6 +63,15 @@ namespace global_source_ip_helpers
|
|||||||
// and check its source address.
|
// and check its source address.
|
||||||
//
|
//
|
||||||
// Since it's a UDP socket, this doesn't actually send any packets
|
// Since it's a UDP socket, this doesn't actually send any packets
|
||||||
|
//
|
||||||
|
// N.B. Successfully obtaining a source address does not imply
|
||||||
|
// connectivity to the given destination address, since all connect()
|
||||||
|
// does is setting the default remote address for subsequent send() and
|
||||||
|
// recv() calls.
|
||||||
|
//
|
||||||
|
// Having said that, the connect() step is still needed because on Windows,
|
||||||
|
// calling getsockname() might not return what we want before calling
|
||||||
|
// connect() if we are binding to 0.0.0.0 or ::.
|
||||||
[[nodiscard]] std::optional<tr_address> get_source_address(
|
[[nodiscard]] std::optional<tr_address> get_source_address(
|
||||||
tr_address const& dst_addr,
|
tr_address const& dst_addr,
|
||||||
tr_port dst_port,
|
tr_port dst_port,
|
||||||
@@ -221,7 +230,7 @@ bool tr_ip_cache::set_global_addr(tr_address const& addr_new) noexcept
|
|||||||
void tr_ip_cache::update_addr(tr_address_type type) noexcept
|
void tr_ip_cache::update_addr(tr_address_type type) noexcept
|
||||||
{
|
{
|
||||||
update_source_addr(type);
|
update_source_addr(type);
|
||||||
if (global_source_addr(type))
|
if (source_addr(type))
|
||||||
{
|
{
|
||||||
update_global_addr(type);
|
update_global_addr(type);
|
||||||
}
|
}
|
||||||
@@ -272,11 +281,9 @@ void tr_ip_cache::update_source_addr(tr_address_type type) noexcept
|
|||||||
TR_ASSERT(is_updating_[type] == is_updating_t::YES);
|
TR_ASSERT(is_updating_[type] == is_updating_t::YES);
|
||||||
|
|
||||||
auto const protocol = tr_ip_protocol_to_sv(type);
|
auto const protocol = tr_ip_protocol_to_sv(type);
|
||||||
|
|
||||||
auto err = 0;
|
auto err = 0;
|
||||||
auto const& source_addr = get_global_source_address(bind_addr(type), err);
|
|
||||||
source_addr_checked_[type] = true;
|
source_addr_checked_[type] = true;
|
||||||
if (source_addr)
|
if (auto const& source_addr = get_global_source_address(bind_addr(type), err); source_addr)
|
||||||
{
|
{
|
||||||
set_source_addr(*source_addr);
|
set_source_addr(*source_addr);
|
||||||
tr_logAddDebug(fmt::format(
|
tr_logAddDebug(fmt::format(
|
||||||
|
|||||||
@@ -28,10 +28,10 @@
|
|||||||
* This class caches 3 useful info:
|
* This class caches 3 useful info:
|
||||||
* 1. Whether your machine supports the IP protocol
|
* 1. Whether your machine supports the IP protocol
|
||||||
* 2. Source address used for global connections
|
* 2. Source address used for global connections
|
||||||
* 3. Global address
|
* 3. Global address (IPv4 public address/IPv6 global unicast address)
|
||||||
*
|
*
|
||||||
* The idea is, if this class successfully cached a source address, that means
|
* The idea is, if this class successfully cached a source address, that means
|
||||||
* you have connectivity to the public internet. And if the global address is
|
* your system is capable in that IP protocol. And if the global address is
|
||||||
* the same as the source address, then you are not behind a NAT.
|
* the same as the source address, then you are not behind a NAT.
|
||||||
*/
|
*/
|
||||||
class tr_ip_cache
|
class tr_ip_cache
|
||||||
@@ -70,7 +70,7 @@ public:
|
|||||||
return global_addr_[type];
|
return global_addr_[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<tr_address> global_source_addr(tr_address_type type) const noexcept
|
[[nodiscard]] std::optional<tr_address> source_addr(tr_address_type type) const noexcept
|
||||||
{
|
{
|
||||||
auto const lock = std::shared_lock{ source_addr_mutex_[type] };
|
auto const lock = std::shared_lock{ source_addr_mutex_[type] };
|
||||||
return source_addr_[type];
|
return source_addr_[type];
|
||||||
|
|||||||
@@ -257,6 +257,16 @@ struct tr_address
|
|||||||
return is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr6);
|
return is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr.addr6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool is_ipv4_loopback_address() const noexcept
|
||||||
|
{
|
||||||
|
return is_ipv4() && reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr)[0] == 127U;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool is_ipv6_loopback_address() const noexcept
|
||||||
|
{
|
||||||
|
return is_ipv6() && IN6_IS_ADDR_LOOPBACK(&addr.addr6);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<tr_address> from_ipv4_mapped() const noexcept;
|
[[nodiscard]] std::optional<tr_address> from_ipv4_mapped() const noexcept;
|
||||||
|
|
||||||
tr_address_type type = NUM_TR_AF_INET_TYPES;
|
tr_address_type type = NUM_TR_AF_INET_TYPES;
|
||||||
|
|||||||
@@ -447,7 +447,7 @@ tr_address tr_session::bind_address(tr_address_type type) const noexcept
|
|||||||
// if user provided an address, use it.
|
// if user provided an address, use it.
|
||||||
// otherwise, if we can determine which one to use via global_source_address(ipv6) magic, use it.
|
// otherwise, if we can determine which one to use via global_source_address(ipv6) magic, use it.
|
||||||
// otherwise, use any_ipv6 (::).
|
// otherwise, use any_ipv6 (::).
|
||||||
auto const source_addr = global_source_address(type);
|
auto const source_addr = source_address(type);
|
||||||
auto const default_addr = source_addr && source_addr->is_global_unicast_address() ? *source_addr :
|
auto const default_addr = source_addr && source_addr->is_global_unicast_address() ? *source_addr :
|
||||||
tr_address::any(TR_AF_INET6);
|
tr_address::any(TR_AF_INET6);
|
||||||
return tr_address::from_string(settings_.bind_address_ipv6).value_or(default_addr);
|
return tr_address::from_string(settings_.bind_address_ipv6).value_or(default_addr);
|
||||||
|
|||||||
@@ -1045,10 +1045,10 @@ public:
|
|||||||
return ip_cache_.set_global_addr(addr);
|
return ip_cache_.set_global_addr(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<tr_address> global_source_address(tr_address_type type) const noexcept
|
[[nodiscard]] std::optional<tr_address> source_address(tr_address_type type) const noexcept
|
||||||
{
|
{
|
||||||
TR_ASSERT(tr_address::is_valid(type));
|
TR_ASSERT(tr_address::is_valid(type));
|
||||||
return ip_cache_.global_source_addr(type);
|
return ip_cache_.source_addr(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto speed_limit(tr_direction const dir) const noexcept
|
[[nodiscard]] auto speed_limit(tr_direction const dir) const noexcept
|
||||||
|
|||||||
@@ -284,10 +284,10 @@ void tr_session::tr_udp_core::sendto(void const* buf, size_t buflen, struct sock
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
addrport && addrport->address().is_global_unicast_address() &&
|
addrport && !addrport->address().is_ipv4_loopback_address() && !addrport->address().is_ipv6_loopback_address() &&
|
||||||
!session_.global_source_address(tr_af_to_ip_protocol(to->sa_family)))
|
!session_.source_address(tr_af_to_ip_protocol(to->sa_family)))
|
||||||
{
|
{
|
||||||
// don't try to connect to a global address if we don't have connectivity to public internet
|
// don't try to send if we don't have a route in this IP protocol
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (::sendto(sock, static_cast<char const*>(buf), buflen, 0, to, tolen) != -1)
|
else if (::sendto(sock, static_cast<char const*>(buf), buflen, 0, to, tolen) != -1)
|
||||||
|
|||||||
@@ -175,13 +175,13 @@ TEST_F(IPCacheTest, globalSourceIPv4)
|
|||||||
ip_cache_ = std::make_unique<tr_ip_cache>(mediator);
|
ip_cache_ = std::make_unique<tr_ip_cache>(mediator);
|
||||||
|
|
||||||
ip_cache_->update_source_addr(TR_AF_INET);
|
ip_cache_->update_source_addr(TR_AF_INET);
|
||||||
auto const addr = ip_cache_->global_source_addr(TR_AF_INET);
|
auto const addr = ip_cache_->source_addr(TR_AF_INET);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
{
|
{
|
||||||
GTEST_SKIP() << "globalSourceIPv4 did not return an address, either:\n"
|
GTEST_SKIP() << "globalSourceIPv4 did not return an address, either:\n"
|
||||||
<< "1. globalSourceIPv4 is broken\n"
|
<< "1. globalSourceIPv4 is broken\n"
|
||||||
<< "2. Your system does not support IPv4\n"
|
<< "2. Your system does not support IPv4\n"
|
||||||
<< "3. You don't have IPv4 connectivity to public internet";
|
<< "3. You don't have an IPv4 address to your interface";
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(addr->is_ipv4());
|
EXPECT_TRUE(addr->is_ipv4());
|
||||||
}
|
}
|
||||||
@@ -199,13 +199,13 @@ TEST_F(IPCacheTest, globalSourceIPv6)
|
|||||||
ip_cache_ = std::make_unique<tr_ip_cache>(mediator);
|
ip_cache_ = std::make_unique<tr_ip_cache>(mediator);
|
||||||
|
|
||||||
ip_cache_->update_source_addr(TR_AF_INET6);
|
ip_cache_->update_source_addr(TR_AF_INET6);
|
||||||
auto const addr = ip_cache_->global_source_addr(TR_AF_INET6);
|
auto const addr = ip_cache_->source_addr(TR_AF_INET6);
|
||||||
if (!addr)
|
if (!addr)
|
||||||
{
|
{
|
||||||
GTEST_SKIP() << "globalSourceIPv6 did not return an address, either:\n"
|
GTEST_SKIP() << "globalSourceIPv6 did not return an address, either:\n"
|
||||||
<< "1. globalSourceIPv6 is broken\n"
|
<< "1. globalSourceIPv6 is broken\n"
|
||||||
<< "2. Your system does not support IPv6\n"
|
<< "2. Your system does not support IPv6\n"
|
||||||
<< "3. You don't have IPv6 connectivity to public internet";
|
<< "3. You don't have an IPv6 address to your interface";
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(addr->is_ipv6());
|
EXPECT_TRUE(addr->is_ipv6());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user