diff options
| author | Bitflash-sh <btf01@proton.me> | 2026-07-24 12:13:30 -0300 |
|---|---|---|
| committer | Bitflash-sh <btf01@proton.me> | 2026-07-24 12:13:30 -0300 |
| commit | 3efd9a089cc6b924d9bc917d3d8145f7c01265ad (patch) | |
| tree | 5c4d1bc9a487e54fb0ec5b386dbd4360b6779f42 /src | |
| parent | 51d617e70f90a972185917ea0096c1ecef2bfa8b (diff) | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/headless.cpp | 2 | ||||
| -rw-r--r-- | src/net.cpp | 21 | ||||
| -rw-r--r-- | src/nostr.cpp | 32 | ||||
| -rw-r--r-- | src/nostr.h | 15 | ||||
| -rw-r--r-- | src/ui.cpp | 6 |
5 files changed, 62 insertions, 14 deletions
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[]) | |||
| 95 | if (mapArgs.count("/proxy")) | 95 | if (mapArgs.count("/proxy")) |
| 96 | addrProxy = CAddress(mapArgs["/proxy"].c_str()); | 96 | addrProxy = CAddress(mapArgs["/proxy"].c_str()); |
| 97 | if (mapArgs.count("/rvrelay")) | 97 | if (mapArgs.count("/rvrelay")) |
| 98 | strBtfMeetingRelay = mapArgs["/rvrelay"]; | 98 | vBtfMeetingRelays = { mapArgs["/rvrelay"] }; |
| 99 | if (mapArgs.count("/connectbtf")) | 99 | if (mapArgs.count("/connectbtf")) |
| 100 | strBtfConnect = mapArgs["/connectbtf"]; | 100 | strBtfConnect = mapArgs["/connectbtf"]; |
| 101 | if (mapArgs.count("/debug")) | 101 | 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) | |||
| 441 | return; | 441 | return; |
| 442 | Sleep(5000); | 442 | Sleep(5000); |
| 443 | } | 443 | } |
| 444 | size_t iRelay = 0; | ||
| 444 | loop | 445 | loop |
| 445 | { | 446 | { |
| 446 | if (fShutdown) | 447 | if (fShutdown) |
| 447 | return; | 448 | return; |
| 448 | string strMeeting = strBtfMeetingRelay; | 449 | if (vBtfMeetingRelays.empty()) |
| 450 | { | ||
| 451 | Sleep(10000); // no meeting relay configured | ||
| 452 | continue; | ||
| 453 | } | ||
| 454 | // Stick to the current relay while it works; on failure, fail over to | ||
| 455 | // the next seed so a DDoS'd/blocked relay IP can't keep us offline. | ||
| 456 | string strMeeting = vBtfMeetingRelays[iRelay % vBtfMeetingRelays.size()]; | ||
| 449 | size_t colon = strMeeting.rfind(':'); | 457 | size_t colon = strMeeting.rfind(':'); |
| 450 | if (colon == string::npos) | 458 | if (colon == string::npos) |
| 451 | { | 459 | { |
| 452 | Sleep(10000); // no meeting relay configured | 460 | iRelay++; |
| 461 | Sleep(2000); | ||
| 453 | continue; | 462 | continue; |
| 454 | } | 463 | } |
| 455 | string strHost = strMeeting.substr(0, colon); | 464 | string strHost = strMeeting.substr(0, colon); |
| @@ -465,9 +474,13 @@ void ThreadBtfAccept(void* parg) | |||
| 465 | } | 474 | } |
| 466 | if (rv == btf::RV_INVALID) | 475 | if (rv == btf::RV_INVALID) |
| 467 | { | 476 | { |
| 468 | Sleep(10000); // relay unreachable; retry | 477 | iRelay++; // this relay is down/attacked -> try the next one |
| 478 | Sleep(3000); | ||
| 469 | continue; | 479 | continue; |
| 470 | } | 480 | } |
| 481 | // Registered OK: advertise THIS relay in our descriptor so clients dial | ||
| 482 | // us here, and keep using it (iRelay unchanged) until it fails. | ||
| 483 | BtfSetActiveRelay(strMeeting); | ||
| 471 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); | 484 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); |
| 472 | if (hSocket == INVALID_SOCKET) | 485 | if (hSocket == INVALID_SOCKET) |
| 473 | continue; | 486 | continue; |
| @@ -1146,7 +1159,7 @@ bool StartNode(string& strError) | |||
| 1146 | 1159 | ||
| 1147 | // Anonymous inbound: this node's .btf hidden service, reachable through the | 1160 | // Anonymous inbound: this node's .btf hidden service, reachable through the |
| 1148 | // meeting relay without exposing our IP or needing a public port. | 1161 | // meeting relay without exposing our IP or needing a public port. |
| 1149 | if (!strBtfMeetingRelay.empty()) | 1162 | if (!vBtfMeetingRelays.empty()) |
| 1150 | if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) | 1163 | if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) |
| 1151 | printf("Error: _beginthread(ThreadBtfAccept) failed\n"); | 1164 | printf("Error: _beginthread(ThreadBtfAccept) failed\n"); |
| 1152 | 1165 | ||
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); | |||
| 55 | 55 | ||
| 56 | // Rendezvous meeting relay this node registers at (ThreadBtfAccept in net.cpp) | 56 | // Rendezvous meeting relay this node registers at (ThreadBtfAccept in net.cpp) |
| 57 | // and advertises in its .btf descriptor. Seed default; /rvrelay overrides. | 57 | // and advertises in its .btf descriptor. Seed default; /rvrelay overrides. |
| 58 | string strBtfMeetingRelay = "90.156.222.107:8434"; | 58 | // Seed rendezvous relays. More entries = more resilience: the node fails over |
| 59 | // between them (see ThreadBtfAccept), so DDoSing one relay IP can't take the | ||
| 60 | // network down. /rvrelay overrides this list with a single entry. | ||
| 61 | vector<string> vBtfMeetingRelays = { | ||
| 62 | "92.246.128.180:8434", // Sao Paulo, BR | ||
| 63 | "31.44.4.249:8434", // New Jersey, US | ||
| 64 | "90.156.222.107:8434", // Almaty, KZ | ||
| 65 | }; | ||
| 66 | string strBtfActiveRelay; | ||
| 67 | static CCriticalSection cs_activeRelay; | ||
| 68 | |||
| 69 | string BtfActiveRelay() | ||
| 70 | { | ||
| 71 | CRITICAL_BLOCK(cs_activeRelay) | ||
| 72 | if (!strBtfActiveRelay.empty()) | ||
| 73 | return strBtfActiveRelay; | ||
| 74 | if (!vBtfMeetingRelays.empty()) | ||
| 75 | return vBtfMeetingRelays[0]; | ||
| 76 | return ""; | ||
| 77 | } | ||
| 78 | |||
| 79 | void BtfSetActiveRelay(const string& relay) | ||
| 80 | { | ||
| 81 | CRITICAL_BLOCK(cs_activeRelay) | ||
| 82 | strBtfActiveRelay = relay; | ||
| 83 | } | ||
| 59 | 84 | ||
| 60 | 85 | ||
| 61 | // | 86 | // |
| @@ -519,8 +544,9 @@ static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) | |||
| 519 | { | 544 | { |
| 520 | // meeting_node is the rendezvous relay this node's hidden service | 545 | // meeting_node is the rendezvous relay this node's hidden service |
| 521 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. | 546 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. |
| 522 | string meeting_node = strBtfMeetingRelay.empty() ? "rendezvous-pending" | 547 | string meeting_node = BtfActiveRelay(); |
| 523 | : strBtfMeetingRelay; | 548 | if (meeting_node.empty()) |
| 549 | meeting_node = "rendezvous-pending"; | ||
| 524 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), | 550 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), |
| 525 | meeting_node, (uint64_t)GetTime()); | 551 | meeting_node, (uint64_t)GetTime()); |
| 526 | if (desc.empty()) | 552 | 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 @@ | |||
| 11 | #define BITFLASH_NOSTR_H | 11 | #define BITFLASH_NOSTR_H |
| 12 | 12 | ||
| 13 | #include <string> | 13 | #include <string> |
| 14 | #include <vector> | ||
| 14 | 15 | ||
| 15 | // Thread entry point (signature compatible with _beginthread) | 16 | // Thread entry point (signature compatible with _beginthread) |
| 16 | void ThreadNostrSeed(void* parg); | 17 | void ThreadNostrSeed(void* parg); |
| @@ -19,9 +20,17 @@ void ThreadNostrSeed(void* parg); | |||
| 19 | extern const char* pszNostrRelays[]; | 20 | extern const char* pszNostrRelays[]; |
| 20 | extern const int nNostrRelays; | 21 | extern const int nNostrRelays; |
| 21 | 22 | ||
| 22 | // Rendezvous meeting relay ("host:port") this node registers at and advertises | 23 | // Rendezvous meeting relays ("host:port") this node can register its .btf |
| 23 | // in its .btf descriptor. Overridable with /rvrelay=host:port. | 24 | // service at. It registers at the first reachable one and advertises that in |
| 24 | extern std::string strBtfMeetingRelay; | 25 | // its descriptor; if the relay dies it fails over to another, so attacking a |
| 26 | // single relay's IP can't take the network down. Overridable with /rvrelay. | ||
| 27 | extern std::vector<std::string> vBtfMeetingRelays; | ||
| 28 | // The relay this node is currently registered at (set by ThreadBtfAccept). | ||
| 29 | extern std::string strBtfActiveRelay; | ||
| 30 | // The active relay, or the first seed if none active yet ("" if the list is empty). | ||
| 31 | std::string BtfActiveRelay(); | ||
| 32 | // Record the relay this node just registered its service at (thread-safe). | ||
| 33 | void BtfSetActiveRelay(const std::string& relay); | ||
| 25 | 34 | ||
| 26 | // Copy this node's .btf identity (lazily loading or generating it): the x-only | 35 | // Copy this node's .btf identity (lazily loading or generating it): the x-only |
| 27 | // pubkey (= the .btf address) and the x25519 secret for the end-to-end channel. | 36 | // pubkey (= the .btf address) and the x25519 secret for the end-to-end channel. |
| @@ -2969,10 +2969,10 @@ bool CMyApp::OnInit2() | |||
| 2969 | if (mapArgs.count("/proxy")) | 2969 | if (mapArgs.count("/proxy")) |
| 2970 | addrProxy = CAddress(mapArgs["/proxy"].c_str()); | 2970 | addrProxy = CAddress(mapArgs["/proxy"].c_str()); |
| 2971 | 2971 | ||
| 2972 | // Rendezvous meeting relay this node registers its .btf service at and | 2972 | // Rendezvous meeting relay override: replace the seed list with a single |
| 2973 | // advertises in its descriptor (default is the seed relay; empty disables). | 2973 | // relay (default keeps the built-in seed list with failover). |
| 2974 | if (mapArgs.count("/rvrelay")) | 2974 | if (mapArgs.count("/rvrelay")) |
| 2975 | strBtfMeetingRelay = mapArgs["/rvrelay"]; | 2975 | vBtfMeetingRelays = { mapArgs["/rvrelay"] }; |
| 2976 | 2976 | ||
| 2977 | // Keep an anonymous outbound connection to a specific .btf peer. | 2977 | // Keep an anonymous outbound connection to a specific .btf peer. |
| 2978 | if (mapArgs.count("/connectbtf")) | 2978 | if (mapArgs.count("/connectbtf")) |