diff options
-rw-r--r-- | qa/rpc-tests/README.md | 5 | ||||
-rwxr-xr-x | qa/rpc-tests/maxblocksinflight.py | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/maxuploadtarget.py | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/p2p-acceptblock.py | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/sendheaders.py | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/comptool.py | 1 | ||||
-rwxr-xr-x | qa/rpc-tests/test_framework/mininode.py | 24 | ||||
-rw-r--r-- | src/net.cpp | 29 | ||||
-rw-r--r-- | src/net.h | 28 | ||||
-rw-r--r-- | src/rpcnet.cpp | 22 |
10 files changed, 69 insertions, 44 deletions
diff --git a/qa/rpc-tests/README.md b/qa/rpc-tests/README.md index 898931936b..651b01f18a 100644 --- a/qa/rpc-tests/README.md +++ b/qa/rpc-tests/README.md @@ -47,10 +47,7 @@ implements the test logic. * ```NodeConn``` is the class used to connect to a bitcoind. If you implement a callback class that derives from ```NodeConnCB``` and pass that to the ```NodeConn``` object, your code will receive the appropriate callbacks when -events of interest arrive. NOTE: be sure to call -```self.create_callback_map()``` in your derived classes' ```__init__``` -function, so that the correct mappings are set up between p2p messages and your -callback functions. +events of interest arrive. * You can pass the same handler to multiple ```NodeConn```'s if you like, or pass different ones to each -- whatever makes the most sense for your test. diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index a601147ce8..1a9ae480ab 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -34,7 +34,6 @@ class TestManager(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) self.log = logging.getLogger("BlockRelayTest") - self.create_callback_map() def add_new_connection(self, connection): self.connection = connection diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py index e714465db1..249663779c 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -25,7 +25,6 @@ if uploadtarget has been reached. class TestNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.ping_counter = 1 self.last_pong = msg_pong() diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 700deab207..23872d8494 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -62,7 +62,6 @@ The test: class TestNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.ping_counter = 1 self.last_pong = msg_pong() diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index 63e071805a..e6e26dbce3 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -70,7 +70,6 @@ f. Announce 1 more header that builds on that fork. class BaseNode(NodeConnCB): def __init__(self): NodeConnCB.__init__(self) - self.create_callback_map() self.connection = None self.last_inv = None self.last_headers = None diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index e0b3ce040d..9444424dcf 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -45,7 +45,6 @@ class TestNode(NodeConnCB): def __init__(self, block_store, tx_store): NodeConnCB.__init__(self) - self.create_callback_map() self.conn = None self.bestblockhash = None self.block_store = block_store diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 64985d58e2..9d0fb713a1 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -1015,32 +1015,10 @@ class NodeConnCB(object): return time.sleep(0.05) - # Derived classes should call this function once to set the message map - # which associates the derived classes' functions to incoming messages - def create_callback_map(self): - self.cbmap = { - "version": self.on_version, - "verack": self.on_verack, - "addr": self.on_addr, - "alert": self.on_alert, - "inv": self.on_inv, - "getdata": self.on_getdata, - "getblocks": self.on_getblocks, - "tx": self.on_tx, - "block": self.on_block, - "getaddr": self.on_getaddr, - "ping": self.on_ping, - "pong": self.on_pong, - "headers": self.on_headers, - "getheaders": self.on_getheaders, - "reject": self.on_reject, - "mempool": self.on_mempool - } - def deliver(self, conn, message): with mininode_lock: try: - self.cbmap[message.command](conn, message) + getattr(self, 'on_' + message.command)(conn, message) except: print "ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0]) diff --git a/src/net.cpp b/src/net.cpp index a8aa97feec..159d44cba1 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -67,6 +67,16 @@ 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", + "sendheaders", "verack"}; + +const static std::string NET_MESSAGE_COMMAND_OTHER = "*other*"; + // // Global state variables // @@ -627,7 +637,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, @@ -682,6 +694,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(); } @@ -2378,6 +2399,9 @@ CNode::CNode(SOCKET hSocketIn, const CAddress& addrIn, const std::string& addrNa nPingUsecTime = 0; fPingQueued = false; nMinPingUsecTime = std::numeric_limits<int64_t>::max(); + 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); @@ -2457,7 +2481,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 @@ -2480,6 +2504,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; @@ -182,6 +182,7 @@ struct LocalServiceInfo { extern CCriticalSection cs_mapLocalHost; extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost; +typedef std::map<std::string, uint64_t> 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; @@ -373,6 +376,9 @@ protected: static std::vector<CSubNet> vWhitelistedRange; static CCriticalSection cs_vWhitelistedRange; + mapMsgCmdSize mapSendBytesPerMsgCmd; + mapMsgCmdSize mapRecvBytesPerMsgCmd; + // Basic fuzz-testing void Fuzz(int nChance); // modifies ssSend @@ -525,7 +531,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(); @@ -535,7 +541,7 @@ public: try { BeginMessage(pszCommand); - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -551,7 +557,7 @@ public: { BeginMessage(pszCommand); ssSend << a1; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -567,7 +573,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -583,7 +589,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -599,7 +605,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -615,7 +621,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -631,7 +637,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -647,7 +653,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -663,7 +669,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8; - EndMessage(); + EndMessage(pszCommand); } catch (...) { @@ -679,7 +685,7 @@ public: { BeginMessage(pszCommand); ssSend << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << a9; - EndMessage(); + EndMessage(pszCommand); } catch (...) { diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp index 2578848891..0ce108b06e 100644 --- a/src/rpcnet.cpp +++ b/src/rpcnet.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" @@ -165,6 +173,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); } |