fix: Replaces random endpoint resolution with sequential (#5365)

This change addresses an issue where `rippled` attempts to connect to an IPv6 address, even when the local network lacks IPv6 support, resulting in a "Network is unreachable" error.

The fix replaces the custom endpoint selection logic with `boost::async_connect`, which sequentially attempts to connect to available endpoints until one succeeds or all fail.
This commit is contained in:
Vito Tumas
2025-04-28 21:38:55 +02:00
committed by GitHub
parent fa1e25abef
commit 3502df2174
2 changed files with 23 additions and 40 deletions

View File

@@ -102,7 +102,7 @@ ValidatorSite::ValidatorSite(
ValidatorSite::~ValidatorSite() ValidatorSite::~ValidatorSite()
{ {
std::unique_lock<std::mutex> lock{state_mutex_}; std::unique_lock<std::mutex> lock{state_mutex_};
if (timer_.expires_at() > clock_type::time_point{}) if (timer_.expiry() > clock_type::time_point{})
{ {
if (!stopping_) if (!stopping_)
{ {
@@ -168,7 +168,7 @@ ValidatorSite::start()
{ {
std::lock_guard l0{sites_mutex_}; std::lock_guard l0{sites_mutex_};
std::lock_guard l1{state_mutex_}; std::lock_guard l1{state_mutex_};
if (timer_.expires_at() == clock_type::time_point{}) if (timer_.expiry() == clock_type::time_point{})
setTimer(l0, l1); setTimer(l0, l1);
} }

View File

@@ -96,6 +96,9 @@ public:
void void
onResolve(error_code const& ec, results_type results); onResolve(error_code const& ec, results_type results);
void
onConnect(error_code const& ec, endpoint_type const& endpoint);
void void
onStart(); onStart();
@@ -195,46 +198,26 @@ WorkBase<Impl>::onResolve(error_code const& ec, results_type results)
if (ec) if (ec)
return fail(ec); return fail(ec);
// Use last endpoint if it is successfully connected boost::asio::async_connect(
// and is in the list, otherwise pick a random endpoint socket_,
// from the list (excluding last endpoint). If there is results,
// only one endpoint and it is the last endpoint then
// use the last endpoint.
lastEndpoint_ = [&]() -> endpoint_type {
int foundIndex = 0;
auto const foundIt = std::find_if(
results.begin(), results.end(), [&](endpoint_type const& e) {
if (e == lastEndpoint_)
return true;
foundIndex++;
return false;
});
if (foundIt != results.end() && lastStatus_)
return lastEndpoint_;
else if (results.size() == 1)
return *results.begin();
else if (foundIt == results.end())
return *std::next(results.begin(), rand_int(results.size() - 1));
// lastEndpoint_ is part of the collection
// Pick a random number from the n-1 valid choices, if we use
// this as an index, note the last element will never be chosen
// and the `lastEndpoint_` index may be chosen. So when the
// `lastEndpoint_` index is chosen, that is treated as if the
// last element was chosen.
auto randIndex =
(results.size() > 2) ? rand_int(results.size() - 2) : 0;
if (randIndex == foundIndex)
randIndex = results.size() - 1;
return *std::next(results.begin(), randIndex);
}();
socket_.async_connect(
lastEndpoint_,
strand_.wrap(std::bind( strand_.wrap(std::bind(
&Impl::onConnect, &WorkBase::onConnect,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1,
std::placeholders::_2)));
}
template <class Impl>
void
WorkBase<Impl>::onConnect(error_code const& ec, endpoint_type const& endpoint)
{
lastEndpoint_ = endpoint;
if (ec)
return fail(ec);
impl().onConnect(ec);
} }
template <class Impl> template <class Impl>