mirror of https://github.com/infosecn1nja/C3.git
Add new -o overlapped mode to ChannelLinter
parent
bae99690e7
commit
20172dcf49
|
@ -31,6 +31,9 @@ namespace FSecure::C3::Linter
|
||||||
/// Whether application channel should test sending and receiving through the channel
|
/// Whether application channel should test sending and receiving through the channel
|
||||||
bool m_TestChannelIO;
|
bool m_TestChannelIO;
|
||||||
|
|
||||||
|
/// Change test IO mode to perform Overlapped Read/Write on chunks.
|
||||||
|
bool m_OverlappedIO;
|
||||||
|
|
||||||
/// Command id and its arguments
|
/// Command id and its arguments
|
||||||
std::optional<StringVector> m_Command;
|
std::optional<StringVector> m_Command;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace FSecure::C3::Linter
|
||||||
m_ArgParser.addArgument("-c", "--complementary", '*');
|
m_ArgParser.addArgument("-c", "--complementary", '*');
|
||||||
m_ArgParser.addArgument("-i", "--test-io");
|
m_ArgParser.addArgument("-i", "--test-io");
|
||||||
m_ArgParser.addArgument("-x", "--command", '+');
|
m_ArgParser.addArgument("-x", "--command", '+');
|
||||||
|
m_ArgParser.addArgument("-o", "--overlapped");
|
||||||
m_ArgParser.useExceptions(true);
|
m_ArgParser.useExceptions(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +53,10 @@ Options:
|
||||||
If -c is not present, complementary channel arguments are deduced by swapping
|
If -c is not present, complementary channel arguments are deduced by swapping
|
||||||
parameters from Capability/create/arguments arrays.
|
parameters from Capability/create/arguments arrays.
|
||||||
|
|
||||||
|
-o, --overlapped Changes way IO test is performed.
|
||||||
|
For each sent chunk of packet, complementary channel must read it.
|
||||||
|
Packet order test is not performed.
|
||||||
|
|
||||||
-x <ID> [ARGS... ], --command <ID> [ARGS... ]
|
-x <ID> [ARGS... ], --command <ID> [ARGS... ]
|
||||||
Execute a command with a given <ID> and arguments [ARGS...]
|
Execute a command with a given <ID> and arguments [ARGS...]
|
||||||
|
|
||||||
|
@ -85,6 +90,7 @@ Examples:
|
||||||
m_Config.m_ComplementaryChannelArguments = m_ArgParser.retrieve<std::vector<std::string>>("complementary");
|
m_Config.m_ComplementaryChannelArguments = m_ArgParser.retrieve<std::vector<std::string>>("complementary");
|
||||||
|
|
||||||
m_Config.m_TestChannelIO = m_ArgParser.exists("test-io");
|
m_Config.m_TestChannelIO = m_ArgParser.exists("test-io");
|
||||||
|
m_Config.m_OverlappedIO = m_ArgParser.exists("overlapped");
|
||||||
|
|
||||||
if (m_ArgParser.exists("command"))
|
if (m_ArgParser.exists("command"))
|
||||||
m_Config.m_Command = m_ArgParser.retrieve<StringVector>("command");
|
m_Config.m_Command = m_ArgParser.retrieve<StringVector>("command");
|
||||||
|
|
|
@ -113,9 +113,10 @@ namespace FSecure::C3::Linter
|
||||||
std::cout << "Creating complementary channel ... " << std::flush;
|
std::cout << "Creating complementary channel ... " << std::flush;
|
||||||
auto complementaryArgs = GetComplementaryChannelArgs();
|
auto complementaryArgs = GetComplementaryChannelArgs();
|
||||||
auto complementaryChannel = MakeChannel(complementaryArgs);
|
auto complementaryChannel = MakeChannel(complementaryArgs);
|
||||||
|
assert(complementaryChannel);
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << "OK" << std::endl;
|
||||||
|
|
||||||
TestChannelIO(channel, complementaryChannel);
|
TestChannelIO(*channel.get(), *complementaryChannel.get(), m_Config.m_OverlappedIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Config.m_Command)
|
if (m_Config.m_Command)
|
||||||
|
@ -140,35 +141,44 @@ namespace FSecure::C3::Linter
|
||||||
return channelBridge;
|
return channelBridge;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChannelLinter::TestChannelIO(std::shared_ptr<MockDeviceBridge> const& channel, std::shared_ptr<MockDeviceBridge> const& complementary)
|
void ChannelLinter::TestChannelIO(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped)
|
||||||
{
|
{
|
||||||
assert(channel);
|
TestChannelMTU(channel, complementary, overlapped);
|
||||||
assert(complementary);
|
|
||||||
|
|
||||||
|
if (!overlapped)
|
||||||
|
TestChannelOrder(channel, complementary);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChannelLinter::TestChannelMTU(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped)
|
||||||
|
{
|
||||||
for (size_t packetLen : { 8, 64, 1024, 1024 * 1024})
|
for (size_t packetLen : { 8, 64, 1024, 1024 * 1024})
|
||||||
{
|
{
|
||||||
std::cout << "Testing channel with " << packetLen << " bytes of data ... " << std::flush;
|
std::cout << "Testing channel with " << packetLen << " bytes of data ... " << std::flush;
|
||||||
auto data = ByteVector(FSecure::Utils::GenerateRandomData(packetLen));
|
|
||||||
|
|
||||||
channel->Send(data);
|
auto data = ByteVector(FSecure::Utils::GenerateRandomData(packetLen));
|
||||||
if (data != complementary->Receive()[0])
|
auto passed = overlapped ? TestOverlapped(channel, complementary, data) : TestSequential(channel, complementary, data);
|
||||||
|
if (!passed)
|
||||||
throw std::exception("Data sent and received mismatch");
|
throw std::exception("Data sent and received mismatch");
|
||||||
|
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << "OK" << std::endl;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto numberOfTests = 10;
|
void ChannelLinter::TestChannelOrder(MockDeviceBridge& channel, MockDeviceBridge& complementary)
|
||||||
auto packetSize = 64;
|
{
|
||||||
std::cout << "Testing channel order with " << numberOfTests << " packets of " << packetSize << " bytes of data ... " << std::flush;
|
constexpr auto numberOfTests = 10;
|
||||||
|
constexpr auto packetSize = 64;
|
||||||
std::vector<ByteVector> sent;
|
std::vector<ByteVector> sent;
|
||||||
|
|
||||||
|
std::cout << "Testing channel order with " << numberOfTests << " packets of " << packetSize << " bytes of data ... " << std::flush;
|
||||||
|
|
||||||
for (auto i = 0; i < numberOfTests; ++i)
|
for (auto i = 0; i < numberOfTests; ++i)
|
||||||
{
|
{
|
||||||
sent.push_back(FSecure::Utils::GenerateRandomData(packetSize));
|
sent.push_back(FSecure::Utils::GenerateRandomData(packetSize));
|
||||||
channel->Send(sent[i]);
|
channel.Send(sent[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto received = complementary->Receive(sent.size());
|
auto received = complementary.Receive(sent.size());
|
||||||
received.resize(sent.size());
|
received.resize(sent.size());
|
||||||
|
|
||||||
if (sent != received)
|
if (sent != received)
|
||||||
|
@ -177,6 +187,39 @@ namespace FSecure::C3::Linter
|
||||||
std::cout << "OK" << std::endl;
|
std::cout << "OK" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ChannelLinter::TestOverlapped(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data)
|
||||||
|
{
|
||||||
|
auto sender = channel.GetChunkSender(data);
|
||||||
|
auto receiver = complementary.GetChunkReceiver();
|
||||||
|
|
||||||
|
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
|
||||||
|
{
|
||||||
|
if (!sender.IsDone())
|
||||||
|
if (sender.Send())
|
||||||
|
noProgressCounter = 0;
|
||||||
|
|
||||||
|
if (receiver.Receive())
|
||||||
|
noProgressCounter = 0;
|
||||||
|
|
||||||
|
if (receiver.Size())
|
||||||
|
{
|
||||||
|
auto packets = receiver.GetPackets();
|
||||||
|
return ByteView{ packets[0] } == data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ChannelLinter::TestSequential(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data)
|
||||||
|
{
|
||||||
|
channel.Send(data);
|
||||||
|
auto received = complementary.Receive();
|
||||||
|
return data == ByteView{ received[0] };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void ChannelLinter::TestCommand(std::shared_ptr<MockDeviceBridge> const& channel)
|
void ChannelLinter::TestCommand(std::shared_ptr<MockDeviceBridge> const& channel)
|
||||||
{
|
{
|
||||||
assert(m_Config.m_Command);
|
assert(m_Config.m_Command);
|
||||||
|
|
|
@ -26,10 +26,38 @@ namespace FSecure::C3::Linter
|
||||||
void TestCommand(std::shared_ptr<MockDeviceBridge> const& channel);
|
void TestCommand(std::shared_ptr<MockDeviceBridge> const& channel);
|
||||||
|
|
||||||
/// Test channel pair permeability
|
/// Test channel pair permeability
|
||||||
/// @param first of complementary channels
|
/// @param channel first of channels. Used to send data.
|
||||||
/// @param second of complementary channels
|
/// @param complementary second of channels. Used to receive data.
|
||||||
|
/// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks .
|
||||||
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
||||||
void TestChannelIO(std::shared_ptr<MockDeviceBridge> const& channel, std::shared_ptr<MockDeviceBridge> const& ch2);
|
void TestChannelIO(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped = false);
|
||||||
|
|
||||||
|
/// Test maximal packet size that can be delivered by channel.
|
||||||
|
/// @param channel first of channels. Used to send data.
|
||||||
|
/// @param complementary second of channels. Used to receive data.
|
||||||
|
/// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks .
|
||||||
|
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
||||||
|
void TestChannelMTU(MockDeviceBridge& channel, MockDeviceBridge& complementary, bool overlapped = false);
|
||||||
|
|
||||||
|
/// Test maximal packet size if read/write operations are overlapped.
|
||||||
|
/// @param channel first of channels. Used to send data.
|
||||||
|
/// @param complementary second of channels. Used to receive data.
|
||||||
|
/// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks .
|
||||||
|
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
||||||
|
bool TestOverlapped(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data);
|
||||||
|
|
||||||
|
/// Test maximal packet size if all chunks must be written before first read.
|
||||||
|
/// @param channel first of channels. Used to send data.
|
||||||
|
/// @param complementary second of channels. Used to receive data.
|
||||||
|
/// @param overlapped informs if operations should be performed overlapped, or first read must be preceded by write of all chunks .
|
||||||
|
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
||||||
|
bool TestSequential(MockDeviceBridge& channel, MockDeviceBridge& complementary, ByteView data);
|
||||||
|
|
||||||
|
/// Test if packets are received in the same order as were passed to send.
|
||||||
|
/// @param channel first of channels. Used to send data.
|
||||||
|
/// @param complementary second of channels. Used to receive data.
|
||||||
|
/// @throws if Channel::OnSend or Channel::OnReceive throws
|
||||||
|
void TestChannelOrder(MockDeviceBridge& channel, MockDeviceBridge& complementary);
|
||||||
|
|
||||||
/// Create channel from string channel arguments
|
/// Create channel from string channel arguments
|
||||||
/// @param channel arguments
|
/// @param channel arguments
|
||||||
|
|
|
@ -92,14 +92,13 @@ namespace FSecure::C3::Linter
|
||||||
|
|
||||||
void MockDeviceBridge::Send(ByteView blob)
|
void MockDeviceBridge::Send(ByteView blob)
|
||||||
{
|
{
|
||||||
auto packetSplitter = m_QoS.GetPacketSplitter(blob);
|
auto sender = GetChunkSender(blob);
|
||||||
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
|
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
|
||||||
{
|
{
|
||||||
auto sent = GetDevice()->OnSendToChannelInternal(packetSplitter.NextChunk());
|
if (sender.Send())
|
||||||
if (packetSplitter.Update(sent))
|
|
||||||
noProgressCounter = 0;
|
noProgressCounter = 0;
|
||||||
|
|
||||||
if (!packetSplitter.HasMore())
|
if (sender.IsDone())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,23 +107,76 @@ namespace FSecure::C3::Linter
|
||||||
|
|
||||||
std::vector<FSecure::ByteVector> MockDeviceBridge::Receive(size_t minExpectedSize)
|
std::vector<FSecure::ByteVector> MockDeviceBridge::Receive(size_t minExpectedSize)
|
||||||
{
|
{
|
||||||
auto packets = std::vector<ByteVector>{};
|
auto receiver = GetChunkReceiver();
|
||||||
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
|
for (auto noProgressCounter = 0; noProgressCounter < 10; ++noProgressCounter)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(GetDevice()->GetUpdateDelay());
|
if (receiver.Receive())
|
||||||
for (auto&& chunk : std::static_pointer_cast<C3::AbstractChannel>(GetDevice())->OnReceiveFromChannelInternal())
|
noProgressCounter = 0;
|
||||||
{
|
|
||||||
if (m_QoS.PushReceivedChunk(chunk))
|
|
||||||
noProgressCounter = 0;
|
|
||||||
|
|
||||||
if (auto packet = m_QoS.GetNextPacket(); !packet.empty()) // this form will ensure that packets are returned in same order they are available.
|
if (receiver.Size() >= minExpectedSize)
|
||||||
packets.emplace_back(std::move(packet));
|
return receiver.GetPackets();
|
||||||
}
|
|
||||||
|
|
||||||
if (packets.size() >= minExpectedSize)
|
|
||||||
return packets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("Cannot receive data");
|
throw std::runtime_error("Cannot receive data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FSecure::C3::Linter::MockDeviceBridge::ChunkSender MockDeviceBridge::GetChunkSender(ByteView blob)
|
||||||
|
{
|
||||||
|
return { *m_Device.get(), m_QoS, blob };
|
||||||
|
}
|
||||||
|
|
||||||
|
FSecure::C3::Linter::MockDeviceBridge::ChunkReceiver MockDeviceBridge::GetChunkReceiver()
|
||||||
|
{
|
||||||
|
return { *m_Device.get(), m_QoS };
|
||||||
|
}
|
||||||
|
|
||||||
|
MockDeviceBridge::ChunkSender::ChunkSender(Device& device, QualityOfService& qos, ByteView blob)
|
||||||
|
: m_Device{ device }, m_Splitter{ qos.GetPacketSplitter(blob) }
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MockDeviceBridge::ChunkSender::Send()
|
||||||
|
{
|
||||||
|
auto sent = m_Device.OnSendToChannelInternal(m_Splitter.NextChunk());
|
||||||
|
return m_Splitter.Update(sent);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MockDeviceBridge::ChunkSender::IsDone()
|
||||||
|
{
|
||||||
|
return !m_Splitter.HasMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MockDeviceBridge::ChunkReceiver::ChunkReceiver(Device& device, QualityOfService& qos)
|
||||||
|
: m_Device{ device }, m_QoS{ qos }
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MockDeviceBridge::ChunkReceiver::Receive()
|
||||||
|
{
|
||||||
|
auto ret = false;
|
||||||
|
std::this_thread::sleep_for(m_Device.GetUpdateDelay());
|
||||||
|
for (auto&& chunk : static_cast<C3::AbstractChannel&>(m_Device).OnReceiveFromChannelInternal())
|
||||||
|
{
|
||||||
|
if (m_QoS.PushReceivedChunk(chunk))
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
if (auto packet = m_QoS.GetNextPacket(); !packet.empty()) // this form will ensure that packets are returned in same order they are available.
|
||||||
|
m_Packets.emplace_back(std::move(packet));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MockDeviceBridge::ChunkReceiver::Size()
|
||||||
|
{
|
||||||
|
return m_Packets.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<FSecure::ByteVector> const& MockDeviceBridge::ChunkReceiver::GetPackets()
|
||||||
|
{
|
||||||
|
return m_Packets;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,71 @@ namespace FSecure::C3::Linter
|
||||||
/// @throws std::runtime_error if unable to return vector of required number of full packets.
|
/// @throws std::runtime_error if unable to return vector of required number of full packets.
|
||||||
std::vector<ByteVector> Receive(size_t minExpectedSize = 1);
|
std::vector<ByteVector> Receive(size_t minExpectedSize = 1);
|
||||||
|
|
||||||
|
/// Abstraction over sending chunks until all of data is transmitted.
|
||||||
|
/// This class does not participate in resource ownership.
|
||||||
|
/// Transmitting device, QoS object as well as data must be valid as long as ChunkSender is used.
|
||||||
|
class ChunkSender
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor creating object responsible for sending blob of data in chunks.
|
||||||
|
/// @param device transmitting data over channel.
|
||||||
|
/// @param qos responsible for splitting data in chunks with correct identification headers.
|
||||||
|
/// @param blob data.
|
||||||
|
ChunkSender(Device& device, QualityOfService& qos, ByteView blob);
|
||||||
|
|
||||||
|
/// Sends data.
|
||||||
|
/// @return true if at least one valid chunk was send.
|
||||||
|
bool Send();
|
||||||
|
|
||||||
|
/// All of the data was correctly send.
|
||||||
|
/// @return true if there is no more data.
|
||||||
|
bool IsDone();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Device& m_Device;
|
||||||
|
QualityOfService::PacketSplitter m_Splitter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Abstraction over receiving data.
|
||||||
|
/// This class does not participate in resource ownership.
|
||||||
|
/// Transmitting device and QoS object must be valid as long as ChunkReceiver is used.
|
||||||
|
class ChunkReceiver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor creating object responsible for receiving data in chunks.
|
||||||
|
/// @param device receiving data from channel.
|
||||||
|
/// @param qos responsible for merging data from chunks.
|
||||||
|
ChunkReceiver(Device& device, QualityOfService& qos);
|
||||||
|
|
||||||
|
/// Receive data.
|
||||||
|
/// @return true if at least one valid chunk was received.
|
||||||
|
bool Receive();
|
||||||
|
|
||||||
|
/// Number of merged complete packets.
|
||||||
|
/// @return packet count.
|
||||||
|
size_t Size();
|
||||||
|
|
||||||
|
/// Get all packets.
|
||||||
|
/// Packet sequential number is not used for reordering.
|
||||||
|
/// Packets are returned in same order, as were available for merging from chunks.
|
||||||
|
/// @return complete packets.
|
||||||
|
std::vector<ByteVector> const& GetPackets();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Device& m_Device;
|
||||||
|
QualityOfService& m_QoS;
|
||||||
|
std::vector<ByteVector> m_Packets;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create ChunkSender using current bridge to send data.
|
||||||
|
/// @param blob data.
|
||||||
|
/// @return new ChunkSender.
|
||||||
|
ChunkSender GetChunkSender(ByteView blob);
|
||||||
|
|
||||||
|
/// Create ChunkReceiver using current bridge to receive data.
|
||||||
|
/// @return ChunkReceiver.
|
||||||
|
ChunkReceiver GetChunkReceiver();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Bridged device
|
/// Bridged device
|
||||||
std::shared_ptr<Device> m_Device;
|
std::shared_ptr<Device> m_Device;
|
||||||
|
|
Loading…
Reference in New Issue