upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/btfrv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/btfrv.cpp')
-rw-r--r--src/btfrv.cpp74
1 files changed, 69 insertions, 5 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 905717f..7d705ef 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -5,6 +5,9 @@
5// Windows/Winsock implementation (Layer 6 will #ifdef this for Linux/BSD sockets). 5// Windows/Winsock implementation (Layer 6 will #ifdef this for Linux/BSD sockets).
6 6
7#include "btfrv.h" 7#include "btfrv.h"
8#ifdef BITFLASH_NODE_BUILD
9#include "proxy.h"
10#endif
8 11
9// Portable sockets: Winsock on Windows, BSD sockets on Linux/POSIX (for the 12// Portable sockets: Winsock on Windows, BSD sockets on Linux/POSIX (for the
10// headless relay `bitflashd` that runs on the VPS). 13// headless relay `bitflashd` that runs on the VPS).
@@ -37,6 +40,8 @@ typedef int SOCKET;
37#include <thread> 40#include <thread>
38#include <mutex> 41#include <mutex>
39#include <atomic> 42#include <atomic>
43#include <chrono>
44#include <system_error>
40 45
41namespace btf 46namespace btf
42{ 47{
@@ -92,6 +97,11 @@ static bool WriteN(SOCKET s, const void* buf, int n)
92 97
93static SOCKET ConnectTo(const char* host, unsigned short port) 98static SOCKET ConnectTo(const char* host, unsigned short port)
94{ 99{
100#ifdef BITFLASH_NODE_BUILD
101 SOCKET s = BtfConnectSocket(host, port, SOCK_RV_DIAL, 10);
102 if (s == INVALID_SOCKET)
103 return INVALID_SOCKET;
104#else
95 struct addrinfo hints, *res = NULL; 105 struct addrinfo hints, *res = NULL;
96 memset(&hints, 0, sizeof(hints)); 106 memset(&hints, 0, sizeof(hints));
97 hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 (some relays are v6-only) 107 hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 (some relays are v6-only)
@@ -130,6 +140,7 @@ static SOCKET ConnectTo(const char* host, unsigned short port)
130 } 140 }
131 freeaddrinfo(res); 141 freeaddrinfo(res);
132 if (s == INVALID_SOCKET) return INVALID_SOCKET; 142 if (s == INVALID_SOCKET) return INVALID_SOCKET;
143#endif
133 144
134 // Clear the timeout -- the socket is now a live tunnel and must not 145 // Clear the timeout -- the socket is now a live tunnel and must not
135 // time out when idle. recv() will block until data arrives or peer closes. 146 // time out when idle. recv() will block until data arrives or peer closes.
@@ -200,8 +211,28 @@ static void HandleIncoming(SOCKET s)
200 bool a = WriteN(svc, &ok, 1); 211 bool a = WriteN(svc, &ok, 1);
201 bool b = WriteN(s, &ok, 1); 212 bool b = WriteN(s, &ok, 1);
202 if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; } 213 if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; }
203 std::thread(Forward, s, svc).detach(); 214 // One thread pumps s -> svc; this thread pumps svc -> s. Spawning a
204 std::thread(Forward, svc, s).detach(); 215 // thread per direction left no clean way to fail the second spawn:
216 // std::thread throws std::system_error when pthread_create is refused
217 // (NPROC limit, container pids cgroup, memory pressure), the first
218 // pump was already detached, and closing its sockets from here would
219 // race it -- so nothing caught the throw and it reached
220 // std::terminate, killing the relay over one pair it could not serve.
221 // Running one direction on this thread leaves exactly one spawn to
222 // guard, and a failure here has nothing running yet to race.
223 try
224 {
225 std::thread(Forward, s, svc).detach();
226 }
227 catch (const std::system_error& e)
228 {
229 fprintf(stderr, "rendezvous relay: cannot spawn forward thread (%s), pair dropped\n",
230 e.what());
231 CLOSESOCK(svc);
232 CLOSESOCK(s);
233 return;
234 }
235 Forward(svc, s);
205 } 236 }
206 else 237 else
207 { 238 {
@@ -227,6 +258,7 @@ bool RvRelayRun(unsigned short port)
227 if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; } 258 if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; }
228 if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; } 259 if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; }
229 260
261 int nSpawnFails = 0;
230 while (!g_stop) 262 while (!g_stop)
231 { 263 {
232 struct sockaddr_in cli; 264 struct sockaddr_in cli;
@@ -237,7 +269,27 @@ bool RvRelayRun(unsigned short port)
237 if (g_stop) break; 269 if (g_stop) break;
238 continue; 270 continue;
239 } 271 }
240 std::thread(HandleIncoming, s).detach(); 272 try
273 {
274 std::thread(HandleIncoming, s).detach();
275 nSpawnFails = 0;
276 }
277 catch (const std::system_error& e)
278 {
279 // A refused pthread_create is backpressure, not a reason to die:
280 // the condition behind it (NPROC, pids cgroup, memory) does not
281 // clear in milliseconds, and the connection count feeding it is
282 // attacker-influenced. Drop this one peer, stop accepting for a
283 // moment -- longer each time, capped -- and keep serving the
284 // connections already established.
285 fprintf(stderr, "rendezvous relay: cannot spawn connection thread (%s), connection dropped\n",
286 e.what());
287 CLOSESOCK(s);
288 nSpawnFails++;
289 int nWaitMs = nSpawnFails < 5 ? nSpawnFails * 1000 : 5000;
290 for (int i = 0; i < nWaitMs && !g_stop; i += 100)
291 std::this_thread::sleep_for(std::chrono::milliseconds(100));
292 }
241 } 293 }
242 CLOSESOCK(g_listen); 294 CLOSESOCK(g_listen);
243 g_listen = INVALID_SOCKET; 295 g_listen = INVALID_SOCKET;
@@ -325,17 +377,29 @@ bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs)
325} 377}
326 378
327RvSocket RvClientConnect(const char* relay_host, unsigned short port, 379RvSocket RvClientConnect(const char* relay_host, unsigned short port,
328 const unsigned char target_pubkey[32]) 380 const unsigned char target_pubkey[32],
381 RvClientFail* pFail)
329{ 382{
383 if (pFail) *pFail = RVC_FAIL_DIAL;
330 if (!RvInit()) return RV_INVALID; 384 if (!RvInit()) return RV_INVALID;
331 SOCKET s = ConnectTo(relay_host, port); 385 SOCKET s = ConnectTo(relay_host, port);
332 if (s == INVALID_SOCKET) return RV_INVALID; 386 if (s == INVALID_SOCKET) return RV_INVALID;
387 if (pFail) *pFail = RVC_FAIL_PAIR;
333 unsigned char hdr[33]; 388 unsigned char hdr[33];
334 hdr[0] = (unsigned char)'C'; 389 hdr[0] = (unsigned char)'C';
335 memcpy(hdr + 1, target_pubkey, 32); 390 memcpy(hdr + 1, target_pubkey, 32);
336 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } 391 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
337 unsigned char paired = 0; 392 unsigned char paired = 0;
338 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } 393 if (!ReadN(s, &paired, 1)) { CLOSESOCK(s); return RV_INVALID; }
394 if (paired != 0x01)
395 {
396 // The relay answered and declined: HandleIncoming writes 0x00 and
397 // closes when no service is parked under this pubkey right now.
398 if (pFail) *pFail = RVC_FAIL_REFUSED;
399 CLOSESOCK(s);
400 return RV_INVALID;
401 }
402 if (pFail) *pFail = RVC_FAIL_NONE;
339 return (RvSocket)s; 403 return (RvSocket)s;
340} 404}
341 405