upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp82
1 files changed, 60 insertions, 22 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 37a15c6..11eb11d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2827,11 +2827,13 @@ bool ProcessMessages(CNode* pfrom)
2827 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", (int)(pstart - vRecv.begin())); 2827 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE SKIPPED %d BYTES\n\n", (int)(pstart - vRecv.begin()));
2828 vRecv.erase(vRecv.begin(), pstart); 2828 vRecv.erase(vRecv.begin(), pstart);
2829 2829
2830 // Read the header from a copy first. The old path consumed the header, 2830 // Read the header from a copy first, and do not consume it until the
2831 // noticed the payload was incomplete, inserted the header back at the 2831 // whole body is here. The old path consumed the header, noticed the
2832 // front of the receive buffer, then slept inside the shared message 2832 // payload was incomplete, inserted the header back at the front of the
2833 // thread. A peer could announce a large payload and drip bytes forever, 2833 // receive buffer, then slept inside the shared message thread. A peer
2834 // making every pass pay for an O(n) insert and a 100 ms stall. 2834 // could announce a large payload and drip bytes forever, making every
2835 // pass pay for an O(n) insert and a 100 ms stall -- a cheap way for one
2836 // peer to slow the thread that serves all of them. (upstream #109)
2835 CMessageHeader hdr; 2837 CMessageHeader hdr;
2836 CDataStream vHeader(vRecv.begin(), vRecv.begin() + sizeof(CMessageHeader), 2838 CDataStream vHeader(vRecv.begin(), vRecv.begin() + sizeof(CMessageHeader),
2837 vRecv.nType, vRecv.nVersion); 2839 vRecv.nType, vRecv.nVersion);
@@ -2841,7 +2843,10 @@ bool ProcessMessages(CNode* pfrom)
2841 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str()); 2843 if (LogAcceptsCategory("net")) printf("\n\nPROCESSMESSAGE: ERRORS IN HEADER %s\n\n\n", hdr.GetCommand().c_str());
2842 if (hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_SIZE) 2844 if (hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_SIZE)
2843 pfrom->fDisconnect = true; 2845 pfrom->fDisconnect = true;
2844 pfrom->nIncompleteMessageStart = 0; 2846 pfrom->nMsgWaitSince = 0;
2847 pfrom->nMsgWaitSize = 0;
2848 // The header was read from a copy, so it is still in vRecv. Skip it
2849 // explicitly or this loop never advances.
2845 vRecv.ignore(sizeof(CMessageHeader)); 2850 vRecv.ignore(sizeof(CMessageHeader));
2846 continue; 2851 continue;
2847 } 2852 }
@@ -2852,30 +2857,63 @@ bool ProcessMessages(CNode* pfrom)
2852 unsigned int nPayloadAvailable = vRecv.size() - sizeof(CMessageHeader); 2857 unsigned int nPayloadAvailable = vRecv.size() - sizeof(CMessageHeader);
2853 if (nMessageSize > nPayloadAvailable) 2858 if (nMessageSize > nPayloadAvailable)
2854 { 2859 {
2855 if (pfrom->nIncompleteMessageStart == 0 || 2860 // Wait for the rest of the message. The header stays in vRecv.
2856 pfrom->nIncompleteMessageSize != nMessageSize || 2861 //
2857 pfrom->strIncompleteMessageCommand != strCommand) 2862 // This is the mechanism the original note asked for: "need a
2863 // mechanism to give up waiting for overlong message size error".
2864 // Without one the wait is unbounded, and unbounded here means
2865 // permanent. The header stays at the front of vRecv and the loop
2866 // breaks; if the body never completes, every later pass re-reads
2867 // that same header and breaks in the same place. The peer is never
2868 // parsed again -- no blocks, no inv, not even its version message,
2869 // so it never reports a height either, which is why a wedged node
2870 // has peers and GetPeerMedianHeight() still returns -1.
2871 //
2872 // Nothing rescues it. A header could claim up to 256 MB and still
2873 // pass CMessageHeader::IsValid() -- now capped at
2874 // MAX_PROTOCOL_MESSAGE_SIZE -- and any byte that arrives
2875 // refreshes nLastRecv, so the inactivity timeout that drops a
2876 // silent peer never fires on one that is merely unreadable. The
2877 // socket stays ESTABLISHED and select() keeps offering it.
2878 // Measured on a wedged pool: this branch ran 2014 times in the
2879 // deaf window, against 607 in the healthy window before it.
2880 if (LogAcceptsCategory("net")) printf("MESSAGE-BREAK 2\n");
2881 //
2882 // Time the STALL, not the wait. A big message on a slow link is
2883 // still arriving, and dropping that peer would be a bug of our own
2884 // making; what must be caught is a buffer that has stopped growing
2885 // while we go on expecting more. So the clock restarts whenever
2886 // even one more byte has landed since the last look.
2887 //
2888 // Upstream #109 times this differently: it restarts the clock only
2889 // when the (size, command) pair changes, which drops a peer sending
2890 // one big message slowly but honestly. Progress is the better test,
2891 // so that half is kept as it was.
2892 int64 nNow = GetTime();
2893 if (pfrom->nMsgWaitSince == 0 || nPayloadAvailable > pfrom->nMsgWaitSize)
2858 { 2894 {
2859 pfrom->nIncompleteMessageStart = GetTime(); 2895 pfrom->nMsgWaitSince = nNow;
2860 pfrom->nIncompleteMessageSize = nMessageSize; 2896 pfrom->nMsgWaitSize = nPayloadAvailable;
2861 pfrom->strIncompleteMessageCommand = strCommand;
2862 } 2897 }
2863 else if (GetTime() - pfrom->nIncompleteMessageStart > 2898 else if (nNow - pfrom->nMsgWaitSince > BTF_MSG_WAIT_MAX_SECS)
2864 BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS)
2865 { 2899 {
2866 LogPrint("net", 2900 // Deliberately not behind a debug category. This is the node
2867 "disconnecting %s: incomplete %s message, %u of %u bytes after %d seconds\n", 2901 // going deaf to a peer, which is the thing nobody could see.
2868 pfrom->addr.ToString().c_str(), strCommand.c_str(), 2902 printf("peer %s announced a %u byte '%s' and has sent nothing more "
2869 nPayloadAvailable, nMessageSize, 2903 "for %d s (%d of %u payload bytes) -- dropping, this peer is unreadable\n",
2870 BTF_INCOMPLETE_MESSAGE_TIMEOUT_SECS); 2904 pfrom->addr.ToString().c_str(), nMessageSize,
2905 strCommand.c_str(), (int)(nNow - pfrom->nMsgWaitSince),
2906 (int)nPayloadAvailable, nMessageSize);
2871 pfrom->fDisconnect = true; 2907 pfrom->fDisconnect = true;
2872 } 2908 }
2909 // No re-insert, no Sleep(): the header was never consumed, so there
2910 // is nothing to put back, and sleeping here stalls every peer.
2873 break; 2911 break;
2874 } 2912 }
2875 pfrom->nIncompleteMessageStart = 0;
2876 pfrom->nIncompleteMessageSize = 0;
2877 pfrom->strIncompleteMessageCommand.clear();
2878 vRecv.ignore(sizeof(CMessageHeader)); 2913 vRecv.ignore(sizeof(CMessageHeader));
2914 // The body arrived, so this peer is readable again.
2915 pfrom->nMsgWaitSince = 0;
2916 pfrom->nMsgWaitSize = 0;
2879 2917
2880 // Copy message to its own buffer 2918 // Copy message to its own buffer
2881 CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion); 2919 CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);