upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 22:46:52 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 22:46:52 +0000
commitceae3d076b5ad11ee84729ae43f14e9312896646 (patch)
tree7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/net.cpp
parentbe63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff)
parent66e0b9158d050c46b1ff2509faeee3f4573cac1c (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.cpp394
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
21void ThreadReconnectCachedBtfPeers(void* parg); 27void ThreadReconnectCachedBtfPeers(void* parg);
22void ThreadMessageHandler2(void* parg); 28void ThreadMessageHandler2(void* parg);
23void ThreadSocketHandler2(void* parg); 29void ThreadSocketHandler2(void* parg);
24 30
31#ifdef _WIN32
32typedef WSAPOLLFD BtfPollFd;
33static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
34{
35 return WSAPoll(pfd, nfd, nTimeoutMs);
36}
37#else
38typedef struct pollfd BtfPollFd;
39static 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//
53static const char pchMainnetMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd };
54static const char pchTestnetMessageStart[4] = { (char)0xce, (char)0xe2, (char)0xc0, (char)0xff };
55static bool fTestNet = false;
56char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd };
33bool fClient = false; 57bool fClient = false;
34uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); 58uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
35CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); 59CAddress addrLocalHost(0, GetDefaultPort(), nLocalServices);
36unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) 60unsigned short nListenPort = GetDefaultPort(); // local P2P port (tunable via /port)
37CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); 61CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices));
38CNode* pnodeLocalHost = &nodeLocalHost; 62CNode* pnodeLocalHost = &nodeLocalHost;
39bool fShutdown = false; 63bool fShutdown = false;
@@ -45,6 +69,8 @@ deque<pair<int64, CInv> > vRelayExpiration;
45CCriticalSection cs_mapRelay; 69CCriticalSection cs_mapRelay;
46map<CInv, int64> mapAlreadyAskedFor; 70map<CInv, int64> mapAlreadyAskedFor;
47string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) 71string strBtfConnect; // .btf peer to keep connected to (from /connectbtf)
72bool fBtfOnionOnly = false; // require direct onion transport for .btf peers
73static const int BTF_ONION_CONNECT_TIMEOUT_SECS = 120;
48 74
49int nPeersWatched = 0; 75int nPeersWatched = 0;
50int64 nBlocksReceived = 0; 76int64 nBlocksReceived = 0;
@@ -68,6 +94,29 @@ static int64 nBtfHandshakeSilent = 0;
68static string strBtfLastDialFail; 94static string strBtfLastDialFail;
69static string strBtfLastHandshakeTimeout; 95static string strBtfLastHandshakeTimeout;
70 96
97bool IsTestNet()
98{
99 return fTestNet;
100}
101
102unsigned short GetDefaultPort()
103{
104 return htons(fTestNet ? TESTNET_PORT : MAINNET_PORT);
105}
106
107void 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
71void BtfChurnNoteResolveAttempt() 120void 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
159static string SockAccountingText() 208static 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
362bool GetMyExternalIP(unsigned int& ipRet) 436bool 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.
606static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, 686static 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
953static 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
865static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], 974static 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.
953CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, 1103CNode* 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