Compare commits

...

25 Commits

Author SHA1 Message Date
JCW
80f649055b Address clang-tidy error 2026-07-06 15:41:06 +01:00
JCW
cffdaea8f1 Fix test errors 2026-07-06 12:00:20 +01:00
Jingchen
26de3f2561 Merge branch 'develop' into a1q123456/fix-is-valid-develop 2026-07-06 11:50:01 +01:00
JCW
c2b07f02b2 Merge remote-tracking branch 'private/develop' into a1q123456/fix-is-valid-develop 2026-05-19 15:01:11 +01:00
JCW
ff2ac99e45 Fix clang-tidy error 2026-05-14 11:40:37 +01:00
Ed Hennis
9a93adea7d Merge branch 'develop' into a1q123456/fix-is-valid-develop 2026-05-13 20:15:29 -04:00
JCW
ba4bf0935a Fix clang-tidy errors 2026-05-13 12:20:57 +01:00
JCW
7db79b3606 Fix an issue 2026-05-13 11:40:46 +01:00
JCW
d9f188fa94 Merge remote-tracking branch 'private/develop' into a1q123456/fix-is-valid-develop 2026-05-12 21:04:49 +01:00
JCW
821b0d6764 Fix clang-tidy errors 2026-05-12 21:04:27 +01:00
JCW
8340cd645b Address PR comments 2026-05-12 20:35:31 +01:00
JCW
9f385aa62a Merge remote-tracking branch 'private/develop' into a1q123456/fix-is-valid-develop 2026-05-12 18:31:08 +01:00
Jingchen
267737fe76 Update src/xrpld/peerfinder/detail/Logic.h
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-05-12 18:13:15 +01:00
Ayaz Salikhov
0317d3fad2 Merge branch 'develop' into a1q123456/fix-is-valid-develop 2026-04-16 15:23:24 +01:00
JCW
f04c12bc84 Fix formatting 2026-04-16 14:01:26 +01:00
JCW
b2760c04c4 Fix for clang-tidy 2026-04-16 12:33:57 +01:00
JCW
06937e68c0 Fix 2026-04-10 17:11:08 +01:00
JCW
3ccbee28d1 Fix 2026-04-10 17:11:08 +01:00
JCW
494af3aee8 Update comment 2026-04-10 17:11:08 +01:00
JCW
e938405a57 Address PR comments 2026-04-10 17:11:07 +01:00
JCW
068b62bcae Add logging 2026-04-10 17:11:07 +01:00
JCW
39808c8d7c Add logging 2026-04-10 17:11:07 +01:00
JCW
4146596ebc Add an option to allow disabling endpoint verification after tightening the requirements 2026-04-10 17:11:07 +01:00
JCW
98c361608a Address PR comments 2026-04-10 17:10:27 +01:00
JCW
154e0d3a27 Fix is_valid_address 2026-04-10 17:03:41 +01:00
2 changed files with 267 additions and 1 deletions

View File

@@ -305,6 +305,15 @@ public:
BEAST_EXPECT(!isLoopback(ep));
BEAST_EXPECTS(to_string(ep) == "fd00::1", to_string(ep));
// unspecified IPv6 (::)
ep = Endpoint(AddressV6{});
BEAST_EXPECT(isUnspecified(ep));
BEAST_EXPECT(!isPublic(ep));
BEAST_EXPECT(!isPrivate(ep));
BEAST_EXPECT(!isMulticast(ep));
BEAST_EXPECT(!isLoopback(ep));
BEAST_EXPECTS(to_string(ep) == "::", to_string(ep));
{
ep = Endpoint::fromString("192.0.2.112");
BEAST_EXPECT(!isUnspecified(ep));

View File

@@ -21,6 +21,7 @@
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
namespace xrpl::PeerFinder {
@@ -64,8 +65,9 @@ public:
void
asyncConnect(beast::IP::Endpoint const& ep, Handler&& handler)
{
// NOLINTNEXTLINE(misc-const-correctness)
boost::system::error_code ec;
handler(ep, ep, ec);
handler(ec);
}
};
@@ -368,6 +370,259 @@ public:
}
}
void
testIsValidAddress()
{
testcase("is_valid_address");
TestStore store;
TestChecker checker;
TestStopwatch clock;
Logic<TestChecker> logic(clock, store, checker, journal_);
auto const pass = [&](std::string const& s) {
BEAST_EXPECT(logic.isValidAddress(beast::IP::Endpoint::fromString(s)));
};
auto const fail = [&](std::string const& s) {
BEAST_EXPECT(!logic.isValidAddress(beast::IP::Endpoint::fromString(s)));
};
// Invalid: port 0
fail("65.0.0.1:0");
// --- IPv4 ranges ---
// For each range: 1 before (pass), first (fail), last (fail),
// 1 after (pass)
// 0.0.0.0/8 - "This network"
// No "before" - nothing before 0.0.0.0
fail("0.0.0.0:8080");
fail("0.255.255.255:8080");
pass("1.0.0.0:8080");
// 10.0.0.0/8 - Private (RFC 1918)
pass("9.255.255.255:8080");
fail("10.0.0.0:8080");
fail("10.255.255.255:8080");
pass("11.0.0.0:8080");
// 100.64.0.0/10 - Shared Address Space / CGNAT (RFC 6598)
pass("100.63.255.255:8080");
fail("100.64.0.0:8080");
fail("100.127.255.255:8080");
pass("100.128.0.0:8080");
// 127.0.0.0/8 - Loopback
pass("126.255.255.255:8080");
fail("127.0.0.0:8080");
fail("127.255.255.255:8080");
pass("128.0.0.0:8080");
// 169.254.0.0/16 - Link-local
pass("169.253.255.255:8080");
fail("169.254.0.0:8080");
fail("169.254.255.255:8080");
pass("169.255.0.0:8080");
// 172.16.0.0/12 - Private (RFC 1918)
pass("172.15.255.255:8080");
fail("172.16.0.0:8080");
fail("172.31.255.255:8080");
pass("172.32.0.0:8080");
// 192.0.0.0/24 - IETF Protocol Assignments (RFC 6890)
pass("191.255.255.255:8080");
fail("192.0.0.0:8080");
fail("192.0.0.255:8080");
pass("192.0.1.0:8080");
// 192.0.2.0/24 - TEST-NET-1 (RFC 5737)
pass("192.0.1.255:8080");
fail("192.0.2.0:8080");
fail("192.0.2.255:8080");
pass("192.0.3.0:8080");
// 192.88.99.0/24 - 6to4 Relay Anycast (RFC 7526)
pass("192.88.98.255:8080");
fail("192.88.99.0:8080");
fail("192.88.99.255:8080");
pass("192.88.100.0:8080");
// 192.168.0.0/16 - Private (RFC 1918)
pass("192.167.255.255:8080");
fail("192.168.0.0:8080");
fail("192.168.255.255:8080");
pass("192.169.0.0:8080");
// 198.18.0.0/15 - Benchmarking (RFC 2544)
pass("198.17.255.255:8080");
fail("198.18.0.0:8080");
fail("198.19.255.255:8080");
pass("198.20.0.0:8080");
// 198.51.100.0/24 - TEST-NET-2 (RFC 5737)
pass("198.51.99.255:8080");
fail("198.51.100.0:8080");
fail("198.51.100.255:8080");
pass("198.51.101.0:8080");
// 203.0.113.0/24 - TEST-NET-3 (RFC 5737)
pass("203.0.112.255:8080");
fail("203.0.113.0:8080");
fail("203.0.113.255:8080");
pass("203.0.114.0:8080");
// 224.0.0.0/4 - Multicast
pass("223.255.255.255:8080");
fail("224.0.0.0:8080");
fail("239.255.255.255:8080");
// 240.0.0.0 (after multicast) is also blocked (reserved)
// 240.0.0.0/4 - Reserved (RFC 1112)
// 239.255.255.255 (before reserved) is also blocked (multicast)
fail("240.0.0.0:8080");
fail("255.255.255.255:8080");
// --- IPv6 ranges ---
// ::1 - Loopback (single address)
fail("[::1]:8080");
// :: - Unspecified (single address)
fail("[::]:8080");
// fc00::/7 - Unique Local Address (ULA)
pass("[fb00::1]:8080");
fail("[fc00::1]:8080");
fail("[fdff::1]:8080");
pass("[fe00::1]:8080");
// fe80::/10 - Link-local
pass("[fe7f::1]:8080");
fail("[fe80::1]:8080");
fail("[febf::1]:8080");
pass("[fec0::1]:8080");
// ff00::/8 - Multicast
pass("[feff::1]:8080");
fail("[ff00::1]:8080");
fail("[ffff::1]:8080");
// No "after" - ffff:... is the highest IPv6 range
// 100::/64 - Discard prefix (RFC 6666)
pass("[ff::1]:8080");
fail("[100::]:8080");
fail("[100::ffff:ffff:ffff:ffff]:8080");
pass("[100:0:0:1::1]:8080");
// 2001::/32 - IETF Protocol Assignments / Teredo (RFC 4380)
pass("[2000:ffff::1]:8080");
fail("[2001::]:8080");
fail("[2001:0:ffff::1]:8080");
pass("[2001:1::1]:8080");
// 2001:20::/28 - ORCHIDv2 (RFC 7343)
pass("[2001:1f::1]:8080");
fail("[2001:20::1]:8080");
fail("[2001:2f::1]:8080");
pass("[2001:30::1]:8080");
// 2001:db8::/32 - Documentation (RFC 3849)
pass("[2001:db7::1]:8080");
fail("[2001:db8::1]:8080");
fail("[2001:db8:ffff::1]:8080");
pass("[2001:db9::1]:8080");
// 2002::/16 - 6to4 (RFC 3056, deprecated)
pass("[2001:ffff::1]:8080");
fail("[2002::1]:8080");
fail("[2002:ffff::1]:8080");
pass("[2003::1]:8080");
// --- IPv6 v4-mapped (delegates to IPv4 checks) ---
fail("[::ffff:10.0.0.1]:8080");
fail("[::ffff:100.64.0.1]:8080");
fail("[::ffff:169.254.1.1]:8080");
fail("[::ffff:192.0.2.1]:8080");
fail("[::ffff:198.18.0.1]:8080");
fail("[::ffff:224.0.0.1]:8080");
fail("[::ffff:240.0.0.1]:8080");
// --- Valid public addresses ---
pass("8.8.8.8:443");
pass("65.0.0.1:8080");
pass("[2001:4860:4860::8888]:8080");
pass("[2606:4700:4700::1111]:8080");
}
void
testVerifyEndpoints()
{
// Helper that sets up a Logic instance, creates and activates a slot,
// then calls on_endpoints with the given list and returns the
// livecache size afterwards.
auto run = [&](bool verifyEndpoints, Endpoints eps) -> std::size_t {
TestStore store;
TestChecker checker;
TestStopwatch clock;
Logic<TestChecker> logic(clock, store, checker, journal_);
{
Config c;
c.autoConnect = false;
c.listeningPort = 1024;
c.ipLimit = 2;
c.verifyEndpoints = verifyEndpoints;
logic.config(c);
}
auto const remote = beast::IP::Endpoint::fromString("65.0.0.1:5");
auto const local = beast::IP::Endpoint::fromString("65.0.0.2:1024");
auto const [slot, r] = logic.newOutboundSlot(remote);
BEAST_EXPECT(slot != nullptr);
BEAST_EXPECT(r == Result::Success);
BEAST_EXPECT(logic.onConnected(slot, local));
PublicKey const pk(randomKeyPair(KeyType::Secp256k1).first);
BEAST_EXPECT(logic.activate(slot, pk, false) == Result::Success);
logic.onEndpoints(slot, std::move(eps));
auto const size = logic.livecache.size();
logic.onClosed(slot);
return size;
};
{
testcase("verify_endpoints enabled");
// Valid public addresses
Endpoints eps;
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.1:5"), 1);
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.2:6"), 1);
// Invalid: private address
eps.emplace_back(beast::IP::Endpoint::fromString("10.0.0.1:5"), 1);
// Invalid: port 0
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.3:0"), 1);
// With verification enabled, only the 2 valid endpoints survive
BEAST_EXPECT(run(true, eps) == 2);
}
{
testcase("verify_endpoints disabled");
Endpoints eps;
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.1:5"), 1);
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.2:6"), 1);
// Private address — kept when verification is off
eps.emplace_back(beast::IP::Endpoint::fromString("10.0.0.1:5"), 1);
// Port 0 — kept when verification is off
eps.emplace_back(beast::IP::Endpoint::fromString("44.0.0.3:0"), 1);
// Without verification, all 4 endpoints survive
BEAST_EXPECT(run(false, eps) == 4);
}
}
void
testOnConnectedSelfConnection()
{
@@ -524,6 +779,8 @@ public:
testActivateInboundDisabled();
testAddFixedPeerNoPort();
testOnConnectedSelfConnection();
testIsValidAddress();
testVerifyEndpoints();
}
};