diff options
Diffstat (limited to 'relay/btfrv.cpp')
| -rw-r--r-- | relay/btfrv.cpp | 97 |
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); | |||
| 44 | static SOCKET g_listen = INVALID_SOCKET; | 45 | static 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. |
| 47 | static 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. | ||
| 64 | static const size_t MAX_WAITING_PER_KEY = 4; | ||
| 65 | static std::map<std::string, std::deque<SOCKET> > g_waiting; | ||
| 48 | static std::mutex g_mtx; | 66 | static 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) |