diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 82 | ||||
| -rw-r--r-- | src/net.h | 15 |
2 files changed, 75 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); |
| @@ -146,6 +146,16 @@ static const int64 BTF_NO_PEER_HEIGHT_MAX_SECS = 20 * 60; | |||
| 146 | // minutes is far beyond that, and well inside BTF_RECV_TIMEOUT_SECS. | 146 | // minutes is far beyond that, and well inside BTF_RECV_TIMEOUT_SECS. |
| 147 | static const int64 BTF_MSG_WAIT_MAX_SECS = 2 * 60; | 147 | static const int64 BTF_MSG_WAIT_MAX_SECS = 2 * 60; |
| 148 | 148 | ||
| 149 | // Largest payload a peer may announce in a message header: MAX_PROTOCOL_MESSAGE_SIZE | ||
| 150 | // is defined further up this file. This tree reached the same cap by the same | ||
| 151 | // reasoning as upstream #109 -- a 256 MB allowance is eight times what the | ||
| 152 | // deserializer will accept (serialize.h throws above MAX_SIZE), so the header | ||
| 153 | // check admitted an allocation the parser would only reject later, after | ||
| 154 | // BTF_MSG_WAIT_MAX_SECS of a stalled peer slot -- so only the note survives. | ||
| 155 | // | ||
| 156 | // Both definitions were textually clean and 59 lines apart, so the replay onto | ||
| 157 | // upstream merged them without a conflict and only the compiler objected. | ||
| 158 | |||
| 149 | // Send a ping after this long with nothing to say, so a peer running the | 159 | // Send a ping after this long with nothing to say, so a peer running the |
| 150 | // inactivity check above does not mistake a quiet node for a dead one. Must | 160 | // inactivity check above does not mistake a quiet node for a dead one. Must |
| 151 | // stay well under BTF_RECV_TIMEOUT_SECS. | 161 | // stay well under BTF_RECV_TIMEOUT_SECS. |
| @@ -741,6 +751,11 @@ public: | |||
| 741 | { | 751 | { |
| 742 | if (hSocket != INVALID_SOCKET) | 752 | if (hSocket != INVALID_SOCKET) |
| 743 | { | 753 | { |
| 754 | // Route through BtfCloseSocket so the accounting sees this close. | ||
| 755 | // A raw closesocket() here was invisible to sockcount, which is | ||
| 756 | // why the leak numbers never added up, and leaving the descriptor | ||
| 757 | // set afterwards is how a number that now belongs to somebody else | ||
| 758 | // gets closed a second time. (#103) | ||
| 744 | BtfCloseSocket(hSocket); | 759 | BtfCloseSocket(hSocket); |
| 745 | hSocket = INVALID_SOCKET; | 760 | hSocket = INVALID_SOCKET; |
| 746 | } | 761 | } |