From ebdaa1c68412eba114cf7639fa3256dbf3e52093 Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Mon, 28 Apr 2025 21:38:55 +0200 Subject: [PATCH] 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. --- src/xrpld/app/misc/detail/ValidatorSite.cpp | 4 +- src/xrpld/app/misc/detail/WorkBase.h | 59 ++++++++------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp index 799689ef2..42d4e9e27 100644 --- a/src/xrpld/app/misc/detail/ValidatorSite.cpp +++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp @@ -102,7 +102,7 @@ ValidatorSite::ValidatorSite( ValidatorSite::~ValidatorSite() { std::unique_lock lock{state_mutex_}; - if (timer_.expires_at() > clock_type::time_point{}) + if (timer_.expiry() > clock_type::time_point{}) { if (!stopping_) { @@ -168,7 +168,7 @@ ValidatorSite::start() { std::lock_guard l0{sites_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); } diff --git a/src/xrpld/app/misc/detail/WorkBase.h b/src/xrpld/app/misc/detail/WorkBase.h index a2b852edf..17f935126 100644 --- a/src/xrpld/app/misc/detail/WorkBase.h +++ b/src/xrpld/app/misc/detail/WorkBase.h @@ -96,6 +96,9 @@ public: void onResolve(error_code const& ec, results_type results); + void + onConnect(error_code const& ec, endpoint_type const& endpoint); + void onStart(); @@ -195,46 +198,26 @@ WorkBase::onResolve(error_code const& ec, results_type results) if (ec) return fail(ec); - // Use last endpoint if it is successfully connected - // and is in the list, otherwise pick a random endpoint - // from the list (excluding last endpoint). If there is - // 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_, + boost::asio::async_connect( + socket_, + results, strand_.wrap(std::bind( - &Impl::onConnect, + &WorkBase::onConnect, impl().shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1, + std::placeholders::_2))); +} + +template +void +WorkBase::onConnect(error_code const& ec, endpoint_type const& endpoint) +{ + lastEndpoint_ = endpoint; + + if (ec) + return fail(ec); + + impl().onConnect(ec); } template