Browse Source

Cleanup. More fixes. More tests.

cl-refactor
subtly 9 years ago
parent
commit
99a97e05f0
  1. 4
      libp2p/RLPXFrameCoder.cpp
  2. 4
      libp2p/RLPXFrameCoder.h
  3. 90
      libp2p/RLPXFrameReader.cpp
  4. 59
      libp2p/RLPXFrameReader.h
  5. 2
      libp2p/RLPXFrameWriter.cpp
  6. 7
      libp2p/RLPXFrameWriter.h
  7. 92
      test/libp2p/rlpx.cpp

4
libp2p/RLPXFrameCoder.cpp

@ -37,8 +37,8 @@ RLPXFrameInfo::RLPXFrameInfo(bytesConstRef _header)
RLP header(_header.cropped(3), RLP::ThrowOnFail | RLP::FailIfTooSmall); RLP header(_header.cropped(3), RLP::ThrowOnFail | RLP::FailIfTooSmall);
auto itemCount = header.itemCount(); auto itemCount = header.itemCount();
protocolId = header[0].toInt<uint16_t>(); protocolId = header[0].toInt<uint16_t>();
hasSequence = itemCount > 1; multiFrame = itemCount > 1;
sequenceId = hasSequence ? header[1].toInt<uint16_t>() : 0; sequenceId = multiFrame ? header[1].toInt<uint16_t>() : 0;
totalLength = itemCount == 3 ? header[2].toInt<uint32_t>() : 0; totalLength = itemCount == 3 ? header[2].toInt<uint32_t>() : 0;
} }

4
libp2p/RLPXFrameCoder.h

@ -48,9 +48,9 @@ struct RLPXFrameInfo
uint8_t padding = 0; ///< Length of padding which follows @length. uint8_t padding = 0; ///< Length of padding which follows @length.
uint16_t protocolId = 0; ///< Protocol ID as negotiated by handshake. uint16_t protocolId = 0; ///< Protocol ID as negotiated by handshake.
bool hasSequence = false; ///< If this frame is part of a sequence bool multiFrame = false; ///< If this frame is part of a sequence
uint16_t sequenceId = 0; ///< Sequence ID of frame uint16_t sequenceId = 0; ///< Sequence ID of frame
uint32_t totalLength = 0; ///< Set to uint32_t totalLength = 0; ///< Set to total length of packet in first frame of multiframe packet
}; };
class RLPXHandshake; class RLPXHandshake;

90
libp2p/RLPXFrameReader.cpp

@ -0,0 +1,90 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameReader.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "RLPXFrameReader.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
std::vector<RLPXPacket> RLPXFrameReader::demux(RLPXFrameCoder& _coder, RLPXFrameInfo const& _info, bytesRef _frame)
{
if (!_coder.authAndDecryptFrame(_frame))
BOOST_THROW_EXCEPTION(RLPXFrameDecrytFailed());
std::vector<RLPXPacket> ret;
if (_frame.empty())
// drop: bad frame (empty)
return ret;
if (_info.multiFrame && _info.totalLength && _frame.size() > _info.totalLength)
// drop: bad frame (too large)
return ret;
// trim mac
bytesConstRef buffer = _frame.cropped(0, _frame.size() - (h128::size + _info.padding));
// continue populating multiframe packets
if (_info.multiFrame && m_incomplete.count(_info.sequenceId))
{
uint32_t& remaining = m_incomplete.at(_info.sequenceId).second;
if (!_info.totalLength && buffer.size() > 0 && buffer.size() <= remaining)
{
remaining -= buffer.size();
RLPXPacket& p = m_incomplete.at(_info.sequenceId).first;
if(p.append(buffer) && !remaining)
ret.push_back(std::move(p));
if (!remaining)
m_incomplete.erase(_info.sequenceId);
if (!ret.empty() && remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
else if (ret.empty() && !remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
return ret;
}
else
m_incomplete.erase(_info.sequenceId);
}
while (!buffer.empty())
{
auto type = nextRLP(buffer);
if (type.empty())
break;
buffer = buffer.cropped(type.size());
// consume entire buffer if packet has sequence
auto packet = _info.multiFrame ? buffer : nextRLP(buffer);
buffer = buffer.cropped(packet.size());
RLPXPacket p(m_protocolType, type);
if (!packet.empty())
p.append(packet);
uint32_t remaining = _info.totalLength - type.size() - packet.size();
if (p.isValid())
ret.push_back(std::move(p));
else if (_info.multiFrame && remaining)
m_incomplete.insert(std::make_pair(_info.sequenceId, std::make_pair(std::move(p), remaining)));
else
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
}
return ret;
}

59
libp2p/RLPXFrameReader.h

@ -44,64 +44,7 @@ public:
RLPXFrameReader(uint16_t _protocolType): m_protocolType(_protocolType) {} RLPXFrameReader(uint16_t _protocolType): m_protocolType(_protocolType) {}
/// Processes a single frame returning complete packets. /// Processes a single frame returning complete packets.
std::vector<RLPXPacket> demux(RLPXFrameCoder& _coder, bytesRef _frame, bool _sequence = false, uint16_t _seq = 0, uint32_t _totalSize = 0) std::vector<RLPXPacket> demux(RLPXFrameCoder& _coder, RLPXFrameInfo const& _info, bytesRef _frame);
{
if (!_coder.authAndDecryptFrame(_frame))
BOOST_THROW_EXCEPTION(RLPXFrameDecrytFailed());
std::vector<RLPXPacket> ret;
if (!_sequence && (!_frame.size() || _frame.size() > _totalSize))
return ret;
// trim mac
bytesConstRef buffer = _frame.cropped(0, _frame.size() - h128::size);
// continue populating incomplete packets
if (_sequence && m_incomplete.count(_seq))
{
uint32_t& remaining = m_incomplete.at(_seq).second;
if (!_totalSize && buffer.size() > 0 && buffer.size() <= remaining)
{
remaining -= buffer.size();
RLPXPacket& p = m_incomplete.at(_seq).first;
if(p.append(buffer) && !remaining)
ret.push_back(std::move(p));
if (!remaining)
m_incomplete.erase(_seq);
if (!ret.empty() && remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
else if (ret.empty() && !remaining)
BOOST_THROW_EXCEPTION(RLPXInvalidPacket());
return ret;
}
else
m_incomplete.erase(_seq);
}
while (!buffer.empty())
{
auto type = nextRLP(buffer);
if (type.empty())
break;
buffer = buffer.cropped(type.size());
// consume entire buffer if packet has sequence
auto packet = _sequence ? buffer : nextRLP(buffer);
buffer = buffer.cropped(packet.size());
RLPXPacket p(m_protocolType, type);
if (!packet.empty())
p.append(packet);
uint32_t remaining = _totalSize - type.size() - packet.size();
if (p.isValid())
ret.push_back(std::move(p));
else if (_sequence && remaining)
m_incomplete.insert(std::make_pair(_seq, std::make_pair(std::move(p), remaining)));
// else drop invalid packet
}
return ret;
}
protected: protected:
uint16_t m_protocolType; uint16_t m_protocolType;

2
libp2p/RLPXFrameWriter.cpp

@ -50,7 +50,7 @@ size_t RLPXFrameWriter::mux(RLPXFrameCoder& _coder, unsigned _size, vector<bytes
return 0; return 0;
size_t ret = 0; size_t ret = 0;
size_t frameLen = _size; size_t frameLen = _size / 16 * 16;
bytes payload(0); bytes payload(0);
bool swapQueues = false; bool swapQueues = false;
while (frameLen >= c_overhead + c_blockSize) while (frameLen >= c_overhead + c_blockSize)

7
libp2p/RLPXFrameWriter.h

@ -71,12 +71,13 @@ public:
/// Moves @_payload output to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe. /// Moves @_payload output to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe.
void enque(uint8_t _packetType, RLPStream& _payload, PacketPriority _priority = PriorityLow); void enque(uint8_t _packetType, RLPStream& _payload, PacketPriority _priority = PriorityLow);
/// Moves @_p to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe.
void enque(RLPXPacket&& _p, PacketPriority _priority = PriorityLow);
/// Returns number of packets framed and outputs frames to o_bytes. Not thread-safe. /// Returns number of packets framed and outputs frames to o_bytes. Not thread-safe.
size_t mux(RLPXFrameCoder& _coder, unsigned _size, std::vector<bytes>& o_toWrite); size_t mux(RLPXFrameCoder& _coder, unsigned _size, std::vector<bytes>& o_toWrite);
protected:
/// Moves @_p to queue, to be muxed into frames by mux() when network buffer is ready for writing. Thread-safe.
void enque(RLPXPacket&& _p, PacketPriority _priority = PriorityLow);
private: private:
uint16_t const m_protocolType; // Protocol Type uint16_t const m_protocolType; // Protocol Type
std::pair<WriterState, WriterState> m_q; // High, Low frame queues std::pair<WriterState, WriterState> m_q; // High, Low frame queues

92
test/libp2p/rlpx.cpp

@ -454,7 +454,7 @@ BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives)
BOOST_REQUIRE(plainTest3 == expectedPlain3); BOOST_REQUIRE(plainTest3 == expectedPlain3);
} }
BOOST_AUTO_TEST_CASE(segmentedPacket) BOOST_AUTO_TEST_CASE(segmentedPacketFlush)
{ {
ECDHE localEph; ECDHE localEph;
h256 localNonce = Nonce::get(); h256 localNonce = Nonce::get();
@ -519,7 +519,7 @@ BOOST_AUTO_TEST_CASE(segmentedPacket)
BOOST_REQUIRE(decryptedHeader); BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size); bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header); RLPXFrameInfo f(header);
auto p = f.hasSequence ? r.demux(decoder, frame, true, f.sequenceId, f.totalLength) : r.demux(decoder, frame); auto p = r.demux(decoder, f, frame);
if (p.size()) if (p.size())
packets += move(p); packets += move(p);
} }
@ -529,7 +529,7 @@ BOOST_AUTO_TEST_CASE(segmentedPacket)
BOOST_REQUIRE_EQUAL(sha3(packets.front().type()), sha3(packetTypeRLP)); BOOST_REQUIRE_EQUAL(sha3(packets.front().type()), sha3(packetTypeRLP));
} }
BOOST_AUTO_TEST_CASE(coalescedPackets) BOOST_AUTO_TEST_CASE(coalescedPacketsPadded)
{ {
ECDHE localEph; ECDHE localEph;
h256 localNonce = Nonce::get(); h256 localNonce = Nonce::get();
@ -542,27 +542,97 @@ BOOST_AUTO_TEST_CASE(coalescedPackets)
/// Test writing four 32 byte RLPStream packets such that /// Test writing four 32 byte RLPStream packets such that
/// a single 1KB frame will incldue all four packets. /// a single 1KB frame will incldue all four packets.
auto dequeLen = 1024; // sufficient enough for all packets auto dequeLen = 1024; // sufficient enough for all packets
bytes initialStuff = sha3("A").asBytes(); bytes stuff = sha3("A").asBytes();
vector<h256> packets; vector<bytes> packetsOut;
for (unsigned i = 0; i < 4; i++) for (unsigned i = 0; i < 4; i++)
packets.push_back(sha3(initialStuff)); packetsOut.push_back(stuff);
RLPXFrameWriter w(0); RLPXFrameWriter w(0);
uint8_t packetType = 127; uint8_t packetType = 127;
for (auto const& p: packets) bytes packetTypeRLP((RLPStream() << packetType).out());
for (auto const& p: packetsOut)
w.enque(packetType, (RLPStream() << p)); w.enque(packetType, (RLPStream() << p));
vector<bytes> encframes; vector<bytes> encframes;
BOOST_REQUIRE_EQUAL(4, w.mux(encoder, dequeLen, encframes)); BOOST_REQUIRE_EQUAL(4, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(0, w.mux(encoder, dequeLen, encframes)); BOOST_REQUIRE_EQUAL(0, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(1, encframes.size()); BOOST_REQUIRE_EQUAL(1, encframes.size());
auto expectedFrameSize = RLPXFrameWriter::EmptyFrameLength + packets.size() * (/*packet-type*/ 1 + h256::size + /*rlp-prefix*/ 1); auto expectedFrameSize = RLPXFrameWriter::EmptyFrameLength + packetsOut.size() * (/*packet-type*/ 1 + h256::size + /*rlp-prefix*/ 1);
expectedFrameSize += ((16 - (expectedFrameSize % 16)) % 16); expectedFrameSize += ((16 - (expectedFrameSize % 16)) % 16);
BOOST_REQUIRE_EQUAL(expectedFrameSize, encframes[0].size()); BOOST_REQUIRE_EQUAL(expectedFrameSize, encframes[0].size());
// read and assemble dequed encframes
RLPXFrameCoder decoder(false, localEph.pubkey(), localNonce, remoteEph, remoteNonce, &ackCipher, &authCipher);
vector<RLPXPacket> packets;
RLPXFrameReader r(0);
bytesRef frameWithHeader(encframes[0].data(), encframes[0].size());
bytesRef header = frameWithHeader.cropped(0, h256::size);
bool decryptedHeader = decoder.authAndDecryptHeader(header);
BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header);
BOOST_REQUIRE_EQUAL(f.multiFrame, false);
auto p = r.demux(decoder, f, frame);
packets += move(p);
RLPStream rlpPayload;
rlpPayload << stuff;
BOOST_REQUIRE_EQUAL(packets.size(), 4);
while (!packets.empty())
{
BOOST_REQUIRE_EQUAL(packets.back().size(), packetTypeRLP.size() + rlpPayload.out().size());
BOOST_REQUIRE_EQUAL(sha3(RLP(packets.back().data()).payload()), sha3(stuff));
BOOST_REQUIRE_EQUAL(sha3(packets.back().type()), sha3(packetTypeRLP));
packets.pop_back();
}
} }
BOOST_AUTO_TEST_CASE(singleFramePacket) BOOST_AUTO_TEST_CASE(singleFramePacketFlush)
{ {
ECDHE localEph;
h256 localNonce = Nonce::get();
ECDHE remoteEph;
h256 remoteNonce = Nonce::get();
bytes ackCipher{0};
bytes authCipher{1};
RLPXFrameCoder encoder(true, remoteEph.pubkey(), remoteNonce, localEph, localNonce, &ackCipher, &authCipher);
/// Test writing four 32 byte RLPStream packets such that
/// a single 1KB frame will incldue all four packets.
bytes stuff = sha3("A").asBytes();
RLPXFrameWriter w(0);
uint8_t packetType = 127;
bytes packetTypeRLP((RLPStream() << packetType).out());
w.enque(packetType, (RLPStream() << stuff));
vector<bytes> encframes;
auto dequeLen = RLPXFrameWriter::EmptyFrameLength + 34;
dequeLen += ((16 - (dequeLen % 16)) % 16);
BOOST_REQUIRE_EQUAL(1, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(0, w.mux(encoder, dequeLen, encframes));
BOOST_REQUIRE_EQUAL(1, encframes.size());
BOOST_REQUIRE_EQUAL(dequeLen, encframes[0].size());
// read and assemble dequed encframes
RLPXFrameCoder decoder(false, localEph.pubkey(), localNonce, remoteEph, remoteNonce, &ackCipher, &authCipher);
vector<RLPXPacket> packets;
RLPXFrameReader r(0);
bytesRef frameWithHeader(encframes[0].data(), encframes[0].size());
bytesRef header = frameWithHeader.cropped(0, h256::size);
bool decryptedHeader = decoder.authAndDecryptHeader(header);
BOOST_REQUIRE(decryptedHeader);
bytesRef frame = frameWithHeader.cropped(h256::size);
RLPXFrameInfo f(header);
BOOST_REQUIRE_EQUAL(f.multiFrame, false);
auto p = r.demux(decoder, f, frame);
packets += move(p);
RLPStream rlpPayload;
rlpPayload << stuff;
BOOST_REQUIRE_EQUAL(packets.size(), 1);
BOOST_REQUIRE_EQUAL(packets.back().size(), packetTypeRLP.size() + rlpPayload.out().size());
BOOST_REQUIRE_EQUAL(sha3(RLP(packets.back().data()).payload()), sha3(stuff));
BOOST_REQUIRE_EQUAL(sha3(packets.back().type()), sha3(packetTypeRLP));
} }
BOOST_AUTO_TEST_CASE(manyProtocols) BOOST_AUTO_TEST_CASE(manyProtocols)
@ -570,9 +640,5 @@ BOOST_AUTO_TEST_CASE(manyProtocols)
} }
BOOST_AUTO_TEST_CASE(allOfSingleSegmentedCoalescedWithManyProtocols)
{
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

Loading…
Cancel
Save