From 3efd9a089cc6b924d9bc917d3d8145f7c01265ad Mon Sep 17 00:00:00 2001 From: Bitflash-sh Date: Fri, 24 Jul 2026 12:13:30 -0300 Subject: Multi-relay rendezvous with failover + relay rate-limiting Seed list of 3 geographically-spread relays (Sao Paulo, New Jersey, Almaty); nodes register at the first reachable one and fail over if it is attacked or goes down, so no single relay IP is a point of failure. Relay installer now applies iptables connection rate-limits to soak up single-source floods. --- src/headless.cpp | 2 +- src/net.cpp | 21 +++++++++++++++++---- src/nostr.cpp | 32 +++++++++++++++++++++++++++++--- src/nostr.h | 15 ++++++++++++--- src/ui.cpp | 6 +++--- 5 files changed, 62 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/headless.cpp b/src/headless.cpp index 94597f1..646476f 100644 --- a/src/headless.cpp +++ b/src/headless.cpp @@ -95,7 +95,7 @@ int main(int argc, char* argv[]) if (mapArgs.count("/proxy")) addrProxy = CAddress(mapArgs["/proxy"].c_str()); if (mapArgs.count("/rvrelay")) - strBtfMeetingRelay = mapArgs["/rvrelay"]; + vBtfMeetingRelays = { mapArgs["/rvrelay"] }; if (mapArgs.count("/connectbtf")) strBtfConnect = mapArgs["/connectbtf"]; if (mapArgs.count("/debug")) diff --git a/src/net.cpp b/src/net.cpp index 8ab9ccc..659de05 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -441,15 +441,24 @@ void ThreadBtfAccept(void* parg) return; Sleep(5000); } + size_t iRelay = 0; loop { if (fShutdown) return; - string strMeeting = strBtfMeetingRelay; + if (vBtfMeetingRelays.empty()) + { + Sleep(10000); // no meeting relay configured + continue; + } + // Stick to the current relay while it works; on failure, fail over to + // the next seed so a DDoS'd/blocked relay IP can't keep us offline. + string strMeeting = vBtfMeetingRelays[iRelay % vBtfMeetingRelays.size()]; size_t colon = strMeeting.rfind(':'); if (colon == string::npos) { - Sleep(10000); // no meeting relay configured + iRelay++; + Sleep(2000); continue; } string strHost = strMeeting.substr(0, colon); @@ -465,9 +474,13 @@ void ThreadBtfAccept(void* parg) } if (rv == btf::RV_INVALID) { - Sleep(10000); // relay unreachable; retry + iRelay++; // this relay is down/attacked -> try the next one + Sleep(3000); continue; } + // Registered OK: advertise THIS relay in our descriptor so clients dial + // us here, and keep using it (iRelay unchanged) until it fails. + BtfSetActiveRelay(strMeeting); btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); if (hSocket == INVALID_SOCKET) continue; @@ -1146,7 +1159,7 @@ bool StartNode(string& strError) // Anonymous inbound: this node's .btf hidden service, reachable through the // meeting relay without exposing our IP or needing a public port. - if (!strBtfMeetingRelay.empty()) + if (!vBtfMeetingRelays.empty()) if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) printf("Error: _beginthread(ThreadBtfAccept) failed\n"); diff --git a/src/nostr.cpp b/src/nostr.cpp index 8e2a07d..59817f5 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp @@ -55,7 +55,32 @@ const int nNostrRelays = ARRAYLEN(pszNostrRelays); // Rendezvous meeting relay this node registers at (ThreadBtfAccept in net.cpp) // and advertises in its .btf descriptor. Seed default; /rvrelay overrides. -string strBtfMeetingRelay = "90.156.222.107:8434"; +// Seed rendezvous relays. More entries = more resilience: the node fails over +// between them (see ThreadBtfAccept), so DDoSing one relay IP can't take the +// network down. /rvrelay overrides this list with a single entry. +vector vBtfMeetingRelays = { + "92.246.128.180:8434", // Sao Paulo, BR + "31.44.4.249:8434", // New Jersey, US + "90.156.222.107:8434", // Almaty, KZ +}; +string strBtfActiveRelay; +static CCriticalSection cs_activeRelay; + +string BtfActiveRelay() +{ + CRITICAL_BLOCK(cs_activeRelay) + if (!strBtfActiveRelay.empty()) + return strBtfActiveRelay; + if (!vBtfMeetingRelays.empty()) + return vBtfMeetingRelays[0]; + return ""; +} + +void BtfSetActiveRelay(const string& relay) +{ + CRITICAL_BLOCK(cs_activeRelay) + strBtfActiveRelay = relay; +} // @@ -519,8 +544,9 @@ static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) { // meeting_node is the rendezvous relay this node's hidden service // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. - string meeting_node = strBtfMeetingRelay.empty() ? "rendezvous-pending" - : strBtfMeetingRelay; + string meeting_node = BtfActiveRelay(); + if (meeting_node.empty()) + meeting_node = "rendezvous-pending"; string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), meeting_node, (uint64_t)GetTime()); if (desc.empty()) diff --git a/src/nostr.h b/src/nostr.h index 5ec9f65..41e59df 100644 --- a/src/nostr.h +++ b/src/nostr.h @@ -11,6 +11,7 @@ #define BITFLASH_NOSTR_H #include +#include // Thread entry point (signature compatible with _beginthread) void ThreadNostrSeed(void* parg); @@ -19,9 +20,17 @@ void ThreadNostrSeed(void* parg); extern const char* pszNostrRelays[]; extern const int nNostrRelays; -// Rendezvous meeting relay ("host:port") this node registers at and advertises -// in its .btf descriptor. Overridable with /rvrelay=host:port. -extern std::string strBtfMeetingRelay; +// Rendezvous meeting relays ("host:port") this node can register its .btf +// service at. It registers at the first reachable one and advertises that in +// its descriptor; if the relay dies it fails over to another, so attacking a +// single relay's IP can't take the network down. Overridable with /rvrelay. +extern std::vector vBtfMeetingRelays; +// The relay this node is currently registered at (set by ThreadBtfAccept). +extern std::string strBtfActiveRelay; +// The active relay, or the first seed if none active yet ("" if the list is empty). +std::string BtfActiveRelay(); +// Record the relay this node just registered its service at (thread-safe). +void BtfSetActiveRelay(const std::string& relay); // Copy this node's .btf identity (lazily loading or generating it): the x-only // pubkey (= the .btf address) and the x25519 secret for the end-to-end channel. diff --git a/src/ui.cpp b/src/ui.cpp index 3e734a5..cb1a94d 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -2969,10 +2969,10 @@ bool CMyApp::OnInit2() if (mapArgs.count("/proxy")) addrProxy = CAddress(mapArgs["/proxy"].c_str()); - // Rendezvous meeting relay this node registers its .btf service at and - // advertises in its descriptor (default is the seed relay; empty disables). + // Rendezvous meeting relay override: replace the seed list with a single + // relay (default keeps the built-in seed list with failover). if (mapArgs.count("/rvrelay")) - strBtfMeetingRelay = mapArgs["/rvrelay"]; + vBtfMeetingRelays = { mapArgs["/rvrelay"] }; // Keep an anonymous outbound connection to a specific .btf peer. if (mapArgs.count("/connectbtf")) -- cgit v1.2.3