diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
| commit | ceae3d076b5ad11ee84729ae43f14e9312896646 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/net.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | 66e0b9158d050c46b1ff2509faeee3f4573cac1c (diff) | |
Bring main onto the line this project actually ships
main had drifted to a history with no ancestor in common with the tree we
build and deploy. It sat at be63e83 (2026-08-09) with 28 commits; the shipped
line, deploy/soak-2026-08-21, had 383. The two shared nothing -- git
merge-base returns no result at all -- so anyone cloning this repository
landed on a default branch that was neither current nor related to the
running node.
The 28 commits on main were audited before this merge: every one is already
present in the tree by content, including the two that are hardest to see
because they were rewritten afterwards -- the payout reorg-safety work
(PayoutBlockStillOnChain, since evolved to re-check on every retry rather
than once at maturity) and the nostr relay certificate verification
(SSL_set1_host, now with a Windows root-store path). Nothing on main is lost
by this merge.
Recorded as a merge rather than a force-push: main only ever moves forward,
its old history stays reachable as the first parent, and this is revertible.
The tree is taken wholesale from the merged branch, so main's content is now
exactly what was built and tested.
What this brings onto main:
- compactblock: BIP152-style compact block relay ported from upstream 1.2.23,
primitives and unit test only, with the RAND_bytes failure path closed and
the reconstruction allocation bounded.
- upstream/main 17741d9: isolated testnet parameters and genesis, optional
2 MB large pages for RandomX. Mainnet genesis verified byte-identical
through upstream's BuildGenesisBlock() refactor.
- deploy: every image built on bookworm so binaries run on Debian 12, and the
container build now honours the RandomX and libsecp256k1 pins it had been
silently ignoring since before 1.2.15.
Gates at this tree: 15/15 selftests, compactblock's 17 checks, consensus
constants unchanged against e4f898c apart from the pre-existing
MAX_BLOCK_SIZE_GEN, mainnet genesis intact, money-log check ok.
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 |