diff --git a/Makefile b/Makefile index 484550a..ff087de 100644 --- a/Makefile +++ b/Makefile @@ -5,12 +5,23 @@ TEST_CXXFLAGS := $(CXXFLAGS) -I./googletest/include/ all: dpnet.dll -dpnet.dll: src/dpnet.o src/dpnet.def src/DirectPlay8Address.o src/DirectPlay8Peer.o src/network.o +check: tests/all-tests.exe + wine tests/all-tests.exe + +dpnet.dll: src/dpnet.o src/dpnet.def src/DirectPlay8Address.o src/DirectPlay8Peer.o src/network.o src/packet.o $(CXX) $(CXXFLAGS) -Wl,--enable-stdcall-fixup -shared -o $@ $^ -ldxguid -lws2_32 -static-libstdc++ -static-libgcc tests/DirectPlay8Address.exe: tests/DirectPlay8Address.o src/DirectPlay8Address.o googletest/src/gtest-all.o googletest/src/gtest_main.o $(CXX) $(TEST_CXXFLAGS) -o $@ $^ -ldxguid -lole32 -static-libstdc++ -static-libgcc +tests/PacketSerialiser.exe: tests/PacketSerialiser.o src/packet.o googletest/src/gtest-all.o googletest/src/gtest_main.o + $(CXX) $(TEST_CXXFLAGS) -o $@ $^ -ldxguid -lole32 -static-libstdc++ -static-libgcc + +tests/all-tests.exe: tests/DirectPlay8Address.o src/DirectPlay8Address.o \ + tests/PacketSerialiser.o tests/PacketDeserialiser.o src/packet.o \ + googletest/src/gtest-all.o googletest/src/gtest_main.o + $(CXX) $(TEST_CXXFLAGS) -o $@ $^ -ldxguid -lole32 -static-libstdc++ -static-libgcc + src/%.o: src/%.cpp $(CXX) $(CXXFLAGS) -c -o $@ $< diff --git a/src/packet.cpp b/src/packet.cpp new file mode 100644 index 0000000..1235be9 --- /dev/null +++ b/src/packet.cpp @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include + +#include "packet.hpp" + +const uint32_t FIELD_TYPE_NULL = 0; +const uint32_t FIELD_TYPE_DWORD = 1; +const uint32_t FIELD_TYPE_DATA = 2; +const uint32_t FIELD_TYPE_WSTRING = 3; + +PacketSerialiser::PacketSerialiser(uint32_t type) +{ + /* Avoid reallocations during packet construction unless we get given a lot of data. */ + sbuf.reserve(4096); + + TLVChunk header; + header.type = type; + header.value_length = 0; + + sbuf.insert(sbuf.begin(), (unsigned char*)(&header), (unsigned char*)(&header + 1)); +} + +std::pair PacketSerialiser::raw_packet() +{ + return std::make_pair(sbuf.data(), sbuf.size()); +} + +void PacketSerialiser::append_null() +{ + TLVChunk header; + header.type = FIELD_TYPE_NULL; + header.value_length = 0; + + sbuf.insert(sbuf.end(), (unsigned char*)(&header), (unsigned char*)(&header + 1)); + + ((TLVChunk*)(sbuf.data()))->value_length += sizeof(header); +} + +void PacketSerialiser::append_dword(DWORD value) +{ + TLVChunk header; + header.type = FIELD_TYPE_DWORD; + header.value_length = sizeof(DWORD); + + sbuf.insert(sbuf.end(), (unsigned char*)(&header), (unsigned char*)(&header + 1)); + sbuf.insert(sbuf.end(), (unsigned char*)(&value), (unsigned char*)(&value + 1)); + + ((TLVChunk*)(sbuf.data()))->value_length += sizeof(header) + sizeof(value); +} + +void PacketSerialiser::append_data(const void *data, size_t size) +{ + TLVChunk header; + header.type = FIELD_TYPE_DATA; + header.value_length = size; + + sbuf.insert(sbuf.end(), (unsigned char*)(&header), (unsigned char*)(&header + 1)); + sbuf.insert(sbuf.end(), (unsigned char*)(data), (unsigned char*)(data) + size); + + ((TLVChunk*)(sbuf.data()))->value_length += sizeof(header) + size; +} + +void PacketSerialiser::append_wstring(const std::wstring &string) +{ + size_t string_bytes = string.length() * sizeof(wchar_t); + + TLVChunk header; + header.type = FIELD_TYPE_WSTRING; + header.value_length = string_bytes; + + sbuf.insert(sbuf.end(), (unsigned char*)(&header), (unsigned char*)(&header + 1)); + sbuf.insert(sbuf.end(), (unsigned char*)(string.data()), (unsigned char*)(string.data()) + string_bytes); + + ((TLVChunk*)(sbuf.data()))->value_length += sizeof(header) + string_bytes; +} + +PacketDeserialiser::PacketDeserialiser(const void *serialised_packet, size_t packet_size) +{ + header = (const TLVChunk*)(serialised_packet); + + if(packet_size < sizeof(TLVChunk) || packet_size < sizeof(TLVChunk) + header->value_length) + { + throw Error::Incomplete(); + } + + const unsigned char *at = header->value; + size_t value_remain = header->value_length; + + while(value_remain > 0) + { + const TLVChunk *field = (TLVChunk*)(at); + + if(value_remain < sizeof(TLVChunk) || value_remain < sizeof(TLVChunk) + field->value_length) + { + throw Error::Malformed(); + } + + fields.push_back(field); + + at += sizeof(TLVChunk) + field->value_length; + value_remain -= sizeof(TLVChunk) + field->value_length; + } +} + +uint32_t PacketDeserialiser::packet_type() +{ + return header->type; +} + +size_t PacketDeserialiser::num_fields() +{ + return fields.size(); +} + +bool PacketDeserialiser::is_null(size_t index) +{ + if(fields.size() <= index) + { + throw Error::MissingField(); + } + + return (fields[index]->type == FIELD_TYPE_NULL); +} + +DWORD PacketDeserialiser::get_dword(size_t index) +{ + if(fields.size() <= index) + { + throw Error::MissingField(); + } + + if(fields[index]->type != FIELD_TYPE_DWORD) + { + throw Error::TypeMismatch(); + } + + if(fields[index]->value_length != sizeof(DWORD)) + { + throw Error::Malformed(); + } + + return *(DWORD*)(fields[index]->value); +} + +std::pair PacketDeserialiser::get_data(size_t index) +{ + if(fields.size() <= index) + { + throw Error::MissingField(); + } + + if(fields[index]->type != FIELD_TYPE_DATA) + { + throw Error::TypeMismatch(); + } + + return std::make_pair((const void*)(fields[index]->value), (size_t)(fields[index]->value_length)); +} + +std::wstring PacketDeserialiser::get_wstring(size_t index) +{ + if(fields.size() <= index) + { + throw Error::MissingField(); + } + + if(fields[index]->type != FIELD_TYPE_WSTRING) + { + throw Error::TypeMismatch(); + } + + if((fields[index]->value_length % sizeof(wchar_t)) != 0) + { + throw Error::Malformed(); + } + + return std::wstring((const wchar_t*)(fields[index]->value), (fields[index]->value_length / sizeof(wchar_t))); +} diff --git a/src/packet.hpp b/src/packet.hpp new file mode 100644 index 0000000..8daec2a --- /dev/null +++ b/src/packet.hpp @@ -0,0 +1,89 @@ +#ifndef DPLITE_PACKET_HPP +#define DPLITE_PACKET_HPP + +#include +#include +#include +#include +#include +#include +#include + +struct TLVChunk +{ + uint32_t type; + uint32_t value_length; + unsigned char value[0]; +}; + +class PacketSerialiser +{ + private: + std::vector sbuf; + + public: + PacketSerialiser(uint32_t type); + + std::pair raw_packet(); + + void append_null(); + void append_dword(DWORD value); + void append_data(const void *data, size_t size); + void append_wstring(const std::wstring &string); +}; + +class PacketDeserialiser +{ + private: + const TLVChunk *header; + std::vector fields; + + public: + class Error: public std::runtime_error + { + protected: + Error(const std::string &what): runtime_error(what) {} + + public: + class Incomplete; + class Malformed; + class MissingField; + class TypeMismatch; + }; + + PacketDeserialiser(const void *serialised_packet, size_t packet_size); + + uint32_t packet_type(); + size_t num_fields(); + + bool is_null(size_t index); + DWORD get_dword(size_t index); + std::pair get_data(size_t index); + std::wstring get_wstring(size_t index); +}; + +class PacketDeserialiser::Error::Incomplete: public Error +{ + public: + Incomplete(const std::string &what = "Incomplete packet"): Error(what) {} +}; + +class PacketDeserialiser::Error::Malformed: public Error +{ + public: + Malformed(const std::string &what = "Malformed packet"): Error(what) {} +}; + +class PacketDeserialiser::Error::MissingField: public Error +{ + public: + MissingField(const std::string &what = "Missing field in packet"): Error(what) {} +}; + +class PacketDeserialiser::Error::TypeMismatch: public Error +{ + public: + TypeMismatch(const std::string &what = "Incorrect field type in packet"): Error(what) {} +}; + +#endif /* !DPLITE_PACKET_HPP */ diff --git a/tests/PacketDeserialiser.cpp b/tests/PacketDeserialiser.cpp new file mode 100644 index 0000000..754d55d --- /dev/null +++ b/tests/PacketDeserialiser.cpp @@ -0,0 +1,560 @@ +#include + +#include "../src/packet.hpp" + +class PacketDeserialiserTest: public ::testing::Test { + protected: + PacketDeserialiser *pd; + + PacketDeserialiserTest(): pd(NULL) {} + + virtual ~PacketDeserialiserTest() + { + delete pd; + } +}; + +class PacketDeserialiserEmpty: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserEmpty, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(1)); +} + +TEST_F(PacketDeserialiserEmpty, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(0)); +} + +TEST_F(PacketDeserialiserEmpty, IsNull) +{ + EXPECT_THROW({ pd->is_null(0); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserEmpty, GetDWORD) +{ + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserEmpty, GetData) +{ + EXPECT_THROW({ pd->get_data(0); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserEmpty, GetWString) +{ + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::MissingField); +} + +class PacketDeserialiserNull: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x02, 0x00, 0x00, 0x00, /* type */ + 0x08, 0x00, 0x00, 0x00, /* value_length */ + + 0x00, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserNull, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(2)); +} + +TEST_F(PacketDeserialiserNull, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(1)); +} + +TEST_F(PacketDeserialiserNull, IsNull) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(0), true); }); + EXPECT_THROW({ pd->is_null(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNull, GetDWORD) +{ + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_dword(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNull, GetData) +{ + EXPECT_THROW({ pd->get_data(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNull, GetWString) +{ + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_wstring(1); }, PacketDeserialiser::Error::MissingField); +} + +class PacketDeserialiserDWORD: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x03, 0x00, 0x00, 0x00, /* type */ + 0x0C, 0x00, 0x00, 0x00, /* value_length */ + + 0x01, 0x00, 0x00, 0x00, /* type */ + 0x04, 0x00, 0x00, 0x00, /* value_length */ + 0x01, 0x23, 0x45, 0x67, /* value */ + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserDWORD, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(3)); +} + +TEST_F(PacketDeserialiserDWORD, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(1)); +} + +TEST_F(PacketDeserialiserDWORD, IsNull) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(0), false); }); + EXPECT_THROW({ pd->is_null(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserDWORD, GetDWORD) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->get_dword(0), (DWORD)(0x67452301)); }); + EXPECT_THROW({ pd->get_dword(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserDWORD, GetData) +{ + EXPECT_THROW({ pd->get_data(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserDWORD, GetWString) +{ + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_wstring(1); }, PacketDeserialiser::Error::MissingField); +} + +class PacketDeserialiserData: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x04, 0x00, 0x00, 0x00, /* type */ + 0x0E, 0x00, 0x00, 0x00, /* value_length */ + + 0x02, 0x00, 0x00, 0x00, /* type */ + 0x06, 0x00, 0x00, 0x00, /* value_length */ + 0xFE, 0xED, 0xBE, 0xEF, /* value */ + 0xAA, 0xAA, + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserData, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(4)); +} + +TEST_F(PacketDeserialiserData, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(1)); +} + +TEST_F(PacketDeserialiserData, IsNull) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(0), false); }); + EXPECT_THROW({ pd->is_null(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserData, GetDWORD) +{ + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_dword(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserData, GetData) +{ + const unsigned char EXPECT[] = { 0xFE, 0xED, 0xBE, 0xEF, 0xAA, 0xAA }; + std::pair got; + + ASSERT_NO_THROW({ got = pd->get_data(0); }); + + std::vector got_data ((const unsigned char*)(got.first), (const unsigned char*)(got.first) + got.second); + std::vector expect_data(EXPECT, EXPECT + sizeof(EXPECT)); + + EXPECT_EQ(got_data, expect_data); + + EXPECT_THROW({ pd->get_data(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserData, GetWString) +{ + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_wstring(1); }, PacketDeserialiser::Error::MissingField); +} + +class PacketDeserialiserWString: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x05, 0x00, 0x00, 0x00, /* type */ + 0x12, 0x00, 0x00, 0x00, /* value_length */ + + 0x03, 0x00, 0x00, 0x00, /* type */ + 0x0A, 0x00, 0x00, 0x00, /* value_length */ + 0x48, 0x00, 0x65, 0x00, /* value */ + 0x6C, 0x00, 0x6C, 0x00, + 0x6F, 0x00, + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserWString, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(5)); +} + +TEST_F(PacketDeserialiserWString, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(1)); +} + +TEST_F(PacketDeserialiserWString, IsNull) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(0), false); }); + EXPECT_THROW({ pd->is_null(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserWString, GetDWORD) +{ + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_dword(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserWString, GetData) +{ + EXPECT_THROW({ pd->get_data(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(1); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserWString, GetWString) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->get_wstring(0), L"Hello"); }); + EXPECT_THROW({ pd->get_wstring(1); }, PacketDeserialiser::Error::MissingField); +} + +class PacketDeserialiserNullDWORDDataWString: public PacketDeserialiserTest { + protected: + virtual void SetUp() override + { + static const unsigned char RAW[] = { + 0x34, 0x12, 0x00, 0x00, /* type */ + 0x31, 0x00, 0x00, 0x00, /* value_length (49) */ + + 0x00, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + + 0x01, 0x00, 0x00, 0x00, /* type */ + 0x04, 0x00, 0x00, 0x00, /* value_length */ + 0xFE, 0xED, 0x00, 0x00, /* value */ + + 0x02, 0x00, 0x00, 0x00, /* type */ + 0x05, 0x00, 0x00, 0x00, /* value_length */ + 0x01, 0x23, 0x45, 0x67, /* value */ + 0x89, + + 0x03, 0x00, 0x00, 0x00, /* type */ + 0x08, 0x00, 0x00, 0x00, /* value_length */ + 0x57, 0x00, 0x53, 0x00, /* value */ + 0x74, 0x00, 0x72, 0x00, + }; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + } +}; + +TEST_F(PacketDeserialiserNullDWORDDataWString, Type) +{ + EXPECT_EQ(pd->packet_type(), (uint32_t)(0x1234)); +} + +TEST_F(PacketDeserialiserNullDWORDDataWString, NumFields) +{ + EXPECT_EQ(pd->num_fields(), (size_t)(4)); +} + +TEST_F(PacketDeserialiserNullDWORDDataWString, IsNull) +{ + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(0), true); }); + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(1), false); }); + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(2), false); }); + EXPECT_NO_THROW({ EXPECT_EQ(pd->is_null(3), false); }); + + EXPECT_THROW({ pd->is_null(4); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNullDWORDDataWString, GetDWORD) +{ + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_NO_THROW({ EXPECT_EQ(pd->get_dword(1), (DWORD)(0xEDFE)); }); + EXPECT_THROW({ pd->get_dword(2); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_dword(3); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_dword(4); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNullDWORDDataWString, GetData) +{ + const unsigned char EXPECT[] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; + std::pair got; + + ASSERT_NO_THROW({ got = pd->get_data(2); }); + + std::vector got_data ((const unsigned char*)(got.first), (const unsigned char*)(got.first) + got.second); + std::vector expect_data(EXPECT, EXPECT + sizeof(EXPECT)); + + EXPECT_EQ(got_data, expect_data); + + EXPECT_THROW({ pd->get_data(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(1); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(3); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_data(4); }, PacketDeserialiser::Error::MissingField); +} + +TEST_F(PacketDeserialiserNullDWORDDataWString, GetWString) +{ + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_wstring(1); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_THROW({ pd->get_wstring(2); }, PacketDeserialiser::Error::TypeMismatch); + EXPECT_NO_THROW({ EXPECT_EQ(pd->get_wstring(3), L"WStr"); }); + EXPECT_THROW({ pd->get_wstring(4); }, PacketDeserialiser::Error::MissingField); +} + +TEST(PacketDeserialiser, NoData) +{ + EXPECT_THROW({ PacketDeserialiser p(NULL, 0); }, PacketDeserialiser::Error::Incomplete); +} + +TEST(PacketDeserialiser, PartialHeader) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + }; + + EXPECT_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }, PacketDeserialiser::Error::Incomplete); +} + +TEST(PacketDeserialiser, PartialData) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, + }; + + EXPECT_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }, PacketDeserialiser::Error::Incomplete); +} + +TEST(PacketDeserialiser, ExtraData) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x00 + }; + + EXPECT_NO_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }); +} + +TEST(PacketDeserialiser, FieldShortHeader) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + }; + + EXPECT_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }, PacketDeserialiser::Error::Malformed); +} + +TEST(PacketDeserialiser, FieldTooShort) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, + }; + + EXPECT_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }, PacketDeserialiser::Error::Malformed); +} + +TEST(PacketDeserialiser, FieldTooLong) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, + + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + + 0x00, + }; + + EXPECT_THROW({ PacketDeserialiser p(RAW, sizeof(RAW)); }, PacketDeserialiser::Error::Malformed); +} + +TEST(PacketDeserialiser, ZeroLengthDWORD) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::Malformed); + + delete pd; +} + +TEST(PacketDeserialiser, UndersizeDWORD) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x0B, 0x00, 0x00, 0x00, + + 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::Malformed); + + delete pd; +} + +TEST(PacketDeserialiser, OversizeDWORD) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x0D, 0x00, 0x00, 0x00, + + 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00 + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + EXPECT_THROW({ pd->get_dword(0); }, PacketDeserialiser::Error::Malformed); + + delete pd; +} + +TEST(PacketDeserialiser, ZeroLengthData) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + + 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + + EXPECT_NO_THROW({ + auto data = pd->get_data(0); + EXPECT_EQ(data.second, (size_t)(0)); + }); + + delete pd; +} + +TEST(PacketDeserialiser, ZeroLengthWString) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + EXPECT_NO_THROW({ EXPECT_EQ(pd->get_wstring(0), L""); }); + + delete pd; +} + +TEST(PacketDeserialiser, OneByteWString) +{ + const unsigned char RAW[] = { + 0x01, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, + + 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x00, + }; + + PacketDeserialiser *pd = NULL; + + ASSERT_NO_THROW({ pd = new PacketDeserialiser(RAW, sizeof(RAW)); }); + EXPECT_THROW({ pd->get_wstring(0); }, PacketDeserialiser::Error::Malformed); + + delete pd; +} diff --git a/tests/PacketSerialiser.cpp b/tests/PacketSerialiser.cpp new file mode 100644 index 0000000..9041dc3 --- /dev/null +++ b/tests/PacketSerialiser.cpp @@ -0,0 +1,167 @@ +#include + +#include "../src/packet.hpp" + +TEST(PacketSerialiser, Empty) +{ + PacketSerialiser p(0xAA); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0xAA, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +} + +TEST(PacketSerialiser, Null) +{ + PacketSerialiser p(0xBB); + p.append_null(); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0xBB, 0x00, 0x00, 0x00, /* type */ + 0x08, 0x00, 0x00, 0x00, /* value_length */ + + 0x00, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +} + +TEST(PacketSerialiser, DWORD) +{ + PacketSerialiser p(0xAABBCCDD); + p.append_dword(0xFFEEDDCC); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0xDD, 0xCC, 0xBB, 0xAA, /* type */ + 0x0C, 0x00, 0x00, 0x00, /* value_length */ + + 0x01, 0x00, 0x00, 0x00, /* type */ + 0x04, 0x00, 0x00, 0x00, /* value_length */ + 0xCC, 0xDD, 0xEE, 0xFF, /* value */ + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +} + +TEST(PacketSerialiser, Data) +{ + PacketSerialiser p(0x1234); + + const unsigned char DATA[] = { + 0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF, + }; + + p.append_data(DATA, sizeof(DATA)); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0x34, 0x12, 0x00, 0x00, /* type */ + 0x10, 0x00, 0x00, 0x00, /* value_length */ + + 0x02, 0x00, 0x00, 0x00, /* type */ + 0x08, 0x00, 0x00, 0x00, /* value_length */ + 0x01, 0x23, 0x45, 0x67, /* value */ + 0x89, 0xAB, 0xCD, 0xEF, + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +} + +TEST(PacketSerialiser, WString) +{ + PacketSerialiser p(0x1234); + + p.append_wstring(L"Hello, I'm Gabe Newell"); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0x34, 0x12, 0x00, 0x00, /* type */ + 0x34, 0x00, 0x00, 0x00, /* value_length (52) */ + + 0x03, 0x00, 0x00, 0x00, /* type */ + 0x2C, 0x00, 0x00, 0x00, /* value_length (44) */ + 0x48, 0x00, 0x65, 0x00, /* value */ + 0x6C, 0x00, 0x6C, 0x00, + 0x6F, 0x00, 0x2C, 0x00, + 0x20, 0x00, 0x49, 0x00, + 0x27, 0x00, 0x6D, 0x00, + 0x20, 0x00, 0x47, 0x00, + 0x61, 0x00, 0x62, 0x00, + 0x65, 0x00, 0x20, 0x00, + 0x4E, 0x00, 0x65, 0x00, + 0x77, 0x00, 0x65, 0x00, + 0x6C, 0x00, 0x6C, 0x00 + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +} + +TEST(PacketSerialiser, NullDWORDDataWString) +{ + PacketSerialiser p(0x1234); + + p.append_null(); + p.append_dword(0xEDFE); + + const unsigned char DATA[] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; + p.append_data(DATA, sizeof(DATA)); + + p.append_wstring(L"WStr"); + + std::pair raw = p.raw_packet(); + + const unsigned char EXPECT[] = { + 0x34, 0x12, 0x00, 0x00, /* type */ + 0x31, 0x00, 0x00, 0x00, /* value_length (49) */ + + 0x00, 0x00, 0x00, 0x00, /* type */ + 0x00, 0x00, 0x00, 0x00, /* value_length */ + + 0x01, 0x00, 0x00, 0x00, /* type */ + 0x04, 0x00, 0x00, 0x00, /* value_length */ + 0xFE, 0xED, 0x00, 0x00, /* value */ + + 0x02, 0x00, 0x00, 0x00, /* type */ + 0x05, 0x00, 0x00, 0x00, /* value_length */ + 0x01, 0x23, 0x45, 0x67, /* value */ + 0x89, + + 0x03, 0x00, 0x00, 0x00, /* type */ + 0x08, 0x00, 0x00, 0x00, /* value_length */ + 0x57, 0x00, 0x53, 0x00, /* value */ + 0x74, 0x00, 0x72, 0x00, + }; + + std::vector got((unsigned char*)(raw.first), (unsigned char*)(raw.first) + raw.second); + std::vector expect(EXPECT, EXPECT + sizeof(EXPECT)); + + ASSERT_EQ(got, expect); +}