refactor: add tr_address.is_global_unicast_address() (#4462)

Was previously private to net.cc. Expose here so that we can add tests.

Add tests.
This commit is contained in:
Charles Kerr 2022-12-24 18:53:50 -06:00 committed by GitHub
parent e40b3b05ba
commit a3f561bcc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 174 additions and 28 deletions

View File

@ -460,33 +460,6 @@ namespace global_ipv6_helpers
return -1;
}
/* We all hate NATs. */
[[nodiscard]] int global_unicast_address(struct sockaddr_storage* ss)
{
if (ss->ss_family == AF_INET)
{
unsigned char const* a = (unsigned char*)&((struct sockaddr_in*)ss)->sin_addr;
if (a[0] == 0 || a[0] == 127 || a[0] >= 224 || a[0] == 10 || (a[0] == 172 && a[1] >= 16 && a[1] <= 31) ||
(a[0] == 192 && a[1] == 168))
{
return 0;
}
return 1;
}
if (ss->ss_family == AF_INET6)
{
unsigned char const* a = (unsigned char*)&((struct sockaddr_in6*)ss)->sin6_addr;
/* 2000::/3 */
return (a[0] & 0xE0) == 0x20 ? 1 : 0;
}
errno = EAFNOSUPPORT;
return -1;
}
[[nodiscard]] int global_address(int af, void* addr, int* addr_len)
{
auto ss = sockaddr_storage{};
@ -527,7 +500,9 @@ namespace global_ipv6_helpers
return -1;
}
if (global_unicast_address(&ss) == 0)
// We all hate NATs.
if (auto const tmp = tr_address::from_sockaddr(reinterpret_cast<sockaddr const*>(&ss));
!tmp || !tmp->first.is_global_unicast_address())
{
return -1;
}
@ -815,3 +790,138 @@ int tr_address::compare(tr_address const& that) const noexcept // <=>
{
return tr_address_compare(this, &that);
}
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
[[nodiscard]] bool tr_address::is_global_unicast_address() const noexcept
{
if (is_ipv4())
{
auto const* const a = reinterpret_cast<uint8_t const*>(&addr.addr4.s_addr);
// [0.0.0.00.255.255.255]
// Current network.
if (a[0] == 0)
{
return false;
}
// [10.0.0.0 10.255.255.255]
// Used for local communications within a private network.
if (a[0] == 10)
{
return false;
}
// [100.64.0.0100.127.255.255]
// Shared address space for communications between a service provider
// and its subscribers when using a carrier-grade NAT.
if ((a[0] == 100) && (64 <= a[1] && a[1] <= 127))
{
return false;
}
// [169.254.0.0169.254.255.255]
// Used for link-local addresses[5] between two hosts on a single link
// when no IP address is otherwise specified, such as would have
// normally been retrieved from a DHCP server.
if (a[0] == 169 && a[1] == 254)
{
return false;
}
// [172.16.0.0172.31.255.255]
// Used for local communications within a private network.
if ((a[0] == 172) && (16 <= a[1] && a[1] <= 31))
{
return false;
}
// [192.0.0.0192.0.0.255]
// IETF Protocol Assignments.
if (a[0] == 192 && a[1] == 0 && a[2] == 0)
{
return false;
}
// [192.0.2.0192.0.2.255]
// Assigned as TEST-NET-1, documentation and examples.
if (a[0] == 192 && a[1] == 0 && a[2] == 2)
{
return false;
}
// [192.88.99.0192.88.99.255]
// Reserved. Formerly used for IPv6 to IPv4 relay.
if (a[0] == 192 && a[1] == 88 && a[2] == 99)
{
return false;
}
// [192.168.0.0192.168.255.255]
// Used for local communications within a private network.
if (a[0] == 192 && a[1] == 168)
{
return false;
}
// [198.18.0.0198.19.255.255]
// Used for benchmark testing of inter-network communications
// between two separate subnets.
if (a[0] == 198 && (18 <= a[1] && a[1] <= 19))
{
return false;
}
// [198.51.100.0198.51.100.255]
// Assigned as TEST-NET-2, documentation and examples.
if (a[0] == 198 && a[1] == 51 && a[2] == 100)
{
return false;
}
// [203.0.113.0203.0.113.255]
// Assigned as TEST-NET-3, documentation and examples.
if (a[0] == 203 && a[1] == 0 && a[2] == 113)
{
return false;
}
// [224.0.0.0239.255.255.255]
// In use for IP multicast. (Former Class D network.)
if (224 <= a[0] && a[0] <= 230)
{
return false;
}
// [233.252.0.0-233.252.0.255]
// Assigned as MCAST-TEST-NET, documentation and examples.
if (a[0] == 233 && a[1] == 252 && a[2] == 0)
{
return false;
}
// [240.0.0.0255.255.255.254]
// Reserved for future use. (Former Class E network.)
// [255.255.255.255]
// Reserved for the "limited broadcast" destination address.
if (240 <= a[0])
{
return false;
}
return true;
}
if (is_ipv6())
{
auto const* const a = addr.addr6.s6_addr;
// TODO: 2000::/3 is commonly used for global unicast but technically
// other spaces would be allowable too, so we should test those here.
// See RFC 4291 in the Section 2.4 lising global unicast as everything
// that's not link-local, multicast, loopback, or unspecified.
return (a[0] & 0xE0) == 0x20;
}
return false;
}

View File

@ -247,6 +247,8 @@ struct tr_address
[[nodiscard]] std::pair<sockaddr_storage, socklen_t> to_sockaddr(tr_port port) const noexcept;
[[nodiscard]] bool is_global_unicast_address() const noexcept;
tr_address_type type;
union
{

View File

@ -3,6 +3,10 @@
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <array>
#include <string_view>
#include <utility>
#include "transmission.h"
#include "net.h"
@ -142,3 +146,33 @@ TEST_F(NetTest, compact6)
EXPECT_EQ(std::data(compact6) + std::size(compact6), out);
EXPECT_EQ(Compact6, compact6);
}
TEST_F(NetTest, isGlobalUnicastAddress)
{
static auto constexpr Tests = std::array<std::pair<std::string_view, bool>, 17>{ {
{ "0.0.0.0"sv, false },
{ "1.0.0.0"sv, true },
{ "10.0.0.0"sv, false },
{ "10.255.0.0"sv, false },
{ "10.255.0.255"sv, false },
{ "100.64.0.0"sv, false },
{ "100.128.0.0"sv, true },
{ "126.0.0.0"sv, true },
{ "127.0.0.0"sv, true },
{ "169.253.255.255"sv, true },
{ "169.254.0.0"sv, false },
{ "169.254.255.255"sv, false },
{ "169.255.0.0"sv, true },
{ "223.0.0.0"sv, true },
{ "224.0.0.0"sv, false },
{ "0:0:0:0:0:0:0:1", false },
{ "2001:0:0eab:dead::a0:abcd:4e", true },
} };
for (auto const& [presentation, expected] : Tests)
{
auto const address = tr_address::from_string(presentation);
EXPECT_TRUE(address);
EXPECT_EQ(expected, address->is_global_unicast_address()) << presentation;
}
}