diff options
| author | Bitflash <btf01@proton.me> | 2026-08-01 13:10:23 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 13:10:23 -0300 |
| commit | 21c8bb63cb85f3001ddee9237b1d7e689675d293 (patch) | |
| tree | 9668f6bf39635424a6180c7a7edb6c7931ea7545 /src/net.cpp | |
| parent | 7131c30c33c980f0ba1d37dfc6a3fbe8c319813e (diff) | |
diag: count sockets by where they were created, and whether the close took (#86)
A node was found holding 1502 sockets in Windows bound state, one ephemeral port
each, 9% of the machine range in nineteen hours. Reading the code did not find
it: every connect() failure path here closes its socket.
So count. Every socket is tagged at creation and untagged at close, across all
ten sites, with a counter for closes that fail. Already rules out three
explanations: nothing is created or closed behind the counter (untagged closes
are zero), no close is failing, and the leaked sockets are not in the live set --
tracked live matches what the OS reports as established, while the bound pile is
something the program already closed successfully.
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/src/net.cpp b/src/net.cpp index 19893ee..7a13257 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -51,6 +51,33 @@ int64 nBlocksReceived = 0; | |||
| 51 | int64 nBlocksWithoutParent = 0; | 51 | int64 nBlocksWithoutParent = 0; |
| 52 | static int64 nNodeStartTime = 0; | 52 | static int64 nNodeStartTime = 0; |
| 53 | 53 | ||
| 54 | // --- Socket accounting (implementation lives in sockcount.h) -------------- | ||
| 55 | static const char* pszSockSite[SOCK_SITES] = { | ||
| 56 | "external-ip probe", "listen socket", "inbound accept", | ||
| 57 | "rendezvous dial", "rendezvous listen", "rendezvous accept", | ||
| 58 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", | ||
| 59 | "nostr relay" | ||
| 60 | }; | ||
| 61 | |||
| 62 | static string SockAccountingText() | ||
| 63 | { | ||
| 64 | long long nOpened[SOCK_SITES], nClosed[SOCK_SITES], nFailed[SOCK_SITES], nUntagged = 0; | ||
| 65 | BtfSockSnapshot(nOpened, nClosed, nFailed, &nUntagged); | ||
| 66 | |||
| 67 | string str = "\n sockets by where they were created\n"; | ||
| 68 | str += " site opened closed live close failed\n"; | ||
| 69 | for (int i = 0; i < SOCK_SITES; i++) | ||
| 70 | { | ||
| 71 | if (nOpened[i] == 0) | ||
| 72 | continue; | ||
| 73 | str += strprintf(" %-24s %8lld %8lld %8lld %13lld\n", pszSockSite[i], | ||
| 74 | nOpened[i], nClosed[i], nOpened[i] - nClosed[i], nFailed[i]); | ||
| 75 | } | ||
| 76 | str += strprintf(" %-24s %8s %8lld %8s %13s\n", "closed but never tagged", | ||
| 77 | "-", nUntagged, "-", "-"); | ||
| 78 | return str; | ||
| 79 | } | ||
| 80 | |||
| 54 | // Defined further down, next to the counter it reads. The miner keeps its own | 81 | // Defined further down, next to the counter it reads. The miner keeps its own |
| 55 | // live count -- threads bump it on the way in and on the way out -- which is | 82 | // live count -- threads bump it on the way in and on the way out -- which is |
| 56 | // the honest number here: how many are hashing now, not how many were asked to. | 83 | // the honest number here: how many are hashing now, not how many were asked to. |
| @@ -119,6 +146,8 @@ string GetDiagnosticsText() | |||
| 119 | (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() | 146 | (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() |
| 120 | : ", not mining"); | 147 | : ", not mining"); |
| 121 | 148 | ||
| 149 | str += SockAccountingText(); | ||
| 150 | |||
| 122 | str += "\n peer dir height last recv last send unsent\n"; | 151 | str += "\n peer dir height last recv last send unsent\n"; |
| 123 | foreach(CNode* pnode, vCopy) | 152 | foreach(CNode* pnode, vCopy) |
| 124 | { | 153 | { |
| @@ -157,7 +186,7 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 157 | if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) | 186 | if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) |
| 158 | continue; | 187 | continue; |
| 159 | 188 | ||
| 160 | SOCKET hSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); | 189 | SOCKET hSocket = BtfSocketTag(socket(res->ai_family, res->ai_socktype, res->ai_protocol), SOCK_EXTIP); |
| 161 | if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } | 190 | if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } |
| 162 | 191 | ||
| 163 | // 5-second timeout so a dead service doesn't stall startup | 192 | // 5-second timeout so a dead service doesn't stall startup |
| @@ -173,7 +202,7 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 173 | 202 | ||
| 174 | if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) | 203 | if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) |
| 175 | { | 204 | { |
| 176 | freeaddrinfo(res); closesocket(hSocket); continue; | 205 | freeaddrinfo(res); BtfCloseSocket(hSocket); continue; |
| 177 | } | 206 | } |
| 178 | freeaddrinfo(res); | 207 | freeaddrinfo(res); |
| 179 | 208 | ||
| @@ -190,7 +219,7 @@ bool GetMyExternalIP(unsigned int& ipRet) | |||
| 190 | response += buf; | 219 | response += buf; |
| 191 | if (response.size() > 4096) break; | 220 | if (response.size() > 4096) break; |
| 192 | } | 221 | } |
| 193 | closesocket(hSocket); | 222 | BtfCloseSocket(hSocket); |
| 194 | 223 | ||
| 195 | // Find blank line separating headers from body | 224 | // Find blank line separating headers from body |
| 196 | size_t bodyPos = response.find("\r\n\r\n"); | 225 | size_t bodyPos = response.find("\r\n\r\n"); |
| @@ -950,7 +979,7 @@ void CNode::Disconnect() | |||
| 950 | { | 979 | { |
| 951 | LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); | 980 | LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); |
| 952 | 981 | ||
| 953 | closesocket(hSocket); | 982 | BtfCloseSocket(hSocket); |
| 954 | 983 | ||
| 955 | // All of a nodes broadcasts and subscriptions are automatically torn down | 984 | // All of a nodes broadcasts and subscriptions are automatically torn down |
| 956 | // when it goes down, so a node has to stay up to keep its broadcast going. | 985 | // when it goes down, so a node has to stay up to keep its broadcast going. |
| @@ -1230,7 +1259,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1230 | #else | 1259 | #else |
| 1231 | socklen_t len = sizeof(sockaddr); | 1260 | socklen_t len = sizeof(sockaddr); |
| 1232 | #endif | 1261 | #endif |
| 1233 | SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len); | 1262 | SOCKET hSocket = BtfSocketTag(accept(hListenSocket, (struct sockaddr*)&sockaddr, &len), SOCK_ACCEPT); |
| 1234 | CAddress addr(sockaddr); | 1263 | CAddress addr(sockaddr); |
| 1235 | if (hSocket == INVALID_SOCKET) | 1264 | if (hSocket == INVALID_SOCKET) |
| 1236 | { | 1265 | { |
| @@ -1251,7 +1280,7 @@ void ThreadSocketHandler2(void* parg) | |||
| 1251 | { | 1280 | { |
| 1252 | LogPrint("net", "refusing connection from %s, already at %u\n", | 1281 | LogPrint("net", "refusing connection from %s, already at %u\n", |
| 1253 | addr.ToString().c_str(), nNodes); | 1282 | addr.ToString().c_str(), nNodes); |
| 1254 | closesocket(hSocket); | 1283 | BtfCloseSocket(hSocket); |
| 1255 | } | 1284 | } |
| 1256 | else | 1285 | else |
| 1257 | { | 1286 | { |
| @@ -1639,7 +1668,7 @@ bool StartNode(string& strError) | |||
| 1639 | printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); | 1668 | printf("addrLocalHost = %s\n", addrLocalHost.ToString().c_str()); |
| 1640 | 1669 | ||
| 1641 | // Create socket for listening for incoming connections | 1670 | // Create socket for listening for incoming connections |
| 1642 | SOCKET hListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | 1671 | SOCKET hListenSocket = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), SOCK_LISTEN); |
| 1643 | if (hListenSocket == INVALID_SOCKET) | 1672 | if (hListenSocket == INVALID_SOCKET) |
| 1644 | { | 1673 | { |
| 1645 | strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); | 1674 | strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %d)", WSAGetLastError()); |