diff options
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 181 |
1 files changed, 101 insertions, 80 deletions
diff --git a/src/net.cpp b/src/net.cpp index 34c608c..bb39122 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -13,15 +13,32 @@ | |||
| 13 | #include <thread> // hardware_concurrency, for the miner thread count | 13 | #include <thread> // hardware_concurrency, for the miner thread count |
| 14 | #ifdef _WIN32 | 14 | #ifdef _WIN32 |
| 15 | #include <winsock2.h> | 15 | #include <winsock2.h> |
| 16 | #else | ||
| 17 | #include <poll.h> | ||
| 16 | #endif | 18 | #endif |
| 17 | #include <openssl/rand.h> | 19 | #include <openssl/rand.h> |
| 18 | #include "btfaddr.h" | 20 | #include "btfaddr.h" |
| 19 | #include "btftunnel.h" | 21 | #include "btftunnel.h" |
| 22 | #include "proxy.h" | ||
| 20 | 23 | ||
| 21 | void ThreadReconnectCachedBtfPeers(void* parg); | 24 | void ThreadReconnectCachedBtfPeers(void* parg); |
| 22 | void ThreadMessageHandler2(void* parg); | 25 | void ThreadMessageHandler2(void* parg); |
| 23 | void ThreadSocketHandler2(void* parg); | 26 | void ThreadSocketHandler2(void* parg); |
| 24 | 27 | ||
| 28 | #ifdef _WIN32 | ||
| 29 | typedef WSAPOLLFD BtfPollFd; | ||
| 30 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 31 | { | ||
| 32 | return WSAPoll(pfd, nfd, nTimeoutMs); | ||
| 33 | } | ||
| 34 | #else | ||
| 35 | typedef struct pollfd BtfPollFd; | ||
| 36 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 37 | { | ||
| 38 | return poll(pfd, (nfds_t)nfd, nTimeoutMs); | ||
| 39 | } | ||
| 40 | #endif | ||
| 41 | |||
| 25 | 42 | ||
| 26 | 43 | ||
| 27 | 44 | ||
| @@ -153,7 +170,7 @@ static const char* pszSockSite[SOCK_SITES] = { | |||
| 153 | "external-ip probe", "listen socket", "inbound accept", | 170 | "external-ip probe", "listen socket", "inbound accept", |
| 154 | "rendezvous dial", "rendezvous listen", "rendezvous accept", | 171 | "rendezvous dial", "rendezvous listen", "rendezvous accept", |
| 155 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", | 172 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", |
| 156 | "nostr relay" | 173 | "nostr relay", "stratum bridge" |
| 157 | }; | 174 | }; |
| 158 | 175 | ||
| 159 | static string SockAccountingText() | 176 | static string SockAccountingText() |
| @@ -274,8 +291,8 @@ string GetDiagnosticsText() | |||
| 274 | 291 | ||
| 275 | // The number that would have made the deafness obvious. Anything held but | 292 | // The number that would have made the deafness obvious. Anything held but |
| 276 | // not watched is a socket this node will never read again. | 293 | // not watched is a socket this node will never read again. |
| 277 | str += strprintf(" peers watched %d of %d that select() can hold%s\n", | 294 | str += strprintf(" peers watched %d of %d held by poll%s\n", |
| 278 | nPeersWatched, (int)FD_SETSIZE - 1, | 295 | nPeersWatched, nHeld, |
| 279 | nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); | 296 | nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); |
| 280 | 297 | ||
| 281 | if (nBlocksReceived > 0) | 298 | if (nBlocksReceived > 0) |
| @@ -361,6 +378,9 @@ string GetDiagnosticsText() | |||
| 361 | 378 | ||
| 362 | bool GetMyExternalIP(unsigned int& ipRet) | 379 | bool GetMyExternalIP(unsigned int& ipRet) |
| 363 | { | 380 | { |
| 381 | if (BtfSocks5ProxyEnabled()) | ||
| 382 | return error("GetMyExternalIP() skipped while SOCKS5 proxy is enabled\n"); | ||
| 383 | |||
| 364 | // Try several plain-text IP echo services in order. | 384 | // Try several plain-text IP echo services in order. |
| 365 | // Each returns just the IPv4 address as the first line of the HTTP body. | 385 | // Each returns just the IPv4 address as the first line of the HTTP body. |
| 366 | struct { const char* host; const char* path; } services[] = { | 386 | struct { const char* host; const char* path; } services[] = { |
| @@ -1127,7 +1147,7 @@ void CNode::Disconnect() | |||
| 1127 | 1147 | ||
| 1128 | // Invalidate before anything else can look at it. This used to close the | 1148 | // Invalidate before anything else can look at it. This used to close the |
| 1129 | // socket and leave the handle number sitting in the object, so ~CNode | 1149 | // socket and leave the handle number sitting in the object, so ~CNode |
| 1130 | // closed it a second time and the select loop kept arming it in the | 1150 | // closed it a second time and the socket loop kept arming it in the |
| 1131 | // meantime. See the note on ~CNode in net.h. | 1151 | // meantime. See the note on ~CNode in net.h. |
| 1132 | BtfCloseSocket(hSocket); | 1152 | BtfCloseSocket(hSocket); |
| 1133 | hSocket = INVALID_SOCKET; | 1153 | hSocket = INVALID_SOCKET; |
| @@ -1276,52 +1296,49 @@ void ThreadSocketHandler2(void* parg) | |||
| 1276 | // | 1296 | // |
| 1277 | // Find which sockets have data to receive | 1297 | // Find which sockets have data to receive |
| 1278 | // | 1298 | // |
| 1279 | struct timeval timeout; | 1299 | static const int nPollTimeoutMs = 50; // frequency to poll pnode->vSend |
| 1280 | timeout.tv_sec = 0; | 1300 | |
| 1281 | timeout.tv_usec = 50000; // frequency to poll pnode->vSend | 1301 | struct NodePollResult |
| 1282 | 1302 | { | |
| 1283 | fd_set fdsetRecv; | 1303 | CNode* pnode; |
| 1284 | fd_set fdsetSend; | 1304 | SOCKET hSocket; |
| 1285 | FD_ZERO(&fdsetRecv); | 1305 | bool fRecv; |
| 1286 | FD_ZERO(&fdsetSend); | 1306 | bool fSend; |
| 1287 | SOCKET hSocketMax = 0; | 1307 | bool fInvalid; |
| 1288 | FD_SET(hListenSocket, &fdsetRecv); | 1308 | NodePollResult(CNode* pnodeIn, SOCKET hSocketIn) |
| 1289 | hSocketMax = max(hSocketMax, hListenSocket); | 1309 | : pnode(pnodeIn), hSocket(hSocketIn), fRecv(false), fSend(false), fInvalid(false) {} |
| 1290 | // FD_SET drops silently once the set is full, so count what goes in | 1310 | }; |
| 1291 | // and say so. A peer that never makes it into the set is never read, | 1311 | |
| 1292 | // and that is indistinguishable from a peer with nothing to say -- | 1312 | vector<BtfPollFd> vPoll; |
| 1293 | // the failure this whole path used to hide. | 1313 | vector<NodePollResult> vPollNodes; |
| 1294 | unsigned int nWatched = 1; // the listen socket | 1314 | |
| 1315 | BtfPollFd listenPoll; | ||
| 1316 | memset(&listenPoll, 0, sizeof(listenPoll)); | ||
| 1317 | listenPoll.fd = hListenSocket; | ||
| 1318 | listenPoll.events = POLLIN; | ||
| 1319 | vPoll.push_back(listenPoll); | ||
| 1320 | |||
| 1295 | CRITICAL_BLOCK(cs_vNodes) | 1321 | CRITICAL_BLOCK(cs_vNodes) |
| 1296 | { | 1322 | { |
| 1297 | foreach(CNode* pnode, vNodes) | 1323 | foreach(CNode* pnode, vNodes) |
| 1298 | { | 1324 | { |
| 1299 | if (nWatched >= FD_SETSIZE) | ||
| 1300 | break; | ||
| 1301 | // A disconnected node stays in vNodes until its references go. | 1325 | // A disconnected node stays in vNodes until its references go. |
| 1302 | // Arming its closed handle asks select() to watch a number the | 1326 | // Do not watch a closed handle number: the OS may already have |
| 1303 | // operating system may already have given to somebody else. | 1327 | // handed it to a different connection. |
| 1304 | if (pnode->hSocket == INVALID_SOCKET) | 1328 | if (pnode->hSocket == INVALID_SOCKET) |
| 1305 | continue; | 1329 | continue; |
| 1306 | FD_SET(pnode->hSocket, &fdsetRecv); | 1330 | |
| 1307 | nWatched++; | 1331 | BtfPollFd pfd; |
| 1308 | hSocketMax = max(hSocketMax, pnode->hSocket); | 1332 | memset(&pfd, 0, sizeof(pfd)); |
| 1333 | pfd.fd = pnode->hSocket; | ||
| 1334 | pfd.events = POLLIN; | ||
| 1309 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1335 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1310 | if (!pnode->vSend.empty()) | 1336 | if (!pnode->vSend.empty()) |
| 1311 | FD_SET(pnode->hSocket, &fdsetSend); | 1337 | pfd.events |= POLLOUT; |
| 1312 | } | 1338 | vPoll.push_back(pfd); |
| 1313 | if (vNodes.size() + 1 > (size_t)FD_SETSIZE) | 1339 | vPollNodes.push_back(NodePollResult(pnode, pnode->hSocket)); |
| 1314 | { | ||
| 1315 | static int64 nLastWarned = 0; | ||
| 1316 | if (GetTime() - nLastWarned > 60) | ||
| 1317 | { | ||
| 1318 | nLastWarned = GetTime(); | ||
| 1319 | printf("WARNING: %d peers but select() can only watch %d -- %d are not being read\n", | ||
| 1320 | (int)vNodes.size(), (int)FD_SETSIZE - 1, | ||
| 1321 | (int)vNodes.size() - (int)FD_SETSIZE + 1); | ||
| 1322 | } | ||
| 1323 | } | 1340 | } |
| 1324 | nPeersWatched = (int)nWatched - 1; // less the listen socket | 1341 | nPeersWatched = (int)vPollNodes.size(); |
| 1325 | } | 1342 | } |
| 1326 | 1343 | ||
| 1327 | // Say the state of the node out loud now and then, so a log pulled off | 1344 | // Say the state of the node out loud now and then, so a log pulled off |
| @@ -1342,32 +1359,17 @@ void ThreadSocketHandler2(void* parg) | |||
| 1342 | } | 1359 | } |
| 1343 | 1360 | ||
| 1344 | vfThreadRunning[0] = false; | 1361 | vfThreadRunning[0] = false; |
| 1345 | int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); | 1362 | int nPoll = BtfPoll(&vPoll[0], (unsigned int)vPoll.size(), nPollTimeoutMs); |
| 1346 | vfThreadRunning[0] = true; | 1363 | vfThreadRunning[0] = true; |
| 1347 | CheckForShutdown(0); | 1364 | CheckForShutdown(0); |
| 1348 | if (nSelect == SOCKET_ERROR) | 1365 | if (nPoll == SOCKET_ERROR) |
| 1349 | { | 1366 | { |
| 1350 | // This used to answer a failed select() by marking every | ||
| 1351 | // descriptor from 0 to hSocketMax ready, which is code written for | ||
| 1352 | // POSIX, where a descriptor is a small integer. A Windows SOCKET is | ||
| 1353 | // a kernel handle in the thousands, so the loop iterated over | ||
| 1354 | // numbers that are not sockets and told the rest of the function | ||
| 1355 | // that every peer had data. One bad handle in the set makes | ||
| 1356 | // select() fail on every pass, so a single closed socket put the | ||
| 1357 | // whole thread into that state permanently. | ||
| 1358 | // | ||
| 1359 | // Find the bad handle instead and let it be dropped. | ||
| 1360 | int nErr = WSAGetLastError(); | 1367 | int nErr = WSAGetLastError(); |
| 1361 | LogPrint("net", "select failed: %d\n", nErr); | 1368 | LogPrint("net", "poll failed: %d\n", nErr); |
| 1362 | FD_ZERO(&fdsetRecv); | ||
| 1363 | FD_ZERO(&fdsetSend); | ||
| 1364 | CRITICAL_BLOCK(cs_vNodes) | 1369 | CRITICAL_BLOCK(cs_vNodes) |
| 1365 | { | 1370 | { |
| 1366 | foreach(CNode* pnode, vNodes) | 1371 | foreach(CNode* pnode, vNodes) |
| 1367 | { | 1372 | { |
| 1368 | // Already disconnected: its handle is gone on purpose, and | ||
| 1369 | // reporting it here would name the wrong node for a select() | ||
| 1370 | // failure it did not cause. | ||
| 1371 | if (pnode->hSocket == INVALID_SOCKET) | 1373 | if (pnode->hSocket == INVALID_SOCKET) |
| 1372 | continue; | 1374 | continue; |
| 1373 | int nType = 0; | 1375 | int nType = 0; |
| @@ -1376,24 +1378,27 @@ void ThreadSocketHandler2(void* parg) | |||
| 1376 | #else | 1378 | #else |
| 1377 | socklen_t nTypeLen = sizeof(nType); | 1379 | socklen_t nTypeLen = sizeof(nType); |
| 1378 | #endif | 1380 | #endif |
| 1379 | if (pnode->hSocket != INVALID_SOCKET && | 1381 | if (getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0) |
| 1380 | getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0) | ||
| 1381 | { | 1382 | { |
| 1382 | printf("dropping node %s: its socket is no longer valid\n", | 1383 | printf("dropping node %s: its socket is no longer valid\n", |
| 1383 | pnode->addr.ToString().c_str()); | 1384 | pnode->addr.ToString().c_str()); |
| 1384 | pnode->fDisconnect = true; | 1385 | pnode->fDisconnect = true; |
| 1385 | 1386 | ||
| 1386 | // Stop referring to it, or it goes back into the next | 1387 | // Stop referring to it, or it goes into the next poll |
| 1387 | // select() set and fails it again. | 1388 | // set and fails it again. |
| 1388 | // | 1389 | // |
| 1389 | // Marking fDisconnect is not enough: a node is only | 1390 | // Marking fDisconnect is not enough: a node is only |
| 1390 | // removed from vNodes once it has drained or once | 1391 | // removed from vNodes once it has drained or once |
| 1391 | // DISCONNECT_DRAIN_SECS has passed, so one dead | 1392 | // DISCONNECT_DRAIN_SECS has passed, so one dead |
| 1392 | // descriptor kept failing select() for a full minute -- | 1393 | // descriptor kept failing the wait for a full minute -- |
| 1393 | // and a failed select() zeroes both sets above, which | 1394 | // and a failed wait skips the service loop below |
| 1394 | // means NO peer is read on that pass. Measured on a | 1395 | // entirely, which means NO peer is read on that pass. |
| 1395 | // wedged pool: 1,027 consecutive passes over 61 | 1396 | // Measured on a wedged pool under the old select() |
| 1396 | // seconds, one bad socket, the entire node deaf. | 1397 | // implementation: 1,027 consecutive passes over 61 |
| 1398 | // seconds, one bad socket, the entire node deaf. poll() | ||
| 1399 | // reports a bad descriptor as POLLNVAL on that entry | ||
| 1400 | // rather than failing the whole call, so this path is | ||
| 1401 | // now the backstop rather than the common case. | ||
| 1397 | // | 1402 | // |
| 1398 | // Not closed, only forgotten. getsockopt just told us | 1403 | // Not closed, only forgotten. getsockopt just told us |
| 1399 | // this descriptor is not ours any more; closing it now | 1404 | // this descriptor is not ours any more; closing it now |
| @@ -1402,7 +1407,17 @@ void ThreadSocketHandler2(void* parg) | |||
| 1402 | } | 1407 | } |
| 1403 | } | 1408 | } |
| 1404 | } | 1409 | } |
| 1405 | Sleep(timeout.tv_usec/1000); | 1410 | Sleep(nPollTimeoutMs); |
| 1411 | } | ||
| 1412 | else | ||
| 1413 | { | ||
| 1414 | for (size_t i = 0; i < vPollNodes.size(); i++) | ||
| 1415 | { | ||
| 1416 | short revents = vPoll[i + 1].revents; // poll[0] is the listener | ||
| 1417 | vPollNodes[i].fRecv = (revents & (POLLIN | POLLERR | POLLHUP)) != 0; | ||
| 1418 | vPollNodes[i].fSend = (revents & POLLOUT) != 0; | ||
| 1419 | vPollNodes[i].fInvalid = (revents & POLLNVAL) != 0; | ||
| 1420 | } | ||
| 1406 | } | 1421 | } |
| 1407 | RandAddSeed(); | 1422 | RandAddSeed(); |
| 1408 | 1423 | ||
| @@ -1418,7 +1433,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1418 | // | 1433 | // |
| 1419 | // Accept new connections | 1434 | // Accept new connections |
| 1420 | // | 1435 | // |
| 1421 | if (FD_ISSET(hListenSocket, &fdsetRecv)) | 1436 | if (nPoll != SOCKET_ERROR && (vPoll[0].revents & POLLIN)) |
| 1422 | { | 1437 | { |
| 1423 | struct sockaddr_in sockaddr; | 1438 | struct sockaddr_in sockaddr; |
| 1424 | #ifdef _WIN32 | 1439 | #ifdef _WIN32 |
| @@ -1437,7 +1452,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1437 | { | 1452 | { |
| 1438 | // Refuse rather than accept a connection this node cannot | 1453 | // Refuse rather than accept a connection this node cannot |
| 1439 | // watch. Without this the listen socket kept taking peers -- | 1454 | // watch. Without this the listen socket kept taking peers -- |
| 1440 | // it is always in the select set -- while everything past the | 1455 | // it is always watched -- while everything past the |
| 1441 | // limit sat open and unread, which fed on itself: the deafer | 1456 | // limit sat open and unread, which fed on itself: the deafer |
| 1442 | // the node got, the more connections it collected. | 1457 | // the node got, the more connections it collected. |
| 1443 | unsigned int nNodes = 0; | 1458 | unsigned int nNodes = 0; |
| @@ -1464,22 +1479,28 @@ void ThreadSocketHandler2(void* parg) | |||
| 1464 | // | 1479 | // |
| 1465 | // Service each socket | 1480 | // Service each socket |
| 1466 | // | 1481 | // |
| 1467 | vector<CNode*> vNodesCopy; | 1482 | for (size_t i = 0; i < vPollNodes.size(); i++) |
| 1468 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1469 | vNodesCopy = vNodes; | ||
| 1470 | foreach(CNode* pnode, vNodesCopy) | ||
| 1471 | { | 1483 | { |
| 1472 | CheckForShutdown(0); | 1484 | CheckForShutdown(0); |
| 1473 | SOCKET hSocket = pnode->hSocket; | 1485 | NodePollResult& poll = vPollNodes[i]; |
| 1474 | // Forgotten above because it was already invalid. FD_ISSET on it | 1486 | CNode* pnode = poll.pnode; |
| 1475 | // is meaningless and recv()/send() would be operating on -1. | 1487 | SOCKET hSocket = poll.hSocket; |
| 1476 | if (hSocket == INVALID_SOCKET) | 1488 | |
| 1489 | if (hSocket == INVALID_SOCKET || pnode->hSocket != hSocket) | ||
| 1477 | continue; | 1490 | continue; |
| 1478 | 1491 | ||
| 1492 | if (poll.fInvalid) | ||
| 1493 | { | ||
| 1494 | LogPrint("net", "poll reported invalid socket for %s\n", | ||
| 1495 | pnode->addr.ToString().c_str()); | ||
| 1496 | pnode->fDisconnect = true; | ||
| 1497 | continue; | ||
| 1498 | } | ||
| 1499 | |||
| 1479 | // | 1500 | // |
| 1480 | // Receive | 1501 | // Receive |
| 1481 | // | 1502 | // |
| 1482 | if (FD_ISSET(hSocket, &fdsetRecv)) | 1503 | if (poll.fRecv) |
| 1483 | { | 1504 | { |
| 1484 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | 1505 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) |
| 1485 | { | 1506 | { |
| @@ -1519,7 +1540,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1519 | // | 1540 | // |
| 1520 | // Send | 1541 | // Send |
| 1521 | // | 1542 | // |
| 1522 | if (FD_ISSET(hSocket, &fdsetSend)) | 1543 | if (poll.fSend) |
| 1523 | { | 1544 | { |
| 1524 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1545 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1525 | { | 1546 | { |