diff --git a/src/net.cpp b/src/net.cpp index 96c496b05..92c831baa 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -69,6 +69,15 @@ namespace { }; } +//immutable thread safe array of allowed commands for logging inbound traffic +const static std::string logAllowIncomingMsgCmds[] = { + "version", "addr", "inv", "getdata", "merkleblock", + "getblocks", "getheaders", "tx", "headers", "block", + "getaddr", "mempool", "ping", "pong", "alert", "notfound", + "filterload", "filteradd", "filterclear", "reject"}; + +const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; + // // Global state variables // @@ -664,7 +673,9 @@ void CNode::copyStats(CNodeStats &stats) X(fInbound); X(nStartingHeight); X(nSendBytes); + X(mapSendBytesPerMsgCmd); X(nRecvBytes); + X(mapRecvBytesPerMsgCmd); X(fWhitelisted); // It is common for nodes with good ping times to suddenly become lagged, @@ -719,6 +730,15 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes) nBytes -= handled; if (msg.complete()) { + + //store received bytes per message command + //to prevent a memory DOS, only allow valid commands + mapMsgCmdSize::iterator i = mapRecvBytesPerMsgCmd.find(msg.hdr.pchCommand); + if (i == mapRecvBytesPerMsgCmd.end()) + i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER); + assert(i != mapRecvBytesPerMsgCmd.end()); + i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE; + msg.nTime = GetTimeMicros(); messageHandlerCondition.notify_one(); } @@ -2361,6 +2381,10 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa lastSentFeeFilter = 0; nextSendTimeFeeFilter = 0; + for (unsigned int i = 0; i < sizeof(logAllowIncomingMsgCmds)/sizeof(logAllowIncomingMsgCmds[0]); i++) + mapRecvBytesPerMsgCmd[logAllowIncomingMsgCmds[i]] = 0; + mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0; + { LOCK(cs_nLastNodeId); id = nLastNodeId++; @@ -2439,7 +2463,7 @@ void CNode::AbortMessage() UNLOCK_FUNCTION(cs_vSend) LogPrint("net", "(aborted)\n"); } -void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) +void CNode::EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend) { // The -*messagestest options are intentionally not documented in the help message, // since they are only used during development to debug the networking code and are @@ -2462,6 +2486,9 @@ void CNode::EndMessage() UNLOCK_FUNCTION(cs_vSend) unsigned int nSize = ssSend.size() - CMessageHeader::HEADER_SIZE; WriteLE32((uint8_t*)&ssSend[CMessageHeader::MESSAGE_SIZE_OFFSET], nSize); + //log total amount of bytes per command + mapSendBytesPerMsgCmd[std::string(pszCommand)] += nSize + CMessageHeader::HEADER_SIZE; + // Set the checksum uint256 hash = Hash(ssSend.begin() + CMessageHeader::HEADER_SIZE, ssSend.end()); unsigned int nChecksum = 0; diff --git a/src/net.h b/src/net.h index bb0f8cedb..3c1ff39f5 100644 --- a/src/net.h +++ b/src/net.h @@ -182,6 +182,7 @@ struct LocalServiceInfo { extern CCriticalSection cs_mapLocalHost; extern std::map mapLocalHost; +typedef std::map mapMsgCmdSize; //command, total bytes class CNodeStats { @@ -199,7 +200,9 @@ public: bool fInbound; int nStartingHeight; uint64_t nSendBytes; + mapMsgCmdSize mapSendBytesPerMsgCmd; uint64_t nRecvBytes; + mapMsgCmdSize mapRecvBytesPerMsgCmd; bool fWhitelisted; double dPingTime; double dPingWait; @@ -376,6 +379,9 @@ protected: static std::vector vWhitelistedRange; static CCriticalSection cs_vWhitelistedRange; + mapMsgCmdSize mapSendBytesPerMsgCmd; + mapMsgCmdSize mapRecvBytesPerMsgCmd; + // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -550,7 +556,7 @@ public: void AbortMessage() UNLOCK_FUNCTION(cs_vSend); // TODO: Document the precondition of this function. Is cs_vSend locked? - void EndMessage() UNLOCK_FUNCTION(cs_vSend); + void EndMessage(const char* pszCommand) UNLOCK_FUNCTION(cs_vSend); void PushVersion(); @@ -560,7 +566,7 @@ public: try { BeginMessage(pszCommand); - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -576,7 +582,7 @@ public: { BeginMessage(pszCommand); ssSend << a1; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -592,7 +598,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -608,7 +614,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -624,7 +630,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -640,7 +646,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -656,7 +662,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -672,7 +678,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -688,7 +694,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -704,7 +710,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); + EndMessage(pszCommand); } catch (...) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index a0a9233c0..1f5b086d7 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -111,6 +111,14 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) " n, (numeric) The heights of blocks we're currently asking from this peer\n" " ...\n" " ]\n" + " \"bytessent_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" + " ...\n" + " }\n" + " \"bytesrecv_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes received aggregated by message type\n" + " ...\n" + " }\n" " }\n" " ,...\n" "]\n" @@ -167,6 +175,20 @@ UniValue getpeerinfo(const UniValue& params, bool fHelp) } obj.push_back(Pair("whitelisted", stats.fWhitelisted)); + UniValue sendPerMsgCmd(UniValue::VOBJ); + BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapSendBytesPerMsgCmd) { + if (i.second > 0) + sendPerMsgCmd.push_back(Pair(i.first, i.second)); + } + obj.push_back(Pair("bytessent_per_msg", sendPerMsgCmd)); + + UniValue recvPerMsgCmd(UniValue::VOBJ); + BOOST_FOREACH(const mapMsgCmdSize::value_type &i, stats.mapRecvBytesPerMsgCmd) { + if (i.second > 0) + recvPerMsgCmd.push_back(Pair(i.first, i.second)); + } + obj.push_back(Pair("bytesrecv_per_msg", recvPerMsgCmd)); + ret.push_back(obj); }