diff options
| -rw-r--r-- | relay/btfrv.cpp | 97 | ||||
| -rw-r--r-- | src/net.cpp | 194 | ||||
| -rw-r--r-- | src/net.h | 22 | ||||
| -rw-r--r-- | src/nostr.cpp | 4 |
4 files changed, 287 insertions, 30 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) |
diff --git a/src/net.cpp b/src/net.cpp index 7091981..449d91c 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -51,6 +51,103 @@ int64 nBlocksReceived = 0; | |||
| 51 | int64 nBlocksWithoutParent = 0; | 51 | int64 nBlocksWithoutParent = 0; |
| 52 | static int64 nNodeStartTime = 0; | 52 | static int64 nNodeStartTime = 0; |
| 53 | 53 | ||
| 54 | static CCriticalSection cs_btfChurn; | ||
| 55 | static int64 nBtfResolveAttempts = 0; | ||
| 56 | static int64 nBtfResolveOk = 0; | ||
| 57 | static int64 nBtfResolveMiss = 0; | ||
| 58 | static int64 nBtfDialAttempts = 0; | ||
| 59 | static int64 nBtfDialOk = 0; | ||
| 60 | static int64 nBtfDialFailed = 0; | ||
| 61 | static int64 nBtfRegisterOk = 0; | ||
| 62 | static int64 nBtfRegisterFailed = 0; | ||
| 63 | static int64 nBtfPairOk = 0; | ||
| 64 | static int64 nBtfPairFailed = 0; | ||
| 65 | static int64 nBtfHandshakeNoRecv = 0; | ||
| 66 | static int64 nBtfHandshakeNoSend = 0; | ||
| 67 | static int64 nBtfHandshakeSilent = 0; | ||
| 68 | static string strBtfLastDialFail; | ||
| 69 | static string strBtfLastHandshakeTimeout; | ||
| 70 | |||
| 71 | void BtfChurnNoteResolveAttempt() | ||
| 72 | { | ||
| 73 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 74 | nBtfResolveAttempts++; | ||
| 75 | } | ||
| 76 | |||
| 77 | void BtfChurnNoteResolveResult(bool fOk) | ||
| 78 | { | ||
| 79 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 80 | { | ||
| 81 | if (fOk) nBtfResolveOk++; | ||
| 82 | else nBtfResolveMiss++; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting) | ||
| 87 | { | ||
| 88 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 89 | nBtfDialAttempts++; | ||
| 90 | LogPrint("net", "btfchurn: dial attempt addr=%s meeting=%s\n", | ||
| 91 | strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 92 | } | ||
| 93 | |||
| 94 | void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk) | ||
| 95 | { | ||
| 96 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 97 | { | ||
| 98 | if (fOk) | ||
| 99 | nBtfDialOk++; | ||
| 100 | else | ||
| 101 | { | ||
| 102 | nBtfDialFailed++; | ||
| 103 | strBtfLastDialFail = strBtfAddr + " via " + strMeeting; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | LogPrint("net", "btfchurn: dial %s addr=%s meeting=%s\n", | ||
| 107 | fOk ? "ok" : "failed", strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 108 | } | ||
| 109 | |||
| 110 | void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk) | ||
| 111 | { | ||
| 112 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 113 | { | ||
| 114 | if (fOk) nBtfRegisterOk++; | ||
| 115 | else nBtfRegisterFailed++; | ||
| 116 | } | ||
| 117 | LogPrint("net", "btfchurn: register %s meeting=%s\n", | ||
| 118 | fOk ? "ok" : "failed", strMeeting.c_str()); | ||
| 119 | } | ||
| 120 | |||
| 121 | void BtfChurnNotePairResult(const string& strMeeting, bool fOk) | ||
| 122 | { | ||
| 123 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 124 | { | ||
| 125 | if (fOk) nBtfPairOk++; | ||
| 126 | else nBtfPairFailed++; | ||
| 127 | } | ||
| 128 | LogPrint("net", "btfchurn: pair %s meeting=%s\n", | ||
| 129 | fOk ? "ok" : "timeout-or-drop", strMeeting.c_str()); | ||
| 130 | } | ||
| 131 | |||
| 132 | void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting, | ||
| 133 | bool fRecv, bool fSend) | ||
| 134 | { | ||
| 135 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 136 | { | ||
| 137 | if (!fRecv && !fSend) | ||
| 138 | nBtfHandshakeSilent++; | ||
| 139 | else | ||
| 140 | { | ||
| 141 | if (!fRecv) nBtfHandshakeNoRecv++; | ||
| 142 | if (!fSend) nBtfHandshakeNoSend++; | ||
| 143 | } | ||
| 144 | strBtfLastHandshakeTimeout = strprintf("%s via %s recv=%d send=%d", | ||
| 145 | strBtfAddr.empty() ? "unknown" : strBtfAddr.c_str(), | ||
| 146 | strMeeting.empty() ? "unknown" : strMeeting.c_str(), | ||
| 147 | fRecv, fSend); | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 54 | // --- Socket accounting (implementation lives in sockcount.h) -------------- | 151 | // --- Socket accounting (implementation lives in sockcount.h) -------------- |
| 55 | static const char* pszSockSite[SOCK_SITES] = { | 152 | static const char* pszSockSite[SOCK_SITES] = { |
| 56 | "external-ip probe", "listen socket", "inbound accept", | 153 | "external-ip probe", "listen socket", "inbound accept", |
| @@ -199,19 +296,64 @@ string GetDiagnosticsText() | |||
| 199 | 296 | ||
| 200 | str += SockAccountingText(); | 297 | str += SockAccountingText(); |
| 201 | 298 | ||
| 202 | str += "\n peer dir height last recv last send unsent\n"; | 299 | int64 nResolveAttempts, nResolveOk, nResolveMiss; |
| 300 | int64 nDialAttempts, nDialOk, nDialFailed; | ||
| 301 | int64 nRegisterOk, nRegisterFailed, nPairOk, nPairFailed; | ||
| 302 | int64 nHandshakeNoRecv, nHandshakeNoSend, nHandshakeSilent; | ||
| 303 | string strLastDialFail, strLastHandshakeTimeout; | ||
| 304 | CRITICAL_BLOCK(cs_btfChurn) | ||
| 305 | { | ||
| 306 | nResolveAttempts = nBtfResolveAttempts; | ||
| 307 | nResolveOk = nBtfResolveOk; | ||
| 308 | nResolveMiss = nBtfResolveMiss; | ||
| 309 | nDialAttempts = nBtfDialAttempts; | ||
| 310 | nDialOk = nBtfDialOk; | ||
| 311 | nDialFailed = nBtfDialFailed; | ||
| 312 | nRegisterOk = nBtfRegisterOk; | ||
| 313 | nRegisterFailed = nBtfRegisterFailed; | ||
| 314 | nPairOk = nBtfPairOk; | ||
| 315 | nPairFailed = nBtfPairFailed; | ||
| 316 | nHandshakeNoRecv = nBtfHandshakeNoRecv; | ||
| 317 | nHandshakeNoSend = nBtfHandshakeNoSend; | ||
| 318 | nHandshakeSilent = nBtfHandshakeSilent; | ||
| 319 | strLastDialFail = strBtfLastDialFail; | ||
| 320 | strLastHandshakeTimeout = strBtfLastHandshakeTimeout; | ||
| 321 | } | ||
| 322 | str += "\n .btf churn\n"; | ||
| 323 | str += strprintf(" resolves %lld attempts, %lld ok, %lld no descriptor\n", | ||
| 324 | (long long)nResolveAttempts, (long long)nResolveOk, | ||
| 325 | (long long)nResolveMiss); | ||
| 326 | str += strprintf(" outbound dials %lld attempts, %lld ok, %lld failed\n", | ||
| 327 | (long long)nDialAttempts, (long long)nDialOk, | ||
| 328 | (long long)nDialFailed); | ||
| 329 | str += strprintf(" rendezvous local register ok/fail %lld/%lld, pair ok/drop %lld/%lld\n", | ||
| 330 | (long long)nRegisterOk, (long long)nRegisterFailed, | ||
| 331 | (long long)nPairOk, (long long)nPairFailed); | ||
| 332 | str += strprintf(" handshake grace silent %lld, no recv %lld, no send %lld\n", | ||
| 333 | (long long)nHandshakeSilent, (long long)nHandshakeNoRecv, | ||
| 334 | (long long)nHandshakeNoSend); | ||
| 335 | if (!strLastDialFail.empty()) | ||
| 336 | str += strprintf(" last dial failure %s\n", strLastDialFail.c_str()); | ||
| 337 | if (!strLastHandshakeTimeout.empty()) | ||
| 338 | str += strprintf(" last handshake %s\n", strLastHandshakeTimeout.c_str()); | ||
| 339 | |||
| 340 | str += "\n peer dir height last recv last send unsent via\n"; | ||
| 203 | foreach(CNode* pnode, vCopy) | 341 | foreach(CNode* pnode, vCopy) |
| 204 | { | 342 | { |
| 205 | int nSendSize = 0; | 343 | int nSendSize = 0; |
| 206 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 344 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 207 | nSendSize = (int)pnode->vSend.size(); | 345 | nSendSize = (int)pnode->vSend.size(); |
| 208 | str += strprintf(" %-28s %-4s %6d %10s %10s %7d\n", | 346 | string strVia; |
| 347 | if (!pnode->strBtfMeeting.empty()) | ||
| 348 | strVia = pnode->strBtfMeeting; | ||
| 349 | str += strprintf(" %-28s %-4s %6d %10s %10s %7d %s\n", | ||
| 209 | pnode->addr.ToString().substr(0, 28).c_str(), | 350 | pnode->addr.ToString().substr(0, 28).c_str(), |
| 210 | pnode->fInbound ? "in" : "out", | 351 | pnode->fInbound ? "in" : "out", |
| 211 | pnode->nStartingHeight, | 352 | pnode->nStartingHeight, |
| 212 | FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(), | 353 | FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(), |
| 213 | FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(), | 354 | FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(), |
| 214 | nSendSize); | 355 | nSendSize, |
| 356 | strVia.substr(0, 28).c_str()); | ||
| 215 | } | 357 | } |
| 216 | return str; | 358 | return str; |
| 217 | } | 359 | } |
| @@ -814,30 +956,41 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p | |||
| 814 | return pnode; | 956 | return pnode; |
| 815 | } | 957 | } |
| 816 | 958 | ||
| 959 | BtfChurnNoteDialAttempt(strBtfAddr, strMeeting); | ||
| 817 | size_t colon = strMeeting.rfind(':'); | 960 | size_t colon = strMeeting.rfind(':'); |
| 818 | if (colon == string::npos) | 961 | if (colon == string::npos) |
| 962 | { | ||
| 963 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 819 | return NULL; | 964 | return NULL; |
| 965 | } | ||
| 820 | string strHost = strMeeting.substr(0, colon); | 966 | string strHost = strMeeting.substr(0, colon); |
| 821 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); | 967 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); |
| 822 | if (nPort <= 0 || nPort > 65535) | 968 | if (nPort <= 0 || nPort > 65535) |
| 969 | { | ||
| 970 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 823 | return NULL; | 971 | return NULL; |
| 972 | } | ||
| 824 | 973 | ||
| 825 | btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); | 974 | btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); |
| 826 | if (hSocket == INVALID_SOCKET) | 975 | if (hSocket == INVALID_SOCKET) |
| 827 | { | 976 | { |
| 828 | if (fDebug) | 977 | if (fDebug) |
| 829 | LogPrint("net", "ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); | 978 | LogPrint("net", "ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); |
| 979 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | ||
| 830 | return NULL; | 980 | return NULL; |
| 831 | } | 981 | } |
| 832 | 982 | ||
| 833 | if (fDebug) | 983 | if (fDebug) |
| 834 | LogPrint("net", "connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); | 984 | LogPrint("net", "connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); |
| 985 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, true); | ||
| 835 | 986 | ||
| 836 | // This one answered -- worth trying first next time we start. | 987 | // This one answered -- worth trying first next time we start. |
| 837 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub); | 988 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub); |
| 838 | 989 | ||
| 839 | // Add node | 990 | // Add node |
| 840 | pnode = new CNode(hSocket, addr, false); | 991 | pnode = new CNode(hSocket, addr, false); |
| 992 | pnode->strBtfAddr = strBtfAddr; | ||
| 993 | pnode->strBtfMeeting = strMeeting; | ||
| 841 | pnode->AddRef(); | 994 | pnode->AddRef(); |
| 842 | CRITICAL_BLOCK(cs_vNodes) | 995 | CRITICAL_BLOCK(cs_vNodes) |
| 843 | vNodes.push_back(pnode); | 996 | vNodes.push_back(pnode); |
| @@ -863,12 +1016,15 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 863 | 1016 | ||
| 864 | string strMeeting; | 1017 | string strMeeting; |
| 865 | unsigned char enc_pub[32]; | 1018 | unsigned char enc_pub[32]; |
| 1019 | BtfChurnNoteResolveAttempt(); | ||
| 866 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) | 1020 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) |
| 867 | { | 1021 | { |
| 1022 | BtfChurnNoteResolveResult(false); | ||
| 868 | if (fDebug) | 1023 | if (fDebug) |
| 869 | LogPrint("net", "ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); | 1024 | LogPrint("net", "ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); |
| 870 | return NULL; | 1025 | return NULL; |
| 871 | } | 1026 | } |
| 1027 | BtfChurnNoteResolveResult(true); | ||
| 872 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); | 1028 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); |
| 873 | } | 1029 | } |
| 874 | 1030 | ||
| @@ -951,12 +1107,14 @@ void ThreadBtfAccept(void* parg) | |||
| 951 | } | 1107 | } |
| 952 | if (rv == btf::RV_INVALID) | 1108 | if (rv == btf::RV_INVALID) |
| 953 | { | 1109 | { |
| 1110 | BtfChurnNoteRegisterResult(strMeeting, false); | ||
| 954 | LogPrint("net", "rendezvous: could not register at %s, trying another\n", | 1111 | LogPrint("net", "rendezvous: could not register at %s, trying another\n", |
| 955 | strMeeting.c_str()); | 1112 | strMeeting.c_str()); |
| 956 | iRelay++; // this relay is down/attacked -> try the next one | 1113 | iRelay++; // this relay is down/attacked -> try the next one |
| 957 | Sleep(3000); | 1114 | Sleep(3000); |
| 958 | continue; | 1115 | continue; |
| 959 | } | 1116 | } |
| 1117 | BtfChurnNoteRegisterResult(strMeeting, true); | ||
| 960 | 1118 | ||
| 961 | // Registered. Advertise THIS relay now, while we are listed and before | 1119 | // Registered. Advertise THIS relay now, while we are listed and before |
| 962 | // anybody dials -- a descriptor naming it is what makes a dial possible | 1120 | // anybody dials -- a descriptor naming it is what makes a dial possible |
| @@ -971,7 +1129,9 @@ void ThreadBtfAccept(void* parg) | |||
| 971 | // here, the node stopped being reachable, and nothing in the log said | 1129 | // here, the node stopped being reachable, and nothing in the log said |
| 972 | // so. Re-registering every few minutes when nobody has dialled costs | 1130 | // so. Re-registering every few minutes when nobody has dialled costs |
| 973 | // one reconnect and removes the whole failure mode. | 1131 | // one reconnect and removes the whole failure mode. |
| 974 | if (!btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS)) | 1132 | bool fPaired = btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS); |
| 1133 | BtfChurnNotePairResult(strMeeting, fPaired); | ||
| 1134 | if (!fPaired) | ||
| 975 | { | 1135 | { |
| 976 | btf::RvClose(rv); | 1136 | btf::RvClose(rv); |
| 977 | LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), " | 1137 | LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), " |
| @@ -981,7 +1141,11 @@ void ThreadBtfAccept(void* parg) | |||
| 981 | 1141 | ||
| 982 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); | 1142 | btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); |
| 983 | if (hSocket == INVALID_SOCKET) | 1143 | if (hSocket == INVALID_SOCKET) |
| 1144 | { | ||
| 1145 | LogPrint("net", "rendezvous: paired at %s but channel setup failed\n", | ||
| 1146 | strMeeting.c_str()); | ||
| 984 | continue; | 1147 | continue; |
| 1148 | } | ||
| 985 | 1149 | ||
| 986 | // The dialer is anonymous (its pubkey never reaches us), so tag the | 1150 | // The dialer is anonymous (its pubkey never reaches us), so tag the |
| 987 | // connection with a random marker address. | 1151 | // connection with a random marker address. |
| @@ -991,6 +1155,8 @@ void ThreadBtfAccept(void* parg) | |||
| 991 | 1155 | ||
| 992 | LogPrint("net", "accepted .btf connection via rendezvous %s\n", strMeeting.c_str()); | 1156 | LogPrint("net", "accepted .btf connection via rendezvous %s\n", strMeeting.c_str()); |
| 993 | CNode* pnode = new CNode(hSocket, addr, true); | 1157 | CNode* pnode = new CNode(hSocket, addr, true); |
| 1158 | pnode->strBtfAddr = "inbound"; | ||
| 1159 | pnode->strBtfMeeting = strMeeting; | ||
| 994 | pnode->AddRef(); | 1160 | pnode->AddRef(); |
| 995 | CRITICAL_BLOCK(cs_vNodes) | 1161 | CRITICAL_BLOCK(cs_vNodes) |
| 996 | vNodes.push_back(pnode); | 1162 | vNodes.push_back(pnode); |
| @@ -1457,6 +1623,9 @@ void ThreadSocketHandler2(void* parg) | |||
| 1457 | { | 1623 | { |
| 1458 | if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) | 1624 | if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) |
| 1459 | { | 1625 | { |
| 1626 | if (!pnode->strBtfMeeting.empty()) | ||
| 1627 | BtfChurnNoteHandshakeTimeout(pnode->strBtfAddr, pnode->strBtfMeeting, | ||
| 1628 | pnode->nLastRecv != 0, pnode->nLastSend != 0); | ||
| 1460 | LogPrint("net", "socket no message in first %d seconds, recv=%d send=%d\n", | 1629 | LogPrint("net", "socket no message in first %d seconds, recv=%d send=%d\n", |
| 1461 | BTF_HANDSHAKE_GRACE_SECS, pnode->nLastRecv != 0, pnode->nLastSend != 0); | 1630 | BTF_HANDSHAKE_GRACE_SECS, pnode->nLastRecv != 0, pnode->nLastSend != 0); |
| 1462 | pnode->fDisconnect = true; | 1631 | pnode->fDisconnect = true; |
| @@ -1803,9 +1972,22 @@ bool StartNode(string& strError) | |||
| 1803 | 1972 | ||
| 1804 | // Anonymous inbound: this node's .btf hidden service, reachable through the | 1973 | // Anonymous inbound: this node's .btf hidden service, reachable through the |
| 1805 | // meeting relay without exposing our IP or needing a public port. | 1974 | // meeting relay without exposing our IP or needing a public port. |
| 1975 | // | ||
| 1976 | // Several of these, not one. Each holds its own registration at a | ||
| 1977 | // rendezvous, and a registration is consumed the moment a caller is paired | ||
| 1978 | // with it -- so with a single thread the node is unreachable for the whole | ||
| 1979 | // gap between being paired and registering again. Measured from a node's | ||
| 1980 | // own counters: half of all outbound dials failed for exactly that reason, | ||
| 1981 | // 15 of 30 in eleven minutes, while descriptors resolved 53 times out of | ||
| 1982 | // 54. Spares parked at the relay close that gap. | ||
| 1983 | // | ||
| 1984 | // Needs a relay that keeps more than one registration per node; against an | ||
| 1985 | // older relay the extra threads are harmless, because it drops the previous | ||
| 1986 | // registration on each new one and the node ends up where it started. | ||
| 1806 | if (!vBtfMeetingRelays.empty()) | 1987 | if (!vBtfMeetingRelays.empty()) |
| 1807 | if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) | 1988 | for (int i = 0; i < BTF_ACCEPT_THREADS; i++) |
| 1808 | printf("Error: _beginthread(ThreadBtfAccept) failed\n"); | 1989 | if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) |
| 1990 | printf("Error: _beginthread(ThreadBtfAccept) failed\n"); | ||
| 1809 | 1991 | ||
| 1810 | // Anonymous outbound: keep a connection to a specific .btf peer, if asked. | 1992 | // Anonymous outbound: keep a connection to a specific .btf peer, if asked. |
| 1811 | if (!strBtfConnect.empty()) | 1993 | if (!strBtfConnect.empty()) |
| @@ -54,6 +54,12 @@ static const int64 PEX_MIN_INTERVAL = 60; | |||
| 54 | // traffic is nothing: one per relay per five minutes. | 54 | // traffic is nothing: one per relay per five minutes. |
| 55 | static const int BTF_RENDEZVOUS_WAIT_SECS = 300; | 55 | static const int BTF_RENDEZVOUS_WAIT_SECS = 300; |
| 56 | 56 | ||
| 57 | // Registrations this node parks at a rendezvous at once. One is what the | ||
| 58 | // protocol needs; more is what keeps the node reachable while one of them is | ||
| 59 | // being paired, since pairing consumes the registration it used. Four matches | ||
| 60 | // the relay's ceiling per node. | ||
| 61 | static const int BTF_ACCEPT_THREADS = 3; | ||
| 62 | |||
| 57 | // Liveness of an established peer connection. BTF_RENDEZVOUS_WAIT_SECS bounds | 63 | // Liveness of an established peer connection. BTF_RENDEZVOUS_WAIT_SECS bounds |
| 58 | // the wait for a dial to arrive; these bound the connection that comes out of | 64 | // the wait for a dial to arrive; these bound the connection that comes out of |
| 59 | // it, which has the same failure mode once it goes quiet. | 65 | // it, which has the same failure mode once it goes quiet. |
| @@ -139,6 +145,18 @@ extern int nBestHeight; | |||
| 139 | // absurd height cannot move it. | 145 | // absurd height cannot move it. |
| 140 | int GetPeerMedianHeight(); | 146 | int GetPeerMedianHeight(); |
| 141 | 147 | ||
| 148 | // Lightweight .btf churn accounting. These counters are diagnostics only: they | ||
| 149 | // let an operator tell stale descriptors, relay pairing failures, and paired | ||
| 150 | // tunnels that never speak apart before changing behaviour. | ||
| 151 | void BtfChurnNoteResolveAttempt(); | ||
| 152 | void BtfChurnNoteResolveResult(bool fOk); | ||
| 153 | void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting); | ||
| 154 | void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk); | ||
| 155 | void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk); | ||
| 156 | void BtfChurnNotePairResult(const string& strMeeting, bool fOk); | ||
| 157 | void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting, | ||
| 158 | bool fRecv, bool fSend); | ||
| 159 | |||
| 142 | // Descriptors to hand a peer: ours first, then peers that answered us. | 160 | // Descriptors to hand a peer: ours first, then peers that answered us. |
| 143 | void BtfPexCollect(std::vector<std::string>& vDescOut); | 161 | void BtfPexCollect(std::vector<std::string>& vDescOut); |
| 144 | // Verify descriptors a peer sent and remember the good ones. Returns how many. | 162 | // Verify descriptors a peer sent and remember the good ones. Returns how many. |
| @@ -606,6 +624,8 @@ public: | |||
| 606 | int64 nIncompleteMessageStart; | 624 | int64 nIncompleteMessageStart; |
| 607 | unsigned int nIncompleteMessageSize; | 625 | unsigned int nIncompleteMessageSize; |
| 608 | string strIncompleteMessageCommand; | 626 | string strIncompleteMessageCommand; |
| 627 | string strBtfAddr; | ||
| 628 | string strBtfMeeting; | ||
| 609 | 629 | ||
| 610 | // Height this peer announced in its version message, or -1 if it sent a | 630 | // Height this peer announced in its version message, or -1 if it sent a |
| 611 | // version message that predates the field. Knowing how far along everyone | 631 | // version message that predates the field. Knowing how far along everyone |
| @@ -639,6 +659,8 @@ public: | |||
| 639 | nIncompleteMessageStart = 0; | 659 | nIncompleteMessageStart = 0; |
| 640 | nIncompleteMessageSize = 0; | 660 | nIncompleteMessageSize = 0; |
| 641 | strIncompleteMessageCommand.clear(); | 661 | strIncompleteMessageCommand.clear(); |
| 662 | strBtfAddr.clear(); | ||
| 663 | strBtfMeeting.clear(); | ||
| 642 | nStartingHeight = -1; | 664 | nStartingHeight = -1; |
| 643 | vfSubscribe.assign(256, false); | 665 | vfSubscribe.assign(256, false); |
| 644 | 666 | ||
diff --git a/src/nostr.cpp b/src/nostr.cpp index dc96be2..1fd60ad 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -1596,6 +1596,8 @@ static void ConnectDiscoveredBtfPeers() | |||
| 1596 | // handshakes refused, which then looks exactly like "dead peers" even | 1596 | // handshakes refused, which then looks exactly like "dead peers" even |
| 1597 | // though the descriptors were perfectly resolvable. | 1597 | // though the descriptors were perfectly resolvable. |
| 1598 | map<string, BtfResolvedPeer> resolved; | 1598 | map<string, BtfResolvedPeer> resolved; |
| 1599 | for (const string& addr : candidates) | ||
| 1600 | BtfChurnNoteResolveAttempt(); | ||
| 1599 | BtfResolveMany(candidates, resolved); | 1601 | BtfResolveMany(candidates, resolved); |
| 1600 | 1602 | ||
| 1601 | // Candidates whose descriptor didn't resolve this pass are recorded as a | 1603 | // Candidates whose descriptor didn't resolve this pass are recorded as a |
| @@ -1608,9 +1610,11 @@ static void ConnectDiscoveredBtfPeers() | |||
| 1608 | { | 1610 | { |
| 1609 | if (resolved.count(addr)) | 1611 | if (resolved.count(addr)) |
| 1610 | { | 1612 | { |
| 1613 | BtfChurnNoteResolveResult(true); | ||
| 1611 | toDial.push_back(addr); | 1614 | toDial.push_back(addr); |
| 1612 | continue; | 1615 | continue; |
| 1613 | } | 1616 | } |
| 1617 | BtfChurnNoteResolveResult(false); | ||
| 1614 | int n = ++g_btfPeerFails[addr]; | 1618 | int n = ++g_btfPeerFails[addr]; |
| 1615 | int64 cap = (n >= BTF_CHRONIC_FAIL_THRESHOLD) ? BTF_BACKOFF_CAP_SECS_DEAD | 1619 | int64 cap = (n >= BTF_CHRONIC_FAIL_THRESHOLD) ? BTF_BACKOFF_CAP_SECS_DEAD |
| 1616 | : BTF_BACKOFF_CAP_SECS; | 1620 | : BTF_BACKOFF_CAP_SECS; |