upleb.uk

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

summaryrefslogtreecommitdiff
path: root/relay/btfrv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'relay/btfrv.cpp')
-rw-r--r--relay/btfrv.cpp97
1 files changed, 73 insertions, 24 deletions
diff --git a/relay/btfrv.cpp b/relay/btfrv.cpp
index b1eee55..833c47c 100644
--- a/relay/btfrv.cpp
+++ b/relay/btfrv.cpp
@@ -31,6 +31,7 @@ typedef int SOCKET;
31#include <cstring> 31#include <cstring>
32#include <cstdio> 32#include <cstdio>
33#include <string> 33#include <string>
34#include <deque>
34#include <map> 35#include <map>
35#include <thread> 36#include <thread>
36#include <mutex> 37#include <mutex>
@@ -44,7 +45,24 @@ static std::atomic<bool> g_stop(false);
44static SOCKET g_listen = INVALID_SOCKET; 45static SOCKET g_listen = INVALID_SOCKET;
45 46
46// Services that have registered and are waiting for a client, keyed by pubkey. 47// Services that have registered and are waiting for a client, keyed by pubkey.
47static std::map<std::string, SOCKET> g_waiting; 48// More than one registration per node, on purpose.
49//
50// This used to be one socket per pubkey, and a dial *consumed* it: the first
51// caller took the registration and everyone else was told "not found" until the
52// node noticed and registered again. A node re-registers only after its pairing
53// has been handed off, so between the two there is a window where it is
54// unreachable to the whole network. Measured from a node's own counters: half
55// of all outbound dials failed that way -- 15 of 30 in eleven minutes -- while
56// descriptors resolved 53 times out of 54 and the node's own registrations
57// paired 11 times out of 12. The rendezvous was not losing anybody; it was
58// letting one caller in at a time.
59//
60// A queue per key lets a node keep spare registrations parked, so a dial that
61// arrives while another is being paired finds the next one instead of a closed
62// door. Old nodes register exactly as before and simply keep one entry: the
63// wire protocol does not change, so a relay running this serves both.
64static const size_t MAX_WAITING_PER_KEY = 4;
65static std::map<std::string, std::deque<SOCKET> > g_waiting;
48static std::mutex g_mtx; 66static std::mutex g_mtx;
49 67
50// How often the reaper walks the waiting map. 68// How often the reaper walks the waiting map.
@@ -150,16 +168,24 @@ static void ReapDeadRegistrations()
150 int nBefore = 0, nReaped = 0; 168 int nBefore = 0, nReaped = 0;
151 { 169 {
152 std::lock_guard<std::mutex> lk(g_mtx); 170 std::lock_guard<std::mutex> lk(g_mtx);
153 nBefore = (int)g_waiting.size(); 171 std::map<std::string, std::deque<SOCKET> >::iterator it = g_waiting.begin();
154 std::map<std::string, SOCKET>::iterator it = g_waiting.begin();
155 while (it != g_waiting.end()) 172 while (it != g_waiting.end())
156 { 173 {
157 if (RegistrationDead(it->second)) 174 std::deque<SOCKET>& q = it->second;
175 nBefore += (int)q.size();
176 for (std::deque<SOCKET>::iterator jt = q.begin(); jt != q.end();)
158 { 177 {
159 CLOSESOCK(it->second); 178 if (RegistrationDead(*jt))
160 g_waiting.erase(it++); 179 {
161 nReaped++; 180 CLOSESOCK(*jt);
181 jt = q.erase(jt);
182 nReaped++;
183 }
184 else
185 ++jt;
162 } 186 }
187 if (q.empty())
188 g_waiting.erase(it++);
163 else 189 else
164 ++it; 190 ++it;
165 } 191 }
@@ -209,34 +235,57 @@ static void HandleIncoming(SOCKET s)
209 { 235 {
210 // Register and wait; a future client will pair us. 236 // Register and wait; a future client will pair us.
211 std::lock_guard<std::mutex> lk(g_mtx); 237 std::lock_guard<std::mutex> lk(g_mtx);
212 // If a stale service for this key exists, drop it. 238 std::deque<SOCKET>& q = g_waiting[pubkey];
213 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey); 239
214 if (it != g_waiting.end()) CLOSESOCK(it->second); 240 // Drop registrations this node has already lost the far end of, rather
215 g_waiting[pubkey] = s; 241 // than dropping the previous one on principle. The old code closed the
216 // leave the socket open in the map; do not close here. 242 // prior registration on every new one, which is why a node could never
243 // hold a spare.
244 for (std::deque<SOCKET>::iterator it = q.begin(); it != q.end();)
245 {
246 if (RegistrationDead(*it)) { CLOSESOCK(*it); it = q.erase(it); }
247 else ++it;
248 }
249
250 // A ceiling, because a stranger can open these. Oldest goes first: it
251 // is the one most likely to be stale.
252 while (q.size() >= MAX_WAITING_PER_KEY)
253 {
254 CLOSESOCK(q.front());
255 q.pop_front();
256 }
257 q.push_back(s);
258 // leave the socket open in the queue; do not close here.
217 } 259 }
218 else if (role == 'C') 260 else if (role == 'C')
219 { 261 {
220 SOCKET svc = INVALID_SOCKET; 262 SOCKET svc = INVALID_SOCKET;
221 { 263 {
222 std::lock_guard<std::mutex> lk(g_mtx); 264 std::lock_guard<std::mutex> lk(g_mtx);
223 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey); 265 std::map<std::string, std::deque<SOCKET> >::iterator it = g_waiting.find(pubkey);
224 if (it != g_waiting.end()) 266 if (it != g_waiting.end())
225 { 267 {
226 // Check before handing it over. Pairing a client with a dead 268 std::deque<SOCKET>& q = it->second;
227 // registration produces a tunnel that fails after both sides 269 // Take the oldest one that is still alive, discarding the dead
228 // think they succeeded, which is the failure the dialling node 270 // as we go. Checking before handing it over matters: pairing a
229 // reports as "tunnel to X failed" with nothing to explain it. 271 // client with a dead registration produces a tunnel that fails
230 if (RegistrationDead(it->second)) 272 // after both sides think they succeeded, which is the failure
273 // the dialling node reports as "tunnel to X failed" with
274 // nothing to explain it.
275 while (!q.empty())
231 { 276 {
232 CLOSESOCK(it->second); 277 SOCKET candidate = q.front();
233 g_waiting.erase(it); 278 q.pop_front();
279 if (RegistrationDead(candidate))
280 {
281 CLOSESOCK(candidate);
282 continue;
283 }
284 svc = candidate;
285 break;
234 } 286 }
235 else 287 if (q.empty())
236 {
237 svc = it->second;
238 g_waiting.erase(it); 288 g_waiting.erase(it);
239 }
240 } 289 }
241 } 290 }
242 if (svc == INVALID_SOCKET) 291 if (svc == INVALID_SOCKET)