diff options
| author | Bitflash <btf01@proton.me> | 2026-07-29 13:28:50 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-29 13:28:50 -0300 |
| commit | 997e511e120272e8f2ddd29716b486e597410f02 (patch) | |
| tree | 42c816df51d837bcac9f806864d677c2d9a9de0c /src | |
| parent | 6e836ed5cb5f41391e335b4f508dc6c0e8915ba5 (diff) | |
rendezvous: register and wait for a dial as separate steps (#30)
A node could not become reachable. RvServiceRegister connected, sent its
'S'+pubkey header, and then blocked reading the relay's 0x01 byte -- which the
relay only writes when a client pairs with the service. So the call did not
return when the node was registered; it returned when somebody had already
dialled it.
ThreadBtfAccept called BtfSetActiveRelay after that. The result is a deadlock:
- to be dialled, a node must publish a descriptor naming its meeting node
- to publish, BtfActiveRelay() must be set
- it is only set once somebody has dialled
Before #26 this was masked. BtfActiveRelay() fell back to
vBtfMeetingRelays[0], so a node always published something, and ThreadBtfAccept
picks its relay at random -- so it worked whenever the two happened to match,
roughly one time in the number of relays. That is the flakiness this project has
been chasing: not a bad relay, an advertisement that usually named the wrong one.
#26 removed the fallback, which was right on its own terms and turned an
unreliable heuristic into a reliable deadlock. This is the other half of that
change and they belong together.
RvServiceRegister now returns as soon as the relay has the service listed.
RvServiceWaitPaired does the blocking read. ThreadBtfAccept registers, advertises,
then waits. RvClientConnect is untouched -- a dialer blocking until it is paired
is correct.
Registration failure also had no log line at all, which is why this stayed
invisible: a node that never registered looked identical to one waiting quietly
for a peer. There are now lines for registering, failing over, and being dropped.
Verified: a fresh node reports `registered at 90.156.222.107:8434, waiting for a
dial` and then self-resolves with `meeting_node=90.156.222.107:8434` -- the relay
it is actually on. Two nodes on one machine reach each other.
Diffstat (limited to 'src')
| -rw-r--r-- | src/btfrv.cpp | 13 | ||||
| -rw-r--r-- | src/btfrv.h | 13 | ||||
| -rw-r--r-- | src/net.cpp | 25 |
3 files changed, 44 insertions, 7 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp index 0d02ca8..63e092e 100644 --- a/src/btfrv.cpp +++ b/src/btfrv.cpp | |||
| @@ -250,11 +250,20 @@ RvSocket RvServiceRegister(const char* relay_host, unsigned short port, | |||
| 250 | hdr[0] = (unsigned char)'S'; | 250 | hdr[0] = (unsigned char)'S'; |
| 251 | memcpy(hdr + 1, my_pubkey, 32); | 251 | memcpy(hdr + 1, my_pubkey, 32); |
| 252 | if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } | 252 | if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } |
| 253 | unsigned char paired = 0; | 253 | // Return here: registered, not yet paired. The relay lists the service as |
| 254 | if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } | 254 | // soon as it reads this header. The 0x01 that used to be consumed below |
| 255 | // only arrives when somebody dials us, so blocking on it made "registered" | ||
| 256 | // and "already in use" indistinguishable to the caller -- and left the node | ||
| 257 | // unable to advertise a meeting node until after it had been reached there. | ||
| 255 | return (RvSocket)s; | 258 | return (RvSocket)s; |
| 256 | } | 259 | } |
| 257 | 260 | ||
| 261 | bool RvServiceWaitPaired(RvSocket s) | ||
| 262 | { | ||
| 263 | unsigned char paired = 0; | ||
| 264 | return ReadN((SOCKET)s, &paired, 1) && paired == 0x01; | ||
| 265 | } | ||
| 266 | |||
| 258 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, | 267 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, |
| 259 | const unsigned char target_pubkey[32]) | 268 | const unsigned char target_pubkey[32]) |
| 260 | { | 269 | { |
diff --git a/src/btfrv.h b/src/btfrv.h index c776201..9d6756e 100644 --- a/src/btfrv.h +++ b/src/btfrv.h | |||
| @@ -37,11 +37,20 @@ bool RvRelayRun(unsigned short port); | |||
| 37 | void RvRelayStop(); | 37 | void RvRelayStop(); |
| 38 | 38 | ||
| 39 | // ---- service side (the node being reached by its .btf address) ---- | 39 | // ---- service side (the node being reached by its .btf address) ---- |
| 40 | // Register at the relay under `my_pubkey` (32 bytes) and block until a client is | 40 | // Register at the relay under `my_pubkey` (32 bytes). Returns as soon as the |
| 41 | // paired to us. Returns the paired socket, or RV_INVALID on error. | 41 | // relay has us listed, WITHOUT waiting for anyone to dial -- the caller needs |
| 42 | // that moment to start advertising this relay as its meeting node. Waiting for | ||
| 43 | // the dial was folded into this call before, which deadlocked discovery: a node | ||
| 44 | // could not be advertised until it had been dialled, and could not be dialled | ||
| 45 | // until it was advertised. Returns RV_INVALID on error. | ||
| 42 | RvSocket RvServiceRegister(const char* relay_host, unsigned short port, | 46 | RvSocket RvServiceRegister(const char* relay_host, unsigned short port, |
| 43 | const unsigned char my_pubkey[32]); | 47 | const unsigned char my_pubkey[32]); |
| 44 | 48 | ||
| 49 | // Block on a registered service socket until a client is paired to us. Returns | ||
| 50 | // false if the relay drops us or errors, in which case the caller should close | ||
| 51 | // the socket and register again. | ||
| 52 | bool RvServiceWaitPaired(RvSocket s); | ||
| 53 | |||
| 45 | // ---- client side (the node dialing a .btf address) ---- | 54 | // ---- client side (the node dialing a .btf address) ---- |
| 46 | // Connect through the relay to the service registered under `target_pubkey`. | 55 | // Connect through the relay to the service registered under `target_pubkey`. |
| 47 | // Returns the paired socket, or RV_INVALID on error. | 56 | // Returns the paired socket, or RV_INVALID on error. |
diff --git a/src/net.cpp b/src/net.cpp index 7b2ef83..b5d12d0 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -554,7 +554,10 @@ void ThreadBtfAccept(void* parg) | |||
| 554 | string strHost = strMeeting.substr(0, colon); | 554 | string strHost = strMeeting.substr(0, colon); |
| 555 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); | 555 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); |
| 556 | 556 | ||
| 557 | // Blocks until a client is paired to us (or the relay drops us) | 557 | // Returns as soon as the relay has us listed -- it does not wait for a |
| 558 | // dial. That distinction is the whole point: we have to be advertised | ||
| 559 | // before anyone can dial us, so registering and waiting cannot be the | ||
| 560 | // same call. | ||
| 558 | btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk); | 561 | btf::RvSocket rv = btf::RvServiceRegister(strHost.c_str(), (unsigned short)nPort, pk); |
| 559 | if (fShutdown) | 562 | if (fShutdown) |
| 560 | { | 563 | { |
| @@ -564,13 +567,29 @@ void ThreadBtfAccept(void* parg) | |||
| 564 | } | 567 | } |
| 565 | if (rv == btf::RV_INVALID) | 568 | if (rv == btf::RV_INVALID) |
| 566 | { | 569 | { |
| 570 | LogPrint("net", "rendezvous: could not register at %s, trying another\n", | ||
| 571 | strMeeting.c_str()); | ||
| 567 | iRelay++; // this relay is down/attacked -> try the next one | 572 | iRelay++; // this relay is down/attacked -> try the next one |
| 568 | Sleep(3000); | 573 | Sleep(3000); |
| 569 | continue; | 574 | continue; |
| 570 | } | 575 | } |
| 571 | // Registered OK: advertise THIS relay in our descriptor so clients dial | 576 | |
| 572 | // us here, and keep using it (iRelay unchanged) until it fails. | 577 | // Registered. Advertise THIS relay now, while we are listed and before |
| 578 | // anybody dials -- a descriptor naming it is what makes a dial possible | ||
| 579 | // at all. Keep using it (iRelay unchanged) until it fails. | ||
| 573 | BtfSetActiveRelay(strMeeting); | 580 | BtfSetActiveRelay(strMeeting); |
| 581 | LogPrint("net", "rendezvous: registered at %s, waiting for a dial\n", | ||
| 582 | strMeeting.c_str()); | ||
| 583 | |||
| 584 | // Now block for someone to arrive. | ||
| 585 | if (!btf::RvServiceWaitPaired(rv)) | ||
| 586 | { | ||
| 587 | btf::RvClose(rv); | ||
| 588 | LogPrint("net", "rendezvous: %s dropped us before any dial, re-registering\n", | ||
| 589 | strMeeting.c_str()); | ||
| 590 | continue; | ||
| 591 | } | ||
| 592 | |||
| 574 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); | 593 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); |
| 575 | if (hSocket == INVALID_SOCKET) | 594 | if (hSocket == INVALID_SOCKET) |
| 576 | continue; | 595 | continue; |