diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
| commit | ef9f06d43fc6577c15df2256abbb25b20d4227b8 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/net.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | ceae3d076b5ad11ee84729ae43f14e9312896646 (diff) | |
Bring main onto the line this project actually ships (incl. the 1.2.23 compactblock port)
Closes #6, #7, #8, #11, #35, #34, #29, #28, #27, and #16
See merge request !16
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 394 |
1 files changed, 292 insertions, 102 deletions
diff --git a/src/net.cpp b/src/net.cpp index 34c608c..0576402 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -13,15 +13,35 @@ | |||
| 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> |
| 20 | #include <cerrno> | ||
| 21 | #include <cstdlib> | ||
| 18 | #include "btfaddr.h" | 22 | #include "btfaddr.h" |
| 19 | #include "btftunnel.h" | 23 | #include "btftunnel.h" |
| 24 | #include "proxy.h" | ||
| 25 | #include "tor.h" | ||
| 20 | 26 | ||
| 21 | void ThreadReconnectCachedBtfPeers(void* parg); | 27 | void ThreadReconnectCachedBtfPeers(void* parg); |
| 22 | void ThreadMessageHandler2(void* parg); | 28 | void ThreadMessageHandler2(void* parg); |
| 23 | void ThreadSocketHandler2(void* parg); | 29 | void ThreadSocketHandler2(void* parg); |
| 24 | 30 | ||
| 31 | #ifdef _WIN32 | ||
| 32 | typedef WSAPOLLFD BtfPollFd; | ||
| 33 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 34 | { | ||
| 35 | return WSAPoll(pfd, nfd, nTimeoutMs); | ||
| 36 | } | ||
| 37 | #else | ||
| 38 | typedef struct pollfd BtfPollFd; | ||
| 39 | static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) | ||
| 40 | { | ||
| 41 | return poll(pfd, (nfds_t)nfd, nTimeoutMs); | ||
| 42 | } | ||
| 43 | #endif | ||
| 44 | |||
| 25 | 45 | ||
| 26 | 46 | ||
| 27 | 47 | ||
| @@ -30,10 +50,14 @@ void ThreadSocketHandler2(void* parg); | |||
| 30 | // | 50 | // |
| 31 | // Global state variables | 51 | // Global state variables |
| 32 | // | 52 | // |
| 53 | static const char pchMainnetMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | ||
| 54 | static const char pchTestnetMessageStart[4] = { (char)0xce, (char)0xe2, (char)0xc0, (char)0xff }; | ||
| 55 | static bool fTestNet = false; | ||
| 56 | char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | ||
| 33 | bool fClient = false; | 57 | bool fClient = false; |
| 34 | uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); | 58 | uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); |
| 35 | CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); | 59 | CAddress addrLocalHost(0, GetDefaultPort(), nLocalServices); |
| 36 | unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) | 60 | unsigned short nListenPort = GetDefaultPort(); // local P2P port (tunable via /port) |
| 37 | CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); | 61 | CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); |
| 38 | CNode* pnodeLocalHost = &nodeLocalHost; | 62 | CNode* pnodeLocalHost = &nodeLocalHost; |
| 39 | bool fShutdown = false; | 63 | bool fShutdown = false; |
| @@ -45,6 +69,8 @@ deque<pair<int64, CInv> > vRelayExpiration; | |||
| 45 | CCriticalSection cs_mapRelay; | 69 | CCriticalSection cs_mapRelay; |
| 46 | map<CInv, int64> mapAlreadyAskedFor; | 70 | map<CInv, int64> mapAlreadyAskedFor; |
| 47 | string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) | 71 | string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) |
| 72 | bool fBtfOnionOnly = false; // require direct onion transport for .btf peers | ||
| 73 | static const int BTF_ONION_CONNECT_TIMEOUT_SECS = 120; | ||
| 48 | 74 | ||
| 49 | int nPeersWatched = 0; | 75 | int nPeersWatched = 0; |
| 50 | int64 nBlocksReceived = 0; | 76 | int64 nBlocksReceived = 0; |
| @@ -68,6 +94,29 @@ static int64 nBtfHandshakeSilent = 0; | |||
| 68 | static string strBtfLastDialFail; | 94 | static string strBtfLastDialFail; |
| 69 | static string strBtfLastHandshakeTimeout; | 95 | static string strBtfLastHandshakeTimeout; |
| 70 | 96 | ||
| 97 | bool IsTestNet() | ||
| 98 | { | ||
| 99 | return fTestNet; | ||
| 100 | } | ||
| 101 | |||
| 102 | unsigned short GetDefaultPort() | ||
| 103 | { | ||
| 104 | return htons(fTestNet ? TESTNET_PORT : MAINNET_PORT); | ||
| 105 | } | ||
| 106 | |||
| 107 | void SelectNetworkParams(bool fTestNetIn) | ||
| 108 | { | ||
| 109 | if (fTestNet == fTestNetIn) | ||
| 110 | return; | ||
| 111 | |||
| 112 | fTestNet = fTestNetIn; | ||
| 113 | memcpy(pchMessageStart, | ||
| 114 | fTestNet ? pchTestnetMessageStart : pchMainnetMessageStart, | ||
| 115 | sizeof(pchMessageStart)); | ||
| 116 | nListenPort = GetDefaultPort(); | ||
| 117 | addrLocalHost.port = nListenPort; | ||
| 118 | } | ||
| 119 | |||
| 71 | void BtfChurnNoteResolveAttempt() | 120 | void BtfChurnNoteResolveAttempt() |
| 72 | { | 121 | { |
| 73 | CRITICAL_BLOCK(cs_btfChurn) | 122 | CRITICAL_BLOCK(cs_btfChurn) |
| @@ -153,7 +202,7 @@ static const char* pszSockSite[SOCK_SITES] = { | |||
| 153 | "external-ip probe", "listen socket", "inbound accept", | 202 | "external-ip probe", "listen socket", "inbound accept", |
| 154 | "rendezvous dial", "rendezvous listen", "rendezvous accept", | 203 | "rendezvous dial", "rendezvous listen", "rendezvous accept", |
| 155 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", | 204 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", |
| 156 | "nostr relay" | 205 | "nostr relay", "direct onion peer", "stratum bridge" |
| 157 | }; | 206 | }; |
| 158 | 207 | ||
| 159 | static string SockAccountingText() | 208 | static string SockAccountingText() |
| @@ -246,20 +295,27 @@ string GetDiagnosticsText() | |||
| 246 | int64 nNow = GetTime(); | 295 | int64 nNow = GetTime(); |
| 247 | string str; | 296 | string str; |
| 248 | 297 | ||
| 249 | int nInbound = 0, nHeld = 0; | 298 | int nInbound = 0, nHeld = 0, nDirectOnion = 0, nRendezvous = 0; |
| 250 | vector<CNode*> vCopy; | 299 | vector<CNode*> vCopy; |
| 251 | CRITICAL_BLOCK(cs_vNodes) | 300 | CRITICAL_BLOCK(cs_vNodes) |
| 252 | { | 301 | { |
| 253 | vCopy = vNodes; | 302 | vCopy = vNodes; |
| 254 | nHeld = (int)vNodes.size(); | 303 | nHeld = (int)vNodes.size(); |
| 255 | foreach(CNode* pnode, vNodes) | 304 | foreach(CNode* pnode, vNodes) |
| 305 | { | ||
| 256 | if (pnode->fInbound) | 306 | if (pnode->fInbound) |
| 257 | nInbound++; | 307 | nInbound++; |
| 308 | if (pnode->strBtfMeeting.find(".onion:") != string::npos) | ||
| 309 | nDirectOnion++; | ||
| 310 | else if (!pnode->strBtfMeeting.empty()) | ||
| 311 | nRendezvous++; | ||
| 312 | } | ||
| 258 | } | 313 | } |
| 259 | 314 | ||
| 260 | int nMedian = GetPeerMedianHeight(); | 315 | int nMedian = GetPeerMedianHeight(); |
| 261 | 316 | ||
| 262 | str += "Bitflash node diagnostics\n"; | 317 | str += "Bitflash node diagnostics\n"; |
| 318 | str += strprintf(" network %s\n", IsTestNet() ? "testnet" : "mainnet"); | ||
| 263 | str += strprintf(" uptime %s\n", | 319 | str += strprintf(" uptime %s\n", |
| 264 | FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str()); | 320 | FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str()); |
| 265 | if (nMedian < 0) | 321 | if (nMedian < 0) |
| @@ -274,9 +330,26 @@ string GetDiagnosticsText() | |||
| 274 | 330 | ||
| 275 | // The number that would have made the deafness obvious. Anything held but | 331 | // The number that would have made the deafness obvious. Anything held but |
| 276 | // not watched is a socket this node will never read again. | 332 | // 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", | 333 | str += strprintf(" peers watched %d of %d held by poll%s\n", |
| 278 | nPeersWatched, (int)FD_SETSIZE - 1, | 334 | nPeersWatched, nHeld, |
| 279 | nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); | 335 | nHeld > nPeersWatched ? " <-- NOT ALL PEERS ARE BEING READ" : ""); |
| 336 | str += strprintf(" managed Tor %s\n", BtfManagedTorStatus().c_str()); | ||
| 337 | |||
| 338 | string strTorMode = "disabled"; | ||
| 339 | if (BtfManagedTorEnabled()) | ||
| 340 | strTorMode = "managed"; | ||
| 341 | else if (BtfTorProxyEnabled()) | ||
| 342 | strTorMode = "external"; | ||
| 343 | else if (BtfSocks5ProxyEnabled()) | ||
| 344 | strTorMode = "socks5"; | ||
| 345 | string strOnion = BtfLocalOnionEndpoint(); | ||
| 346 | str += strprintf(" Tor mode %s%s\n", | ||
| 347 | strTorMode.c_str(), | ||
| 348 | fBtfOnionOnly ? " (onion-only)" : ""); | ||
| 349 | str += strprintf(" onion endpoint %s\n", | ||
| 350 | strOnion.empty() ? "none" : strOnion.c_str()); | ||
| 351 | str += strprintf(" .btf peers %d direct onion, %d rendezvous\n", | ||
| 352 | nDirectOnion, nRendezvous); | ||
| 280 | 353 | ||
| 281 | if (nBlocksReceived > 0) | 354 | if (nBlocksReceived > 0) |
| 282 | str += strprintf(" blocks received %lld (%lld arrived without a parent, %.1f%%)\n", | 355 | str += strprintf(" blocks received %lld (%lld arrived without a parent, %.1f%%)\n", |
| @@ -293,6 +366,7 @@ string GetDiagnosticsText() | |||
| 293 | nMining, | 366 | nMining, |
| 294 | (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() | 367 | (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() |
| 295 | : ", not mining"); | 368 | : ", not mining"); |
| 369 | str += strprintf(" large pages %s\n", RandomXLargePagesStatus()); | ||
| 296 | 370 | ||
| 297 | str += SockAccountingText(); | 371 | str += SockAccountingText(); |
| 298 | 372 | ||
| @@ -361,6 +435,9 @@ string GetDiagnosticsText() | |||
| 361 | 435 | ||
| 362 | bool GetMyExternalIP(unsigned int& ipRet) | 436 | bool GetMyExternalIP(unsigned int& ipRet) |
| 363 | { | 437 | { |
| 438 | if (BtfSocks5ProxyEnabled()) | ||
| 439 | return error("GetMyExternalIP() skipped while SOCKS5 proxy is enabled\n"); | ||
| 440 | |||
| 364 | // Try several plain-text IP echo services in order. | 441 | // Try several plain-text IP echo services in order. |
| 365 | // Each returns just the IPv4 address as the first line of the HTTP body. | 442 | // Each returns just the IPv4 address as the first line of the HTTP body. |
| 366 | struct { const char* host; const char* path; } services[] = { | 443 | struct { const char* host; const char* path; } services[] = { |
| @@ -524,6 +601,7 @@ struct CachedBtfPeer | |||
| 524 | { | 601 | { |
| 525 | string btfAddr; | 602 | string btfAddr; |
| 526 | string meeting; | 603 | string meeting; |
| 604 | string onion; | ||
| 527 | string encHex; | 605 | string encHex; |
| 528 | int64 lastSeen; | 606 | int64 lastSeen; |
| 529 | // The peer's own signed descriptor, as it announced itself. Kept verbatim | 607 | // The peer's own signed descriptor, as it announced itself. Kept verbatim |
| @@ -582,6 +660,8 @@ void LoadCachedBtfPeers(vector<CachedBtfPeer>& out) | |||
| 582 | CachedBtfPeer p; | 660 | CachedBtfPeer p; |
| 583 | p.btfAddr = o["btf"].get<string>(); | 661 | p.btfAddr = o["btf"].get<string>(); |
| 584 | p.meeting = o["meeting"].get<string>(); | 662 | p.meeting = o["meeting"].get<string>(); |
| 663 | if (o.contains("onion") && o["onion"].is_string()) | ||
| 664 | btf::NormalizeOnionEndpoint(o["onion"].get<string>(), p.onion); | ||
| 585 | p.encHex = o["enc"].get<string>(); | 665 | p.encHex = o["enc"].get<string>(); |
| 586 | p.lastSeen = o.value("seen", (int64)0); | 666 | p.lastSeen = o.value("seen", (int64)0); |
| 587 | if (o.contains("desc") && o["desc"].is_string()) | 667 | if (o.contains("desc") && o["desc"].is_string()) |
| @@ -605,7 +685,8 @@ static CCriticalSection cs_btfPeerCache; | |||
| 605 | // learned about by exchange must not cost us the ability to pass it on. | 685 | // learned about by exchange must not cost us the ability to pass it on. |
| 606 | static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | 686 | static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, |
| 607 | const unsigned char enc_pub[32], | 687 | const unsigned char enc_pub[32], |
| 608 | const string& strDesc = string()) | 688 | const string& strDesc = string(), |
| 689 | const string& strOnion = string()) | ||
| 609 | { | 690 | { |
| 610 | CRITICAL_BLOCK(cs_btfPeerCache) | 691 | CRITICAL_BLOCK(cs_btfPeerCache) |
| 611 | { | 692 | { |
| @@ -613,11 +694,15 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | |||
| 613 | LoadCachedBtfPeers(peers); | 694 | LoadCachedBtfPeers(peers); |
| 614 | 695 | ||
| 615 | string strKeepDesc = strDesc; | 696 | string strKeepDesc = strDesc; |
| 697 | string strKeepOnion; | ||
| 698 | btf::NormalizeOnionEndpoint(strOnion, strKeepOnion); | ||
| 616 | for (size_t i = 0; i < peers.size(); i++) | 699 | for (size_t i = 0; i < peers.size(); i++) |
| 617 | if (peers[i].btfAddr == strBtfAddr) | 700 | if (peers[i].btfAddr == strBtfAddr) |
| 618 | { | 701 | { |
| 619 | if (strKeepDesc.empty()) | 702 | if (strKeepDesc.empty()) |
| 620 | strKeepDesc = peers[i].desc; | 703 | strKeepDesc = peers[i].desc; |
| 704 | if (strKeepOnion.empty()) | ||
| 705 | strKeepOnion = peers[i].onion; | ||
| 621 | peers.erase(peers.begin() + i); | 706 | peers.erase(peers.begin() + i); |
| 622 | break; | 707 | break; |
| 623 | } | 708 | } |
| @@ -625,6 +710,7 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | |||
| 625 | CachedBtfPeer p; | 710 | CachedBtfPeer p; |
| 626 | p.btfAddr = strBtfAddr; | 711 | p.btfAddr = strBtfAddr; |
| 627 | p.meeting = strMeeting; | 712 | p.meeting = strMeeting; |
| 713 | p.onion = strKeepOnion; | ||
| 628 | p.encHex = BytesToHex(enc_pub, 32); | 714 | p.encHex = BytesToHex(enc_pub, 32); |
| 629 | p.lastSeen = GetTime(); | 715 | p.lastSeen = GetTime(); |
| 630 | p.desc = strKeepDesc; | 716 | p.desc = strKeepDesc; |
| @@ -639,6 +725,8 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, | |||
| 639 | nlohmann::json o; | 725 | nlohmann::json o; |
| 640 | o["btf"] = q.btfAddr; | 726 | o["btf"] = q.btfAddr; |
| 641 | o["meeting"] = q.meeting; | 727 | o["meeting"] = q.meeting; |
| 728 | if (!q.onion.empty()) | ||
| 729 | o["onion"] = q.onion; | ||
| 642 | o["enc"] = q.encHex; | 730 | o["enc"] = q.encHex; |
| 643 | o["seen"] = q.lastSeen; | 731 | o["seen"] = q.lastSeen; |
| 644 | if (!q.desc.empty()) | 732 | if (!q.desc.empty()) |
| @@ -727,7 +815,7 @@ static int TryBtfSeeds() | |||
| 727 | foreach(const string& strRelay, relays) | 815 | foreach(const string& strRelay, relays) |
| 728 | { | 816 | { |
| 729 | if (fShutdown) return nConnected; | 817 | if (fShutdown) return nConnected; |
| 730 | if (ConnectNodeBtfResolved(s.first, strRelay, enc)) | 818 | if (ConnectNodeBtfResolved(s.first, strRelay, "", enc)) |
| 731 | { | 819 | { |
| 732 | LogPrint("net", "btfseed: reached %s via %s\n", | 820 | LogPrint("net", "btfseed: reached %s via %s\n", |
| 733 | s.first.c_str(), strRelay.c_str()); | 821 | s.first.c_str(), strRelay.c_str()); |
| @@ -765,7 +853,7 @@ void ThreadReconnectCachedBtfPeers(void* parg) | |||
| 765 | if (fShutdown) return; | 853 | if (fShutdown) return; |
| 766 | unsigned char enc[32]; | 854 | unsigned char enc[32]; |
| 767 | if (!HexToBytes(p.encHex, enc, 32)) continue; | 855 | if (!HexToBytes(p.encHex, enc, 32)) continue; |
| 768 | if (ConnectNodeBtfResolved(p.btfAddr, p.meeting, enc)) | 856 | if (ConnectNodeBtfResolved(p.btfAddr, p.meeting, p.onion, enc)) |
| 769 | { | 857 | { |
| 770 | nConnected++; | 858 | nConnected++; |
| 771 | if (nConnected >= 8) break; // enough to bootstrap; the rest can wait | 859 | if (nConnected >= 8) break; // enough to bootstrap; the rest can wait |
| @@ -856,14 +944,37 @@ int BtfPexAccept(const vector<string>& vDesc) | |||
| 856 | if (!HexToBytes(d.enc, enc, 32)) | 944 | if (!HexToBytes(d.enc, enc, 32)) |
| 857 | continue; | 945 | continue; |
| 858 | 946 | ||
| 859 | RememberBtfPeer(strAddr, d.meeting_node, enc, strDesc); | 947 | RememberBtfPeer(strAddr, d.meeting_node, enc, strDesc, d.onion); |
| 860 | nKept++; | 948 | nKept++; |
| 861 | } | 949 | } |
| 862 | return nKept; | 950 | return nKept; |
| 863 | } | 951 | } |
| 864 | 952 | ||
| 953 | static bool SplitHostPort(const string& strHostPort, string& strHost, unsigned short& nPort) | ||
| 954 | { | ||
| 955 | size_t colon = strHostPort.rfind(':'); | ||
| 956 | if (colon == string::npos || colon == 0 || colon + 1 >= strHostPort.size()) | ||
| 957 | return false; | ||
| 958 | string port = strHostPort.substr(colon + 1); | ||
| 959 | for (size_t i = 0; i < port.size(); i++) | ||
| 960 | if (!isdigit((unsigned char)port[i])) | ||
| 961 | return false; | ||
| 962 | |||
| 963 | errno = 0; | ||
| 964 | char* end = NULL; | ||
| 965 | long n = strtol(port.c_str(), &end, 10); | ||
| 966 | if (errno == ERANGE || end == NULL || *end != '\0' || n <= 0 || n > 65535) | ||
| 967 | return false; | ||
| 968 | |||
| 969 | strHost = strHostPort.substr(0, colon); | ||
| 970 | nPort = (unsigned short)n; | ||
| 971 | return true; | ||
| 972 | } | ||
| 973 | |||
| 865 | static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], | 974 | static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], |
| 866 | const string& strMeeting, const unsigned char enc_pub[32]) | 975 | const string& strMeeting, const string& strOnion, |
| 976 | const unsigned char enc_pub[32], | ||
| 977 | const string& strDesc = string()) | ||
| 867 | { | 978 | { |
| 868 | CAddress addr = BtfMarkerAddr(pk); | 979 | CAddress addr = BtfMarkerAddr(pk); |
| 869 | CNode* pnode = FindNode(addr.ip); | 980 | CNode* pnode = FindNode(addr.ip); |
| @@ -873,16 +984,54 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p | |||
| 873 | return pnode; | 984 | return pnode; |
| 874 | } | 985 | } |
| 875 | 986 | ||
| 876 | BtfChurnNoteDialAttempt(strBtfAddr, strMeeting); | 987 | string strOnionNorm; |
| 877 | size_t colon = strMeeting.rfind(':'); | 988 | if (BtfSocks5ProxyEnabled() && btf::NormalizeOnionEndpoint(strOnion, strOnionNorm)) |
| 878 | if (colon == string::npos) | ||
| 879 | { | 989 | { |
| 880 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | 990 | string strOnionHost; |
| 991 | unsigned short nOnionPort = 0; | ||
| 992 | if (SplitHostPort(strOnionNorm, strOnionHost, nOnionPort)) | ||
| 993 | { | ||
| 994 | BtfChurnNoteDialAttempt(strBtfAddr, strOnionNorm); | ||
| 995 | SOCKET hOnionSocket = BtfConnectSocket(strOnionHost, nOnionPort, SOCK_ONION_PEER, | ||
| 996 | BTF_ONION_CONNECT_TIMEOUT_SECS); | ||
| 997 | if (hOnionSocket != INVALID_SOCKET) | ||
| 998 | { | ||
| 999 | if (fDebug) | ||
| 1000 | LogPrint("net", "connected %s via direct onion %s\n", | ||
| 1001 | strBtfAddr.c_str(), strOnionNorm.c_str()); | ||
| 1002 | BtfChurnNoteDialResult(strBtfAddr, strOnionNorm, true); | ||
| 1003 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub, strDesc, strOnionNorm); | ||
| 1004 | |||
| 1005 | pnode = new CNode(hOnionSocket, addr, false); | ||
| 1006 | pnode->strBtfAddr = strBtfAddr; | ||
| 1007 | pnode->strBtfMeeting = string("onion ") + strOnionNorm; | ||
| 1008 | pnode->AddRef(); | ||
| 1009 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1010 | vNodes.push_back(pnode); | ||
| 1011 | return pnode; | ||
| 1012 | } | ||
| 1013 | if (fDebug) | ||
| 1014 | LogPrint("net", "ConnectNodeBtf: direct onion to %s at %s failed%s\n", | ||
| 1015 | strBtfAddr.c_str(), strOnionNorm.c_str(), | ||
| 1016 | fBtfOnionOnly ? "" : ", falling back to rendezvous"); | ||
| 1017 | BtfChurnNoteDialResult(strBtfAddr, strOnionNorm, false); | ||
| 1018 | if (fBtfOnionOnly) | ||
| 1019 | return NULL; | ||
| 1020 | } | ||
| 1021 | } | ||
| 1022 | |||
| 1023 | if (fBtfOnionOnly) | ||
| 1024 | { | ||
| 1025 | if (fDebug) | ||
| 1026 | LogPrint("net", "ConnectNodeBtf: onion-only mode refused rendezvous fallback for %s\n", | ||
| 1027 | strBtfAddr.c_str()); | ||
| 881 | return NULL; | 1028 | return NULL; |
| 882 | } | 1029 | } |
| 883 | string strHost = strMeeting.substr(0, colon); | 1030 | |
| 884 | int nPort = atoi(strMeeting.substr(colon + 1).c_str()); | 1031 | BtfChurnNoteDialAttempt(strBtfAddr, strMeeting); |
| 885 | if (nPort <= 0 || nPort > 65535) | 1032 | string strHost; |
| 1033 | unsigned short nPort = 0; | ||
| 1034 | if (!SplitHostPort(strMeeting, strHost, nPort)) | ||
| 886 | { | 1035 | { |
| 887 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); | 1036 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, false); |
| 888 | return NULL; | 1037 | return NULL; |
| @@ -902,7 +1051,7 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p | |||
| 902 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, true); | 1051 | BtfChurnNoteDialResult(strBtfAddr, strMeeting, true); |
| 903 | 1052 | ||
| 904 | // This one answered -- worth trying first next time we start. | 1053 | // This one answered -- worth trying first next time we start. |
| 905 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub); | 1054 | RememberBtfPeer(strBtfAddr, strMeeting, enc_pub, strDesc, strOnionNorm); |
| 906 | 1055 | ||
| 907 | // Add node | 1056 | // Add node |
| 908 | pnode = new CNode(hSocket, addr, false); | 1057 | pnode = new CNode(hSocket, addr, false); |
| @@ -932,9 +1081,10 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 932 | LogPrint("net", "trying %s\n", strBtfAddr.c_str()); | 1081 | LogPrint("net", "trying %s\n", strBtfAddr.c_str()); |
| 933 | 1082 | ||
| 934 | string strMeeting; | 1083 | string strMeeting; |
| 1084 | string strOnion; | ||
| 935 | unsigned char enc_pub[32]; | 1085 | unsigned char enc_pub[32]; |
| 936 | BtfChurnNoteResolveAttempt(); | 1086 | BtfChurnNoteResolveAttempt(); |
| 937 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) | 1087 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub, &strOnion)) |
| 938 | { | 1088 | { |
| 939 | BtfChurnNoteResolveResult(false); | 1089 | BtfChurnNoteResolveResult(false); |
| 940 | if (fDebug) | 1090 | if (fDebug) |
| @@ -942,7 +1092,7 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 942 | return NULL; | 1092 | return NULL; |
| 943 | } | 1093 | } |
| 944 | BtfChurnNoteResolveResult(true); | 1094 | BtfChurnNoteResolveResult(true); |
| 945 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); | 1095 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, strOnion, enc_pub); |
| 946 | } | 1096 | } |
| 947 | 1097 | ||
| 948 | // Same as ConnectNodeBtf, but skips the Nostr resolve step because the caller | 1098 | // Same as ConnectNodeBtf, but skips the Nostr resolve step because the caller |
| @@ -951,7 +1101,8 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 951 | // address). Makes zero relay connections -- only talks to the peer's | 1101 | // address). Makes zero relay connections -- only talks to the peer's |
| 952 | // rendezvous meeting node. | 1102 | // rendezvous meeting node. |
| 953 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, | 1103 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, |
| 954 | const unsigned char enc_pub[32]) | 1104 | const string& strOnion, const unsigned char enc_pub[32], |
| 1105 | const string& strDesc) | ||
| 955 | { | 1106 | { |
| 956 | unsigned char pk[32]; | 1107 | unsigned char pk[32]; |
| 957 | if (!btf::ParseAddress(strBtfAddr, pk)) | 1108 | if (!btf::ParseAddress(strBtfAddr, pk)) |
| @@ -961,7 +1112,7 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting | |||
| 961 | } | 1112 | } |
| 962 | if (BtfLocalAddress() == strBtfAddr) | 1113 | if (BtfLocalAddress() == strBtfAddr) |
| 963 | return NULL; // ourselves | 1114 | return NULL; // ourselves |
| 964 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); | 1115 | return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, strOnion, enc_pub, strDesc); |
| 965 | } | 1116 | } |
| 966 | 1117 | ||
| 967 | // Anonymous inbound listener (this node's `.btf` hidden service). Registers at | 1118 | // Anonymous inbound listener (this node's `.btf` hidden service). Registers at |
| @@ -1127,7 +1278,7 @@ void CNode::Disconnect() | |||
| 1127 | 1278 | ||
| 1128 | // Invalidate before anything else can look at it. This used to close the | 1279 | // 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 | 1280 | // 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 | 1281 | // closed it a second time and the socket loop kept arming it in the |
| 1131 | // meantime. See the note on ~CNode in net.h. | 1282 | // meantime. See the note on ~CNode in net.h. |
| 1132 | BtfCloseSocket(hSocket); | 1283 | BtfCloseSocket(hSocket); |
| 1133 | hSocket = INVALID_SOCKET; | 1284 | hSocket = INVALID_SOCKET; |
| @@ -1276,52 +1427,49 @@ void ThreadSocketHandler2(void* parg) | |||
| 1276 | // | 1427 | // |
| 1277 | // Find which sockets have data to receive | 1428 | // Find which sockets have data to receive |
| 1278 | // | 1429 | // |
| 1279 | struct timeval timeout; | 1430 | static const int nPollTimeoutMs = 50; // frequency to poll pnode->vSend |
| 1280 | timeout.tv_sec = 0; | 1431 | |
| 1281 | timeout.tv_usec = 50000; // frequency to poll pnode->vSend | 1432 | struct NodePollResult |
| 1282 | 1433 | { | |
| 1283 | fd_set fdsetRecv; | 1434 | CNode* pnode; |
| 1284 | fd_set fdsetSend; | 1435 | SOCKET hSocket; |
| 1285 | FD_ZERO(&fdsetRecv); | 1436 | bool fRecv; |
| 1286 | FD_ZERO(&fdsetSend); | 1437 | bool fSend; |
| 1287 | SOCKET hSocketMax = 0; | 1438 | bool fInvalid; |
| 1288 | FD_SET(hListenSocket, &fdsetRecv); | 1439 | NodePollResult(CNode* pnodeIn, SOCKET hSocketIn) |
| 1289 | hSocketMax = max(hSocketMax, hListenSocket); | 1440 | : 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 | 1441 | }; |
| 1291 | // and say so. A peer that never makes it into the set is never read, | 1442 | |
| 1292 | // and that is indistinguishable from a peer with nothing to say -- | 1443 | vector<BtfPollFd> vPoll; |
| 1293 | // the failure this whole path used to hide. | 1444 | vector<NodePollResult> vPollNodes; |
| 1294 | unsigned int nWatched = 1; // the listen socket | 1445 | |
| 1446 | BtfPollFd listenPoll; | ||
| 1447 | memset(&listenPoll, 0, sizeof(listenPoll)); | ||
| 1448 | listenPoll.fd = hListenSocket; | ||
| 1449 | listenPoll.events = POLLIN; | ||
| 1450 | vPoll.push_back(listenPoll); | ||
| 1451 | |||
| 1295 | CRITICAL_BLOCK(cs_vNodes) | 1452 | CRITICAL_BLOCK(cs_vNodes) |
| 1296 | { | 1453 | { |
| 1297 | foreach(CNode* pnode, vNodes) | 1454 | foreach(CNode* pnode, vNodes) |
| 1298 | { | 1455 | { |
| 1299 | if (nWatched >= FD_SETSIZE) | ||
| 1300 | break; | ||
| 1301 | // A disconnected node stays in vNodes until its references go. | 1456 | // A disconnected node stays in vNodes until its references go. |
| 1302 | // Arming its closed handle asks select() to watch a number the | 1457 | // Do not watch a closed handle number: the OS may already have |
| 1303 | // operating system may already have given to somebody else. | 1458 | // handed it to a different connection. |
| 1304 | if (pnode->hSocket == INVALID_SOCKET) | 1459 | if (pnode->hSocket == INVALID_SOCKET) |
| 1305 | continue; | 1460 | continue; |
| 1306 | FD_SET(pnode->hSocket, &fdsetRecv); | 1461 | |
| 1307 | nWatched++; | 1462 | BtfPollFd pfd; |
| 1308 | hSocketMax = max(hSocketMax, pnode->hSocket); | 1463 | memset(&pfd, 0, sizeof(pfd)); |
| 1464 | pfd.fd = pnode->hSocket; | ||
| 1465 | pfd.events = POLLIN; | ||
| 1309 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1466 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1310 | if (!pnode->vSend.empty()) | 1467 | if (!pnode->vSend.empty()) |
| 1311 | FD_SET(pnode->hSocket, &fdsetSend); | 1468 | pfd.events |= POLLOUT; |
| 1469 | vPoll.push_back(pfd); | ||
| 1470 | vPollNodes.push_back(NodePollResult(pnode, pnode->hSocket)); | ||
| 1312 | } | 1471 | } |
| 1313 | if (vNodes.size() + 1 > (size_t)FD_SETSIZE) | 1472 | nPeersWatched = (int)vPollNodes.size(); |
| 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 | } | ||
| 1324 | nPeersWatched = (int)nWatched - 1; // less the listen socket | ||
| 1325 | } | 1473 | } |
| 1326 | 1474 | ||
| 1327 | // Say the state of the node out loud now and then, so a log pulled off | 1475 | // Say the state of the node out loud now and then, so a log pulled off |
| @@ -1342,32 +1490,17 @@ void ThreadSocketHandler2(void* parg) | |||
| 1342 | } | 1490 | } |
| 1343 | 1491 | ||
| 1344 | vfThreadRunning[0] = false; | 1492 | vfThreadRunning[0] = false; |
| 1345 | int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, NULL, &timeout); | 1493 | int nPoll = BtfPoll(&vPoll[0], (unsigned int)vPoll.size(), nPollTimeoutMs); |
| 1346 | vfThreadRunning[0] = true; | 1494 | vfThreadRunning[0] = true; |
| 1347 | CheckForShutdown(0); | 1495 | CheckForShutdown(0); |
| 1348 | if (nSelect == SOCKET_ERROR) | 1496 | if (nPoll == SOCKET_ERROR) |
| 1349 | { | 1497 | { |
| 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(); | 1498 | int nErr = WSAGetLastError(); |
| 1361 | LogPrint("net", "select failed: %d\n", nErr); | 1499 | LogPrint("net", "poll failed: %d\n", nErr); |
| 1362 | FD_ZERO(&fdsetRecv); | ||
| 1363 | FD_ZERO(&fdsetSend); | ||
| 1364 | CRITICAL_BLOCK(cs_vNodes) | 1500 | CRITICAL_BLOCK(cs_vNodes) |
| 1365 | { | 1501 | { |
| 1366 | foreach(CNode* pnode, vNodes) | 1502 | foreach(CNode* pnode, vNodes) |
| 1367 | { | 1503 | { |
| 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) | 1504 | if (pnode->hSocket == INVALID_SOCKET) |
| 1372 | continue; | 1505 | continue; |
| 1373 | int nType = 0; | 1506 | int nType = 0; |
| @@ -1376,24 +1509,27 @@ void ThreadSocketHandler2(void* parg) | |||
| 1376 | #else | 1509 | #else |
| 1377 | socklen_t nTypeLen = sizeof(nType); | 1510 | socklen_t nTypeLen = sizeof(nType); |
| 1378 | #endif | 1511 | #endif |
| 1379 | if (pnode->hSocket != INVALID_SOCKET && | 1512 | 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 | { | 1513 | { |
| 1382 | printf("dropping node %s: its socket is no longer valid\n", | 1514 | printf("dropping node %s: its socket is no longer valid\n", |
| 1383 | pnode->addr.ToString().c_str()); | 1515 | pnode->addr.ToString().c_str()); |
| 1384 | pnode->fDisconnect = true; | 1516 | pnode->fDisconnect = true; |
| 1385 | 1517 | ||
| 1386 | // Stop referring to it, or it goes back into the next | 1518 | // Stop referring to it, or it goes into the next poll |
| 1387 | // select() set and fails it again. | 1519 | // set and fails it again. |
| 1388 | // | 1520 | // |
| 1389 | // Marking fDisconnect is not enough: a node is only | 1521 | // Marking fDisconnect is not enough: a node is only |
| 1390 | // removed from vNodes once it has drained or once | 1522 | // removed from vNodes once it has drained or once |
| 1391 | // DISCONNECT_DRAIN_SECS has passed, so one dead | 1523 | // DISCONNECT_DRAIN_SECS has passed, so one dead |
| 1392 | // descriptor kept failing select() for a full minute -- | 1524 | // descriptor kept failing the wait for a full minute -- |
| 1393 | // and a failed select() zeroes both sets above, which | 1525 | // and a failed wait skips the service loop below |
| 1394 | // means NO peer is read on that pass. Measured on a | 1526 | // entirely, which means NO peer is read on that pass. |
| 1395 | // wedged pool: 1,027 consecutive passes over 61 | 1527 | // Measured on a wedged pool under the old select() |
| 1396 | // seconds, one bad socket, the entire node deaf. | 1528 | // implementation: 1,027 consecutive passes over 61 |
| 1529 | // seconds, one bad socket, the entire node deaf. poll() | ||
| 1530 | // reports a bad descriptor as POLLNVAL on that entry | ||
| 1531 | // rather than failing the whole call, so this path is | ||
| 1532 | // now the backstop rather than the common case. | ||
| 1397 | // | 1533 | // |
| 1398 | // Not closed, only forgotten. getsockopt just told us | 1534 | // Not closed, only forgotten. getsockopt just told us |
| 1399 | // this descriptor is not ours any more; closing it now | 1535 | // this descriptor is not ours any more; closing it now |
| @@ -1402,7 +1538,17 @@ void ThreadSocketHandler2(void* parg) | |||
| 1402 | } | 1538 | } |
| 1403 | } | 1539 | } |
| 1404 | } | 1540 | } |
| 1405 | Sleep(timeout.tv_usec/1000); | 1541 | Sleep(nPollTimeoutMs); |
| 1542 | } | ||
| 1543 | else | ||
| 1544 | { | ||
| 1545 | for (size_t i = 0; i < vPollNodes.size(); i++) | ||
| 1546 | { | ||
| 1547 | short revents = vPoll[i + 1].revents; // poll[0] is the listener | ||
| 1548 | vPollNodes[i].fRecv = (revents & (POLLIN | POLLERR | POLLHUP)) != 0; | ||
| 1549 | vPollNodes[i].fSend = (revents & POLLOUT) != 0; | ||
| 1550 | vPollNodes[i].fInvalid = (revents & POLLNVAL) != 0; | ||
| 1551 | } | ||
| 1406 | } | 1552 | } |
| 1407 | RandAddSeed(); | 1553 | RandAddSeed(); |
| 1408 | 1554 | ||
| @@ -1418,7 +1564,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1418 | // | 1564 | // |
| 1419 | // Accept new connections | 1565 | // Accept new connections |
| 1420 | // | 1566 | // |
| 1421 | if (FD_ISSET(hListenSocket, &fdsetRecv)) | 1567 | if (nPoll != SOCKET_ERROR && (vPoll[0].revents & POLLIN)) |
| 1422 | { | 1568 | { |
| 1423 | struct sockaddr_in sockaddr; | 1569 | struct sockaddr_in sockaddr; |
| 1424 | #ifdef _WIN32 | 1570 | #ifdef _WIN32 |
| @@ -1437,18 +1583,47 @@ void ThreadSocketHandler2(void* parg) | |||
| 1437 | { | 1583 | { |
| 1438 | // Refuse rather than accept a connection this node cannot | 1584 | // Refuse rather than accept a connection this node cannot |
| 1439 | // watch. Without this the listen socket kept taking peers -- | 1585 | // watch. Without this the listen socket kept taking peers -- |
| 1440 | // it is always in the select set -- while everything past the | 1586 | // it is always watched -- while everything past the |
| 1441 | // limit sat open and unread, which fed on itself: the deafer | 1587 | // limit sat open and unread, which fed on itself: the deafer |
| 1442 | // the node got, the more connections it collected. | 1588 | // the node got, the more connections it collected. |
| 1443 | unsigned int nNodes = 0; | 1589 | unsigned int nNodes = 0; |
| 1444 | CRITICAL_BLOCK(cs_vNodes) | 1590 | CRITICAL_BLOCK(cs_vNodes) |
| 1445 | nNodes = (unsigned int)vNodes.size(); | 1591 | nNodes = (unsigned int)vNodes.size(); |
| 1592 | // Also count how many connections this one address already | ||
| 1593 | // holds. A single IP that keeps opening sockets -- a broken | ||
| 1594 | // node or a deliberate connection-exhaustion flood -- must not | ||
| 1595 | // be able to take the whole table, which is what starved the | ||
| 1596 | // relays. | ||
| 1597 | // | ||
| 1598 | // Loopback is exempt. Onion peers reach a managed-Tor node | ||
| 1599 | // through its local hidden-service listener, so every one of | ||
| 1600 | // them accepts as 127.0.0.1; capping loopback would cap all | ||
| 1601 | // inbound onion peers as a single group, which is the opposite | ||
| 1602 | // of where the network is headed. The global MAX_CONNECTIONS | ||
| 1603 | // still bounds the total, and the direct-IP flood this defends | ||
| 1604 | // against never arrives over loopback. (Per-onion-identity | ||
| 1605 | // limiting, once a peer's identity is known past accept(), is | ||
| 1606 | // the right tool for onion and is left for later.) | ||
| 1607 | bool fLoopback = (addr.GetByte(3) == 127); | ||
| 1608 | unsigned int nFromThisIP = 0; | ||
| 1609 | if (!fLoopback) | ||
| 1610 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1611 | for (vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) | ||
| 1612 | if ((*it)->addr.ip == addr.ip) | ||
| 1613 | nFromThisIP++; | ||
| 1614 | |||
| 1446 | if (nNodes >= MAX_CONNECTIONS) | 1615 | if (nNodes >= MAX_CONNECTIONS) |
| 1447 | { | 1616 | { |
| 1448 | LogPrint("net", "refusing connection from %s, already at %u\n", | 1617 | LogPrint("net", "refusing connection from %s, already at %u\n", |
| 1449 | addr.ToString().c_str(), nNodes); | 1618 | addr.ToString().c_str(), nNodes); |
| 1450 | BtfCloseSocket(hSocket); | 1619 | BtfCloseSocket(hSocket); |
| 1451 | } | 1620 | } |
| 1621 | else if (!fLoopback && nFromThisIP >= MAX_CONNECTIONS_PER_IP) | ||
| 1622 | { | ||
| 1623 | LogPrint("net", "refusing connection from %s, already %u from that address\n", | ||
| 1624 | addr.ToString().c_str(), nFromThisIP); | ||
| 1625 | BtfCloseSocket(hSocket); | ||
| 1626 | } | ||
| 1452 | else | 1627 | else |
| 1453 | { | 1628 | { |
| 1454 | LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str()); | 1629 | LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str()); |
| @@ -1464,22 +1639,28 @@ void ThreadSocketHandler2(void* parg) | |||
| 1464 | // | 1639 | // |
| 1465 | // Service each socket | 1640 | // Service each socket |
| 1466 | // | 1641 | // |
| 1467 | vector<CNode*> vNodesCopy; | 1642 | for (size_t i = 0; i < vPollNodes.size(); i++) |
| 1468 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1469 | vNodesCopy = vNodes; | ||
| 1470 | foreach(CNode* pnode, vNodesCopy) | ||
| 1471 | { | 1643 | { |
| 1472 | CheckForShutdown(0); | 1644 | CheckForShutdown(0); |
| 1473 | SOCKET hSocket = pnode->hSocket; | 1645 | NodePollResult& poll = vPollNodes[i]; |
| 1474 | // Forgotten above because it was already invalid. FD_ISSET on it | 1646 | CNode* pnode = poll.pnode; |
| 1475 | // is meaningless and recv()/send() would be operating on -1. | 1647 | SOCKET hSocket = poll.hSocket; |
| 1476 | if (hSocket == INVALID_SOCKET) | 1648 | |
| 1649 | if (hSocket == INVALID_SOCKET || pnode->hSocket != hSocket) | ||
| 1477 | continue; | 1650 | continue; |
| 1478 | 1651 | ||
| 1652 | if (poll.fInvalid) | ||
| 1653 | { | ||
| 1654 | LogPrint("net", "poll reported invalid socket for %s\n", | ||
| 1655 | pnode->addr.ToString().c_str()); | ||
| 1656 | pnode->fDisconnect = true; | ||
| 1657 | continue; | ||
| 1658 | } | ||
| 1659 | |||
| 1479 | // | 1660 | // |
| 1480 | // Receive | 1661 | // Receive |
| 1481 | // | 1662 | // |
| 1482 | if (FD_ISSET(hSocket, &fdsetRecv)) | 1663 | if (poll.fRecv) |
| 1483 | { | 1664 | { |
| 1484 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) | 1665 | TRY_CRITICAL_BLOCK(pnode->cs_vRecv) |
| 1485 | { | 1666 | { |
| @@ -1519,7 +1700,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1519 | // | 1700 | // |
| 1520 | // Send | 1701 | // Send |
| 1521 | // | 1702 | // |
| 1522 | if (FD_ISSET(hSocket, &fdsetSend)) | 1703 | if (poll.fSend) |
| 1523 | { | 1704 | { |
| 1524 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) | 1705 | TRY_CRITICAL_BLOCK(pnode->cs_vSend) |
| 1525 | { | 1706 | { |
| @@ -1915,6 +2096,14 @@ bool StartNode(string& strError) | |||
| 1915 | // IP address, and port for the socket that is being bound | 2096 | // IP address, and port for the socket that is being bound |
| 1916 | int nRetryLimit = 15; | 2097 | int nRetryLimit = 15; |
| 1917 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); | 2098 | struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); |
| 2099 | // Managed Tor forwards hidden-service traffic to 127.0.0.1:<port>, which a | ||
| 2100 | // LAN-only bind cannot receive -- so a node that advertises an onion | ||
| 2101 | // endpoint must listen on all local interfaces. Upstream 1.2.19 bound | ||
| 2102 | // INADDR_ANY unconditionally, which also widened the P2P listener of every | ||
| 2103 | // non-Tor node to VPN tunnels, docker bridges and secondary NICs. Keep the | ||
| 2104 | // wildcard bind conditional on actually having an onion endpoint. | ||
| 2105 | if (!BtfLocalOnionEndpoint().empty()) | ||
| 2106 | sockaddr.sin_addr.s_addr = INADDR_ANY; | ||
| 1918 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) | 2107 | if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) |
| 1919 | { | 2108 | { |
| 1920 | int nErr = WSAGetLastError(); | 2109 | int nErr = WSAGetLastError(); |
| @@ -1959,7 +2148,7 @@ bool StartNode(string& strError) | |||
| 1959 | if (addrIncoming.ip) | 2148 | if (addrIncoming.ip) |
| 1960 | addrLocalHost.ip = addrIncoming.ip; | 2149 | addrLocalHost.ip = addrIncoming.ip; |
| 1961 | 2150 | ||
| 1962 | _beginthread([](void*) { | 2151 | if (_beginthread([](void*) { |
| 1963 | unsigned int ip = addrLocalHost.ip; | 2152 | unsigned int ip = addrLocalHost.ip; |
| 1964 | if (GetMyExternalIP(ip)) { | 2153 | if (GetMyExternalIP(ip)) { |
| 1965 | addrLocalHost.ip = ip; | 2154 | addrLocalHost.ip = ip; |
| @@ -1967,7 +2156,8 @@ bool StartNode(string& strError) | |||
| 1967 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | 2156 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); |
| 1968 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); | 2157 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); |
| 1969 | } | 2158 | } |
| 1970 | }, 0, NULL); | 2159 | }, 0, NULL) == (uintptr_t)-1) |
| 2160 | printf("Error: _beginthread(GetMyExternalIP) failed\n"); | ||
| 1971 | 2161 | ||
| 1972 | // Peers that answered last time, dialled straight away. The relays are the | 2162 | // Peers that answered last time, dialled straight away. The relays are the |
| 1973 | // only way to find anyone otherwise, and reaching them plus walking their | 2163 | // only way to find anyone otherwise, and reaching them plus walking their |