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.cpp51
1 files changed, 49 insertions, 2 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 63e092e..7efd38b 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -258,10 +258,57 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port,
258 return (RvSocket)s; 258 return (RvSocket)s;
259} 259}
260 260
261bool RvServiceWaitPaired(RvSocket s) 261bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs)
262{ 262{
263 // A registration socket is idle on purpose -- we announce ourselves and
264 // wait, possibly for a long time, for someone to dial. ConnectTo() clears
265 // the socket timeout for exactly that reason, and with no timeout at all
266 // this recv() has no way to end when the path dies without either side
267 // saying so: a NAT drops the idle mapping, a relay restarts, a route
268 // changes. No FIN arrives, nothing is readable, and the call never
269 // returns. The thread stays alive and asleep, so ThreadBtfAccept never
270 // reaches the `continue` that would register again, and the node goes deaf
271 // while every outward sign says it is healthy.
272 //
273 // Seen in production on the bootstrap seed: 27 minutes without a block,
274 // eight behind the network, relay reachable throughout, fixed instantly by
275 // a restart.
276 //
277 // TCP keepalive is the obvious answer and it does not work here -- I
278 // measured it. With probes every 5s and a 2-probe limit, a socket whose
279 // replies were being dropped was still ESTAB after four minutes, the probe
280 // counter stuck at zero. So the timeout is ours to enforce, at the one
281 // place that knows what waiting means.
282 //
283 // Expiring is not a failure. It costs one reconnect per interval and
284 // returns the loop to a known state.
285 if (nTimeoutSecs > 0)
286 {
287#ifdef _WIN32
288 DWORD tv = (DWORD)nTimeoutSecs * 1000;
289 setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
290#else
291 struct timeval tv; tv.tv_sec = nTimeoutSecs; tv.tv_usec = 0;
292 setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
293#endif
294 }
295
263 unsigned char paired = 0; 296 unsigned char paired = 0;
264 return ReadN((SOCKET)s, &paired, 1) && paired == 0x01; 297 bool fOk = ReadN((SOCKET)s, &paired, 1) && paired == 0x01;
298
299 // Hand back a socket that blocks again: from here it is a data tunnel, and
300 // a tunnel legitimately sits quiet between messages.
301 if (fOk && nTimeoutSecs > 0)
302 {
303#ifdef _WIN32
304 DWORD tvOff = 0;
305 setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tvOff, sizeof(tvOff));
306#else
307 struct timeval tvOff; tvOff.tv_sec = 0; tvOff.tv_usec = 0;
308 setsockopt((SOCKET)s, SOL_SOCKET, SO_RCVTIMEO, &tvOff, sizeof(tvOff));
309#endif
310 }
311 return fOk;
265} 312}
266 313
267RvSocket RvClientConnect(const char* relay_host, unsigned short port, 314RvSocket RvClientConnect(const char* relay_host, unsigned short port,