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 <btf01@proton.me>2026-07-31 17:40:58 -0300
committerGitHub <noreply@github.com>2026-07-31 17:40:58 -0300
commitd5bcd347cd8d156e3a6d4526fd2470741194a64a (patch)
tree41e0820126fb7a0f1daebc8130054c64a00b9faf /src/net.cpp
parent795e51612bd5b17dbbf8f0cbea64ee4c874b5a33 (diff)
net: a node could only ever watch 63 peers (#78)
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp96
1 files changed, 88 insertions, 8 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 37c081f..5ef8316 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -954,8 +954,27 @@ void ThreadSocketHandler2(void* parg)
954 vector<CNode*> vNodesCopy = vNodes; 954 vector<CNode*> vNodesCopy = vNodes;
955 foreach(CNode* pnode, vNodesCopy) 955 foreach(CNode* pnode, vNodesCopy)
956 { 956 {
957 if (pnode->ReadyToDisconnect() && pnode->vRecv.empty() && pnode->vSend.empty()) 957 // Wait for the buffers to drain before letting a node go, so a
958 // last message still gets out -- but not forever. vSend never
959 // drains through a socket whose far end is gone, and a node
960 // stuck that way stayed in vNodes for the life of the process,
961 // holding its socket and counting as a peer.
962 if (!pnode->ReadyToDisconnect())
963 pnode->nDisconnectSince = 0;
964 else if (pnode->nDisconnectSince == 0)
965 pnode->nDisconnectSince = GetTime();
966
967 bool fDrained = pnode->vRecv.empty() && pnode->vSend.empty();
968 bool fStuck = pnode->nDisconnectSince != 0 &&
969 GetTime() - pnode->nDisconnectSince > DISCONNECT_DRAIN_SECS;
970
971 if (pnode->ReadyToDisconnect() && (fDrained || fStuck))
958 { 972 {
973 if (!fDrained)
974 printf("dropping stuck node %s after %d s with %d bytes unsent\n",
975 pnode->addr.ToString().c_str(),
976 (int)(GetTime() - pnode->nDisconnectSince),
977 (int)pnode->vSend.size());
959 // remove from vNodes 978 // remove from vNodes
960 vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end()); 979 vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
961 pnode->Disconnect(); 980 pnode->Disconnect();
@@ -1010,16 +1029,35 @@ void ThreadSocketHandler2(void* parg)
1010 SOCKET hSocketMax = 0; 1029 SOCKET hSocketMax = 0;
1011 FD_SET(hListenSocket, &fdsetRecv); 1030 FD_SET(hListenSocket, &fdsetRecv);
1012 hSocketMax = max(hSocketMax, hListenSocket); 1031 hSocketMax = max(hSocketMax, hListenSocket);
1032 // FD_SET drops silently once the set is full, so count what goes in
1033 // and say so. A peer that never makes it into the set is never read,
1034 // and that is indistinguishable from a peer with nothing to say --
1035 // the failure this whole path used to hide.
1036 unsigned int nWatched = 1; // the listen socket
1013 CRITICAL_BLOCK(cs_vNodes) 1037 CRITICAL_BLOCK(cs_vNodes)
1014 { 1038 {
1015 foreach(CNode* pnode, vNodes) 1039 foreach(CNode* pnode, vNodes)
1016 { 1040 {
1041 if (nWatched >= FD_SETSIZE)
1042 break;
1017 FD_SET(pnode->hSocket, &fdsetRecv); 1043 FD_SET(pnode->hSocket, &fdsetRecv);
1044 nWatched++;
1018 hSocketMax = max(hSocketMax, pnode->hSocket); 1045 hSocketMax = max(hSocketMax, pnode->hSocket);
1019 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1046 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1020 if (!pnode->vSend.empty()) 1047 if (!pnode->vSend.empty())
1021 FD_SET(pnode->hSocket, &fdsetSend); 1048 FD_SET(pnode->hSocket, &fdsetSend);
1022 } 1049 }
1050 if (vNodes.size() + 1 > (size_t)FD_SETSIZE)
1051 {
1052 static int64 nLastWarned = 0;
1053 if (GetTime() - nLastWarned > 60)
1054 {
1055 nLastWarned = GetTime();
1056 printf("WARNING: %d peers but select() can only watch %d -- %d are not being read\n",
1057 (int)vNodes.size(), (int)FD_SETSIZE - 1,
1058 (int)vNodes.size() - (int)FD_SETSIZE + 1);
1059 }
1060 }
1023 } 1061 }
1024 1062
1025 vfThreadRunning[0] = false; 1063 vfThreadRunning[0] = false;
@@ -1028,12 +1066,37 @@ void ThreadSocketHandler2(void* parg)
1028 CheckForShutdown(0); 1066 CheckForShutdown(0);
1029 if (nSelect == SOCKET_ERROR) 1067 if (nSelect == SOCKET_ERROR)
1030 { 1068 {
1069 // This used to answer a failed select() by marking every
1070 // descriptor from 0 to hSocketMax ready, which is code written for
1071 // POSIX, where a descriptor is a small integer. A Windows SOCKET is
1072 // a kernel handle in the thousands, so the loop iterated over
1073 // numbers that are not sockets and told the rest of the function
1074 // that every peer had data. One bad handle in the set makes
1075 // select() fail on every pass, so a single closed socket put the
1076 // whole thread into that state permanently.
1077 //
1078 // Find the bad handle instead and let it be dropped.
1031 int nErr = WSAGetLastError(); 1079 int nErr = WSAGetLastError();
1032 LogPrint("net", "select failed: %d\n", nErr); 1080 LogPrint("net", "select failed: %d\n", nErr);
1033 for (int i = 0; i <= hSocketMax; i++) 1081 FD_ZERO(&fdsetRecv);
1082 FD_ZERO(&fdsetSend);
1083 CRITICAL_BLOCK(cs_vNodes)
1034 { 1084 {
1035 FD_SET(i, &fdsetRecv); 1085 foreach(CNode* pnode, vNodes)
1036 FD_SET(i, &fdsetSend); 1086 {
1087 int nType = 0;
1088#ifdef _WIN32
1089 int nTypeLen = sizeof(nType);
1090#else
1091 socklen_t nTypeLen = sizeof(nType);
1092#endif
1093 if (getsockopt(pnode->hSocket, SOL_SOCKET, SO_TYPE, (char*)&nType, &nTypeLen) != 0)
1094 {
1095 printf("dropping node %s: its socket is no longer valid\n",
1096 pnode->addr.ToString().c_str());
1097 pnode->fDisconnect = true;
1098 }
1099 }
1037 } 1100 }
1038 Sleep(timeout.tv_usec/1000); 1101 Sleep(timeout.tv_usec/1000);
1039 } 1102 }
@@ -1068,11 +1131,28 @@ void ThreadSocketHandler2(void* parg)
1068 } 1131 }
1069 else 1132 else
1070 { 1133 {
1071 LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str()); 1134 // Refuse rather than accept a connection this node cannot
1072 CNode* pnode = new CNode(hSocket, addr, true); 1135 // watch. Without this the listen socket kept taking peers --
1073 pnode->AddRef(); 1136 // it is always in the select set -- while everything past the
1137 // limit sat open and unread, which fed on itself: the deafer
1138 // the node got, the more connections it collected.
1139 unsigned int nNodes = 0;
1074 CRITICAL_BLOCK(cs_vNodes) 1140 CRITICAL_BLOCK(cs_vNodes)
1075 vNodes.push_back(pnode); 1141 nNodes = (unsigned int)vNodes.size();
1142 if (nNodes >= MAX_CONNECTIONS)
1143 {
1144 LogPrint("net", "refusing connection from %s, already at %u\n",
1145 addr.ToString().c_str(), nNodes);
1146 closesocket(hSocket);
1147 }
1148 else
1149 {
1150 LogPrint("net", "accepted connection from %s\n", addr.ToString().c_str());
1151 CNode* pnode = new CNode(hSocket, addr, true);
1152 pnode->AddRef();
1153 CRITICAL_BLOCK(cs_vNodes)
1154 vNodes.push_back(pnode);
1155 }
1076 } 1156 }
1077 } 1157 }
1078 1158