Commit 60feddc3 authored by Francis Dupont's avatar Francis Dupont

[4070] Began tuple code

parent a6621652
......@@ -1250,6 +1250,7 @@ It is merely echoed by the server
<row><entry>psid</entry><entry>PSID and PSID length separated by a slash, e.g. 3/4 specifies PSID=3 and PSID length=4. In the wire format it is represented by an 8-bit field carrying PSID length (in this case equal to 4) and the 16-bits long PSID value field (in this case equal to "0011000000000000b" using binary notation). Allowed values for a PSID length are 0 to 16. See <ulink url="http://tools.ietf.org/html/rfc7597">RFC 7597</ulink> for the details about the PSID wire representation</entry></row>
<row><entry>record</entry><entry>Structured data that may comprise any types (except "record" and "empty")</entry></row>
<row><entry>string</entry><entry>Any text</entry></row>
<row><entry>tuple</entry><entry>A length encoded as a 8 (16 for DHCPv6) bit unsigned integer followed by a string of this length</entry></row>
<row><entry>uint8</entry><entry>8 bit unsigned integer with allowed values 0 to 255</entry></row>
<row><entry>uint16</entry><entry>16 bit unsigned integer with allowed values 0 to 65535</entry></row>
<row><entry>uint32</entry><entry>32 bit unsigned integer with allowed values 0 to 4294967295</entry></row>
......
......@@ -1173,7 +1173,7 @@ temporarily override a list of interface names and listen on all interfaces.
<row><entry>lq-relay-data</entry><entry>47</entry><entry>record (ipv6-address, binary)</entry><entry>false</entry></row>
<row><entry>lq-client-link</entry><entry>48</entry><entry>ipv6-address</entry><entry>true</entry></row>
<row><entry>bootfile-url</entry><entry>59</entry><entry>string</entry><entry>false</entry></row>
<row><entry>bootfile-param</entry><entry>60</entry><entry>binary</entry><entry>false</entry></row>
<row><entry>bootfile-param</entry><entry>60</entry><entry>tuple</entry><entry>true</entry></row>
<row><entry>client-arch-type</entry><entry>61</entry><entry>uint16</entry><entry>true</entry></row>
<row><entry>nii</entry><entry>62</entry><entry>record (uint8, uint8, uint8)</entry><entry>false</entry></row>
<row><entry>aftr-name</entry><entry>64</entry><entry>fqdn</entry><entry>false</entry></row>
......
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -64,6 +64,17 @@ OptionCustom::addArrayDataField(const IOAddress& address) {
buffers_.push_back(buf);
}
void
OptionCustom::addArrayDataField(const std::string& value) {
checkArrayType();
OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
OptionBuffer buf;
OptionDataTypeUtil::writeTuple(value, lft, buf);
buffers_.push_back(buf);
}
void
OptionCustom::addArrayDataField(const bool value) {
checkArrayType();
......@@ -244,7 +255,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
// that the validate() function in OptionDefinition object
// should have checked wheter it is a case for this option.
data_size = std::distance(data, data_buf.end());
} else if (*field == OPT_IPV6_PREFIX_TYPE ) {
} else if (*field == OPT_IPV6_PREFIX_TYPE) {
// The size of the IPV6 prefix type is determined as
// one byte (which is the size of the prefix in bits)
// followed by the prefix bits (right-padded with
......@@ -252,6 +263,18 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
if (std::distance(data, data_buf.end()) > 0) {
data_size = static_cast<size_t>(sizeof(uint8_t) + (*data + 7) / 8);
}
} else if (*field == OPT_TUPLE_TYPE) {
OpaqueDataTuple::LengthFieldType lft =
getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE :
OpaqueDataTuple::LENGTH_2_BYTES;
std::string value =
OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
lft);
data_size = value.size();
// The size of the buffer holding a tuple is always
// 1 or 2 byte larger than the size of the string
data_size += getUniverse() == Option::V4 ? 1 : 2;
} else {
// If we reached the end of buffer we assume that this option is
// truncated because there is no remaining data to initialize
......@@ -314,6 +337,19 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
// Data size comprises 1 byte holding a prefix length and the
// prefix length (in bytes) rounded to the nearest byte boundary.
data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
} else if (data_type == OPT_TUPLE_TYPE) {
OpaqueDataTuple::LengthFieldType lft =
getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE :
OpaqueDataTuple::LENGTH_2_BYTES;
std::string value =
OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
lft);
data_size = value.size();
// The size of the buffer holding a tuple is always
// 1 or 2 byte larger than the size of the string
data_size += getUniverse() == Option::V4 ? 1 : 2;
}
// We don't perform other checks for data types that can't be
// used together with array indicator such as strings, empty field
......@@ -351,6 +387,19 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
data_size = static_cast<size_t>
(sizeof(uint8_t) + (data_buf[0] + 7) / 8);
}
} else if (data_type == OPT_TUPLE_TYPE) {
OpaqueDataTuple::LengthFieldType lft =
getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE :
OpaqueDataTuple::LENGTH_2_BYTES;
std::string value =
OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
lft);
data_size = value.size();
// The size of the buffer holding a tuple is always
// 1 or 2 byte larger than the size of the string
data_size += getUniverse() == Option::V4 ? 1 : 2;
} else {
data_size = std::distance(data, data_buf.end());
}
......@@ -415,6 +464,9 @@ OptionCustom::dataFieldToText(const OptionDataType data_type,
case OPT_FQDN_TYPE:
text << "\"" << readFqdn(index) << "\"";
break;
case OPT_TUPLE_TYPE:
text << "\"" << readTuple(index) << "\"";
break;
case OPT_STRING_TYPE:
text << "\"" << readString(index) << "\"";
break;
......@@ -499,6 +551,24 @@ OptionCustom::writeBinary(const OptionBuffer& buf,
buffers_[index] = buf;
}
std::string
OptionCustom::readTuple(const uint32_t index) const {
checkIndex(index);
OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
return (OptionDataTypeUtil::readTuple(buffers_[index], lft));
}
void
OptionCustom::writeTuple(const std::string& value, const uint32_t index) {
checkIndex(index);
buffers_[index].clear();
OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
OptionDataTypeUtil::writeTuple(value, lft, buffers_[index]);
}
bool
OptionCustom::readBoolean(const uint32_t index) const {
checkIndex(index);
......
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -113,6 +113,11 @@ public:
buffers_.push_back(buf);
}
/// @brief Create new buffer and store tuple value in it
///
/// @param value value to be stored as a tuple in the created buffer.
void addArrayDataField(const std::string& value);
/// @brief Create new buffer and store variable length prefix in it.
///
/// @param prefix_len Prefix length.
......@@ -163,6 +168,20 @@ public:
/// @param index buffer index.
void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);
/// @brief Read a buffer as length and string tuple.
///
/// @param index buffer index.
///
/// @throw isc::OutOfRange if index is out of range.
/// @return string read from a buffer.
std::string readTuple(const uint32_t index = 0) const;
/// @brief Write a length and string tuple into a buffer.
///
/// @param value value to be written.
/// @param index buffer index.
void writeTuple(const std::string& value, const uint32_t index = 0);
/// @brief Read a buffer as boolean value.
///
/// @param index buffer index.
......
......@@ -9,6 +9,7 @@
#include <dns/name.h>
#include <util/encode/hex.h>
#include <algorithm>
#include <limits>
using namespace isc::asiolink;
......@@ -30,6 +31,7 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
data_types_["ipv6-prefix"] = OPT_IPV6_PREFIX_TYPE;
data_types_["psid"] = OPT_PSID_TYPE;
data_types_["string"] = OPT_STRING_TYPE;
data_types_["tuple"] = OPT_TUPLE_TYPE;
data_types_["fqdn"] = OPT_FQDN_TYPE;
data_types_["record"] = OPT_RECORD_TYPE;
......@@ -47,6 +49,7 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
data_type_names_[OPT_IPV6_PREFIX_TYPE] = "ipv6-prefix";
data_type_names_[OPT_PSID_TYPE] = "psid";
data_type_names_[OPT_STRING_TYPE] = "string";
data_type_names_[OPT_TUPLE_TYPE] = "tuple";
data_type_names_[OPT_FQDN_TYPE] = "fqdn";
data_type_names_[OPT_RECORD_TYPE] = "record";
// The "unknown" data type is declared here so as
......@@ -141,7 +144,7 @@ OptionDataTypeUtil::readAddress(const std::vector<uint8_t>& buf,
return (IOAddress::fromBytes(AF_INET6, &buf[0]));
} else {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
"IP address. Invalid family: " << family);
<< " IP address. Invalid family: " << family);
}
}
......@@ -170,6 +173,77 @@ OptionDataTypeUtil::writeBinary(const std::string& hex_str,
buf.insert(buf.end(), binary.begin(), binary.end());
}
std::string
OptionDataTypeUtil::readTuple(const std::vector<uint8_t>& buf,
OpaqueDataTuple::LengthFieldType lengthfieldtype) {
if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
if (buf.size() < 1) {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
<< " tuple (length). Invalid buffer size: "
<< buf.size());
}
uint8_t len = buf[0];
if (buf.size() < 1 + len) {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
<< " tuple (length " << static_cast<unsigned>(len)
<< "). Invalid buffer size: " << buf.size());
}
std::string value;
value.resize(len);
std::memcpy(&value[0], &buf[1], len);
return (value);
} else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
if (buf.size() < 2) {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
<< " tuple (length). Invalid buffer size: "
<< buf.size());
}
uint16_t len = isc::util::readUint16(&buf[0], 2);
if (buf.size() < 2 + len) {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
<< " tuple (length " << len
<< "). Invalid buffer size: " << buf.size());
}
std::string value;
value.resize(len);
std::memcpy(&value[0], &buf[2], len);
return (value);
} else {
isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
<< " tuple. Invalid length type field: "
<< static_cast<unsigned>(lengthfieldtype));
}
}
void
OptionDataTypeUtil:: writeTuple(const std::string& value,
OpaqueDataTuple::LengthFieldType lengthfieldtype,
std::vector<uint8_t>& buf) {
if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
if (value.size() > std::numeric_limits<uint8_t>::max()) {
isc_throw(BadDataTypeCast, "invalid tuple value (size "
<< value.size() << " larger than "
<< std::numeric_limits<uint8_t>::max() << ")");
}
buf.push_back(static_cast<uint8_t>(value.size()));
} else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
if (value.size() > std::numeric_limits<uint16_t>::max()) {
isc_throw(BadDataTypeCast, "invalid tuple value (size "
<< value.size() << " larger than "
<< std::numeric_limits<uint16_t>::max() << ")");
}
buf.resize(buf.size() + 2);
isc::util::writeUint16(static_cast<uint16_t>(value.size()),
&buf[buf.size() - 2], 2);
} else {
isc_throw(BadDataTypeCast, "unable to write data to the buffer as"
<< " tuple. Invalid length type field: "
<< static_cast<unsigned>(lengthfieldtype));
}
buf.insert(buf.end(), value.begin(), value.end());
}
bool
OptionDataTypeUtil::readBool(const std::vector<uint8_t>& buf) {
if (buf.empty()) {
......
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -8,6 +8,7 @@
#define OPTION_DATA_TYPES_H
#include <asiolink/io_address.h>
#include <dhcp/opaque_data_tuple.h>
#include <dhcp/option.h>
#include <exceptions/exceptions.h>
#include <util/io_utilities.h>
......@@ -57,6 +58,7 @@ enum OptionDataType {
OPT_IPV6_PREFIX_TYPE,
OPT_PSID_TYPE,
OPT_STRING_TYPE,
OPT_TUPLE_TYPE,
OPT_FQDN_TYPE,
OPT_RECORD_TYPE,
OPT_UNKNOWN_TYPE
......@@ -382,6 +384,25 @@ public:
static void writeBinary(const std::string& hex_str,
std::vector<uint8_t>& buf);
/// @brief Read length and string tuple from a buffer.
///
/// @param buf input buffer.
/// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
/// @throw isc::dhcp::BadDataTypeCast when the data being read
/// is truncated.
/// @return string being read.
static std::string readTuple(const std::vector<uint8_t>& buf,
OpaqueDataTuple::LengthFieldType lengthfieldtype);
/// @brief Append length and string tuple to a buffer
///
/// @param value length and string tuple
/// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
/// @param [out] buf output buffer.
static void writeTuple(const std::string& value,
OpaqueDataTuple::LengthFieldType lengthfieldtype,
std::vector<uint8_t>& buf);
/// @brief Read boolean value from a buffer.
///
/// @param buf input buffer.
......
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -192,6 +192,69 @@ TEST_F(OptionDataTypesTest, writeBinary) {
EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
}
// The purpose of this test is to verify that the tuple value stored
TEST_F(OptionDataTypesTest, readTuple) {
// The string
std::string value = "hello world";
// Create an input buffer.
std::vector<uint8_t> buf;
// DHCPv4 tuples use 1 byte length
writeInt<uint8_t>(static_cast<uint8_t>(value.size()), buf);
writeString(value, buf);
// Read the string from the buffer.
std::string result;
ASSERT_NO_THROW(
result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_1_BYTE);
);
// Check that it is valid.
EXPECT_EQ(value, result);
buf.clear();
// DHCPv6 tuples use 2 byte length
writeInt<uint16_t>(static_cast<uint16_t>(value.size()), buf);
writeString(value, buf);
// Read the string from the buffer.
ASSERT_NO_THROW(
result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_2_BYTES);
);
// Check that it is valid.
EXPECT_EQ(value, result);
}
// The purpose of this test is to verify that a tuple value
// are correctly encoded in a buffer
TEST_F(OptionDataTypesTest, writeTuple) {
// The string
std::string value = "hello world";
// Create an output buffer.
std::vector<uint8_t> buf;
// Encode it in DHCPv4
OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_1_BYTE, buf);
// Check that it is valid.
ASSERT_EQ(value.size() + 1, buf.size());
std::vector<uint8_t> expected;
writeInt<uint8_t>(static_cast<uint8_t>(value.size()), expected);
writeString(value, expected);
EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
buf.clear();
// Encode it in DHCPv6
OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_2_BYTES, buf);
// Check that it is valid.
ASSERT_EQ(value.size() + 2, buf.size());
expected.clear();
writeInt<uint16_t>(static_cast<uint16_t>(value.size()), expected);
writeString(value, expected);
EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
}
// The purpose of this test is to verify that the boolean value stored
// in a buffer is correctly read from this buffer.
TEST_F(OptionDataTypesTest, readBool) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment