diff options
Diffstat (limited to 'src/main.cpp')
| -rw-r--r-- | src/main.cpp | 82 |
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); |