<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>
<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
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();
// 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
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
// 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
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());
}
case OPT_FQDN_TYPE:
text << "\"" << readFqdn(index) << "\"";
break;
+ case OPT_TUPLE_TYPE:
+ text << "\"" << readTuple(index) << "\"";
+ break;
case OPT_STRING_TYPE:
text << "\"" << readString(index) << "\"";
break;
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
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.
/// @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.
#include <dns/name.h>
#include <util/encode/hex.h>
#include <algorithm>
+#include <limits>
using namespace isc::asiolink;
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;
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
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);
}
}
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
#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>
OPT_IPV6_PREFIX_TYPE,
OPT_PSID_TYPE,
OPT_STRING_TYPE,
+ OPT_TUPLE_TYPE,
OPT_FQDN_TYPE,
OPT_RECORD_TYPE,
OPT_UNKNOWN_TYPE
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
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) {