Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ec42e11
The invalidAttributeType added to enum class Error declaration.
HelenMamontova Dec 8, 2025
273e007
The enum class ValueType and function make added to the class Attribute
HelenMamontova Dec 8, 2025
3710e59
The case Error::invalidAttributeType added to switch in the function
HelenMamontova Dec 8, 2025
d7ad025
Function Attribute::make definition added.
HelenMamontova Dec 8, 2025
735836f
The case Error::typeIsNotSupported added to switch in the function me…
HelenMamontova Dec 10, 2025
d964a8e
Exception added if type == vsa in the function make.
HelenMamontova Dec 10, 2025
0826ceb
The typeIsNotSupported added to enum class Error declaration.
HelenMamontova Dec 10, 2025
d500b09
The "vsa" check removed in make function make.
HelenMamontova Dec 16, 2025
514d485
The parseValueType function definition and call in the function make
HelenMamontova Dec 17, 2025
30ed640
The variant default added to switch(valueType).
HelenMamontova Dec 17, 2025
906bdbf
Case Error::invalidValueTypeMember and error message added to the
HelenMamontova Dec 17, 2025
353e0da
The element invalidValueTypeMember added to enum class Error.
HelenMamontova Dec 17, 2025
4022d0f
The check "vsa" added to parseValueType function.
HelenMamontova Dec 17, 2025
51263e8
The case Error::invalidValueTypeMember and error message changed in the
HelenMamontova Dec 22, 2025
46976a1
The throw RadProto::Exception(RadProto::Error::invalidAttributeType)
HelenMamontova Dec 22, 2025
8d6e68f
The invalidValueTypeMember changed to invalidValueType in enum class
HelenMamontova Dec 22, 2025
57868ec
The parseValueType function declaration added.
HelenMamontova Dec 22, 2025
29e7b8b
The enum class ValueType added, using ValueType removed.
HelenMamontova Dec 23, 2025
fa61b6d
The enum class ValueType removed.
HelenMamontova Dec 23, 2025
2d1b0ff
The parseValueType function and enum class ValueType moved to anonymous
HelenMamontova Dec 24, 2025
9b4966c
The invalidHexStringLength added to enum class Error.
HelenMamontova Jan 13, 2026
c3826f2
The case Error::invalidHexStringLength added in the function message.
HelenMamontova Jan 13, 2026
d814d73
The header file <iostream> removed, function parseValueType fixed. The
HelenMamontova Jan 13, 2026
8f32fec
The test suite AttributeMakeTests added.
HelenMamontova Jan 13, 2026
1319b72
The tests for methods of class String added to test case TypeStringTest.
HelenMamontova Jan 13, 2026
c8f3c1e
The tests for methods of class Integer added to test case TypeInteger…
HelenMamontova Jan 13, 2026
e96a9bd
The names of case changed in test suite AttributeMakeTests.
HelenMamontova Jan 13, 2026
170c194
The tests for methods of class Integer added to test case TypeDateInt…
HelenMamontova Jan 13, 2026
31dbc9c
The tests for methods of class IpAddress added to test case TypeIpaddr.
HelenMamontova Jan 14, 2026
ef898c1
The tests for methods of class Encrypted added to test case
HelenMamontova Jan 14, 2026
de0f9af
The tests for methods of class Bytes added to test case TypeOctets.
HelenMamontova Jan 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/radproto/attribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace RadProto
virtual std::string toString() const = 0;
virtual std::vector<uint8_t> toVector(const std::string& secret, const std::array<uint8_t, 16>& auth) const = 0;
virtual Attribute* clone() const = 0;
static Attribute* make(uint8_t code, const std::string& type, const std::string& data);
private:
uint8_t m_code;
};
Expand Down
6 changes: 5 additions & 1 deletion include/radproto/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ namespace RadProto
eapMessageAttributeError,
invalidAttributeCode,
invalidAttributeSize,
invalidAttributeType,
invalidValueType,
invalidVendorSpecificAttributeId,
suchAttributeNameAlreadyExists,
suchAttributeCodeAlreadyExists,
suchAttributeNameWithAnotherTypeAlreadyExists
suchAttributeNameWithAnotherTypeAlreadyExists,
typeIsNotSupported,
invalidHexStringLength
};

class Exception: public std::runtime_error
Expand Down
80 changes: 79 additions & 1 deletion src/attribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,94 @@
#include "attribute.h"
#include "utils.h"
#include "error.h"
#include <boost/tokenizer.hpp>
#include <openssl/evp.h>
#include <algorithm>
#include <iostream>

using Attribute = RadProto::Attribute;
namespace
{
enum class ValueType : uint8_t
{
String,
Integer,
IpAddress,
Encrypted,
Bytes,
VendorSpecific,
ChapPassword
};

ValueType parseValueType(const std::string& type)
{
if (type == "string")
return ValueType::String;
else if (type == "integer" || type == "date")
return ValueType::Integer;
else if (type == "ipaddr")
return ValueType::IpAddress;
else if (type == "encrypted")
return ValueType::Encrypted;
else if (type == "octets")
return ValueType::Bytes;
else if (type == "vsa")
throw RadProto::Exception(RadProto::Error::typeIsNotSupported);
else
throw RadProto::Exception(RadProto::Error::invalidValueType);
}
}

Attribute::Attribute(uint8_t code)
: m_code(code)
{
}

Attribute* Attribute::make(uint8_t code, const std::string& type, const std::string& data)
{
ValueType valueType = parseValueType(type);

switch (valueType)
{
case ValueType::String:
return new String(code, data);
case ValueType::Integer:
return new Integer(code, std::stoul(data));
case ValueType::Encrypted:
return new Encrypted(code, data);
case ValueType::IpAddress:
{
using tokenizer = boost::tokenizer<boost::char_separator<char>>;
boost::char_separator<char> sep(".");
tokenizer tok(data, sep);

std::array<uint8_t, 4> ipAddr;
size_t i = 0;
for (const auto& t : tok)
{
ipAddr[i] = static_cast<uint8_t>(std::stoul(t));
++i;
}
return new IpAddress(code, ipAddr);
}
case ValueType::Bytes:
{
if (data.length() % 2 != 0)
throw RadProto::Exception(RadProto::Error::invalidHexStringLength);

std::vector<uint8_t> bytes;

for (size_t i = 0; i < data.length(); i += 2)
{
auto byte = static_cast<uint8_t>(std::stoi(data.substr(i, 2), nullptr, 16));
bytes.push_back(byte);
}
return new Bytes(code, bytes);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no default variant. What will happen if the type is ChapPassword or VendorSpecific?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or if the type is neither of the enum values?

default:
throw RadProto::Exception(RadProto::Error::invalidValueType);
}
}

using String = RadProto::String;
String::String(uint8_t code, const uint8_t* data, size_t size)
: Attribute(code),
Expand Down
10 changes: 9 additions & 1 deletion src/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ std::string ErrorCategory::message(int ev) const noexcept
return "Invalid attribute code";
case Error::invalidAttributeSize:
return "Invalid attribute size";
case Error::invalidAttributeType:
return "Invalid attribute type";
case Error::invalidValueType:
return "Invalid type of enum ValueType";
case Error::invalidVendorSpecificAttributeId:
return "Invalid Vendor Specific attribute Id";
case Error::suchAttributeNameAlreadyExists:
Expand All @@ -45,7 +49,11 @@ std::string ErrorCategory::message(int ev) const noexcept
return "Such attribute code already exists";
case Error::suchAttributeNameWithAnotherTypeAlreadyExists:
return "Such attribute name with another type already exists";
default:
case Error::typeIsNotSupported:
return "Type 'vsa' is not supported in this class";
case Error::invalidHexStringLength:
return "Invalid length of hex string";
default:
return "(Unrecognized error)";
}
}
Expand Down
107 changes: 107 additions & 0 deletions tests/attribute_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,113 @@

BOOST_AUTO_TEST_SUITE(AttributeTests)

BOOST_AUTO_TEST_SUITE(AttributeMakeTests)

BOOST_AUTO_TEST_CASE(TypeString)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(1, "string", "User");
RadProto::String* str = dynamic_cast<RadProto::String*>(attribute);

BOOST_REQUIRE(str);

BOOST_CHECK_EQUAL(str->toString(), "User");

std::vector<uint8_t> values = str->toVector({}, {});
std::vector<uint8_t> expected {1, 6, 'U', 's', 'e', 'r'};

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(str->code(), 1);
}

BOOST_AUTO_TEST_CASE(TypeInteger)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(5, "integer", "169090600");
RadProto::Integer* intg = dynamic_cast<RadProto::Integer*>(attribute);

BOOST_REQUIRE(intg);

BOOST_CHECK_EQUAL(intg->toString(), "169090600");

std::vector<uint8_t> values = intg->toVector({}, {});
std::vector<uint8_t> expected {5, 6, 10, 20, 30, 40};

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(intg->code(), 5);
}

BOOST_AUTO_TEST_CASE(TypeDateToInteger)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(75, "date", "169090600");
RadProto::Integer* intg = dynamic_cast<RadProto::Integer*>(attribute);

BOOST_REQUIRE(intg);

BOOST_CHECK_EQUAL(intg->toString(), "169090600");

std::vector<uint8_t> values = intg->toVector({}, {});
std::vector<uint8_t> expected {75, 6, 10, 20, 30, 40};

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(intg->code(), 75);
}

BOOST_AUTO_TEST_CASE(TypeIpaddr)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(4, "ipaddr", "127.104.22.17");
RadProto::IpAddress* ipadr = dynamic_cast<RadProto::IpAddress*>(attribute);

BOOST_REQUIRE(ipadr);

BOOST_CHECK_EQUAL(ipadr->toString(), "127.104.22.17");

std::vector<uint8_t> values = ipadr->toVector({}, {});
std::vector<uint8_t> expected {4, 6, 127, 104, 22, 17};

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(ipadr->code(), 4);
}

BOOST_AUTO_TEST_CASE(TypeEncrypted)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(2, "encrypted", "123456");
RadProto::Encrypted* encrypt = dynamic_cast<RadProto::Encrypted*>(attribute);

BOOST_REQUIRE(encrypt);

BOOST_CHECK_EQUAL(encrypt->toString(), "123456");

std::array<uint8_t, 16> auth {0x92, 0xfa, 0xa1, 0xed, 0x98, 0x9b, 0xb4, 0x79, 0xfe, 0x20, 0xe2, 0xf4, 0x7f, 0x4a, 0x5a, 0x70};
std::vector<uint8_t> values = encrypt->toVector("secret", auth);
std::vector<uint8_t> expected {0x02, 0x12, 0x25, 0x38, 0x58, 0x18, 0xae, 0x97, 0xeb, 0xeb, 0xbd, 0x46, 0xfd, 0xb9, 0xd1, 0x17, 0x84, 0xeb};

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(encrypt->code(), 2);
}

BOOST_AUTO_TEST_CASE(TypeOctets)
{
RadProto::Attribute* attribute = RadProto::Attribute::make(3, "octets", "313233616263");
RadProto::Bytes* bts = dynamic_cast<RadProto::Bytes*>(attribute);

BOOST_REQUIRE(bts);

BOOST_CHECK_EQUAL(bts->toString(), "313233616263");

std::vector<uint8_t> values = bts->toVector({}, {});
std::vector<uint8_t> expected({3, 8, '1', '2', '3', 'a', 'b', 'c'});

BOOST_TEST(values == expected, boost::test_tools::per_element());

BOOST_CHECK_EQUAL(bts->code(), 3);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_CASE(StringDataConstructor)
{
std::vector<uint8_t> d {'t', 'e', 's', 't'};
Expand Down
Loading