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.
|
||||
//
|
||||
// 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(
|
||||
tr_address const& dst_addr,
|
||||
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
|
||||
{
|
||||
update_source_addr(type);
|
||||
if (global_source_addr(type))
|
||||
if (source_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);
|
||||
|
||||
auto const protocol = tr_ip_protocol_to_sv(type);
|
||||
|
||||
auto err = 0;
|
||||
auto const& source_addr = get_global_source_address(bind_addr(type), err);
|
||||
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);
|
||||
tr_logAddDebug(fmt::format(
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
* This class caches 3 useful info:
|
||||
* 1. Whether your machine supports the IP protocol
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
class tr_ip_cache
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
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] };
|
||||
return source_addr_[type];
|
||||
|
||||
@@ -257,6 +257,16 @@ struct tr_address
|
||||
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;
|
||||
|
||||
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.
|
||||
// otherwise, if we can determine which one to use via global_source_address(ipv6) magic, use it.
|
||||
// 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 :
|
||||
tr_address::any(TR_AF_INET6);
|
||||
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);
|
||||
}
|
||||
|
||||
[[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));
|
||||
return ip_cache_.global_source_addr(type);
|
||||
return ip_cache_.source_addr(type);
|
||||
}
|
||||
|
||||
[[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;
|
||||
}
|
||||
else if (
|
||||
addrport && addrport->address().is_global_unicast_address() &&
|
||||
!session_.global_source_address(tr_af_to_ip_protocol(to->sa_family)))
|
||||
addrport && !addrport->address().is_ipv4_loopback_address() && !addrport->address().is_ipv6_loopback_address() &&
|
||||
!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;
|
||||
}
|
||||
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_->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)
|
||||
{
|
||||
GTEST_SKIP() << "globalSourceIPv4 did not return an address, either:\n"
|
||||
<< "1. globalSourceIPv4 is broken\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());
|
||||
}
|
||||
@@ -199,13 +199,13 @@ TEST_F(IPCacheTest, globalSourceIPv6)
|
||||
ip_cache_ = std::make_unique<tr_ip_cache>(mediator);
|
||||
|
||||
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)
|
||||
{
|
||||
GTEST_SKIP() << "globalSourceIPv6 did not return an address, either:\n"
|
||||
<< "1. globalSourceIPv6 is broken\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());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user