--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option_opaque_data_tuples.h>
+#include <sstream>
+
+namespace isc {
+namespace dhcp {
+
+OptionOpaqueDataTuples::OptionOpaqueDataTuples(Option::Universe u,
+ const uint16_t type)
+ : Option(u, type) {
+}
+
+OptionOpaqueDataTuples::OptionOpaqueDataTuples(Option::Universe u,
+ const uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end)
+ : Option(u, type) {
+ unpack(begin, end);
+}
+
+void
+OptionOpaqueDataTuples::pack(isc::util::OutputBuffer& buf) {
+ packHeader(buf);
+
+ for (TuplesCollection::const_iterator it = tuples_.begin();
+ it != tuples_.end(); ++it) {
+ it->pack(buf);
+ }
+
+}
+
+void
+OptionOpaqueDataTuples::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ if (std::distance(begin, end) < getMinimalLength() - getHeaderLen()) {
+ isc_throw(OutOfRange, "parsed data tuples option data truncated to"
+ " size " << std::distance(begin, end));
+ }
+
+ // Start reading opaque data.
+ size_t offset = 0;
+ while (offset < std::distance(begin, end)) {
+ // Parse a tuple.
+ OpaqueDataTuple tuple(getLengthFieldType(), begin + offset, end);
+ addTuple(tuple);
+ // The tuple has been parsed correctly which implies that it is safe to
+ // advance the offset by its total length.
+ offset += tuple.getTotalLength();
+ }
+}
+
+void
+OptionOpaqueDataTuples::addTuple(const OpaqueDataTuple& tuple) {
+ if (tuple.getLengthFieldType() != getLengthFieldType()) {
+ isc_throw(isc::BadValue, "attempted to add opaque data tuple having"
+ " invalid size of the length field "
+ << tuple.getDataFieldSize() << " to Vendor Class option");
+ }
+
+ tuples_.push_back(tuple);
+}
+
+
+void
+OptionOpaqueDataTuples::setTuple(const size_t at, const OpaqueDataTuple& tuple) {
+ if (at >= getTuplesNum()) {
+ isc_throw(isc::OutOfRange, "attempted to set an opaque data for the"
+ " vendor option at position " << at << " which is out of"
+ " range");
+
+ } else if (tuple.getLengthFieldType() != getLengthFieldType()) {
+ isc_throw(isc::BadValue, "attempted to set opaque data tuple having"
+ " invalid size of the length field "
+ << tuple.getDataFieldSize() << " to Vendor Class option");
+ }
+
+ tuples_[at] = tuple;
+}
+
+OpaqueDataTuple
+OptionOpaqueDataTuples::getTuple(const size_t at) const {
+ if (at >= getTuplesNum()) {
+ isc_throw(isc::OutOfRange, "attempted to get an opaque data for the"
+ " vendor option at position " << at << " which is out of"
+ " range. There are only " << getTuplesNum() << " tuples");
+ }
+ return (tuples_[at]);
+}
+
+bool
+OptionOpaqueDataTuples::hasTuple(const std::string& tuple_str) const {
+ // Iterate over existing tuples (there shouldn't be many of them),
+ // and try to match the searched one.
+ for (TuplesCollection::const_iterator it = tuples_.begin();
+ it != tuples_.end(); ++it) {
+ if (*it == tuple_str) {
+ return (true);
+ }
+ }
+ return (false);
+}
+
+
+uint16_t
+OptionOpaqueDataTuples::len() {
+ // The option starts with the header.
+ uint16_t length = getHeaderLen();
+ // Now iterate over existing tuples and add their size.
+ for (TuplesCollection::const_iterator it = tuples_.begin();
+ it != tuples_.end(); ++it) {
+ length += it->getTotalLength();
+ }
+
+ return (length);
+}
+
+std::string
+OptionOpaqueDataTuples::toText(int indent) {
+ std::ostringstream s;
+
+ // Apply indentation
+ s << std::string(indent, ' ');
+
+
+
+ // Print type and length
+ s << "type=" << getType() << ", len=" << len() - getHeaderLen() << std::dec;
+ // Iterate over all tuples and print their size and contents.
+ for (unsigned i = 0; i < getTuplesNum(); ++i) {
+ // Print the tuple.
+ s << ", data-len" << i << "=" << getTuple(i).getLength();
+ s << ", data" << i << "='" << getTuple(i) << "'";
+ }
+
+ return (s.str());
+}
+
+} // namespace isc::dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef OPTION_OPAQUE_DATA_TUPLES_H
+#define OPTION_OPAQUE_DATA_TUPLES_H
+
+#include <dhcp/dhcp4.h>
+#include <dhcp/dhcp6.h>
+#include <dhcp/opaque_data_tuple.h>
+#include <dhcp/option.h>
+#include <util/buffer.h>
+#include <boost/shared_ptr.hpp>
+#include <stdint.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief This class encapsulates a collection of data tuples and could be
+/// used by multiple options. It is tailored for use with the DHCPv6
+/// Bootfile-param option (option 60).
+///
+/// The format of the option is described in section 3.2 of RFC5970.
+/// This option may carry an arbitrary number of tuples carrying opaque data.
+/// Each tuple consists of a field holding the length of the opaque data
+/// followed by a string containing the data itself. For option 60 each
+/// length field is 2 bytes long and the data is a UTF-8 string that is not
+/// null terminated.
+///
+/// @todo The class is similar to the class used by the DHCPv6 Vendor Class
+/// (16) and DHCPv4 V-I Vendor Class (124) options, though they include an
+/// enterprise (or vendor) ID in the option. In the future it may
+/// make sense to rewrite the OptionVendorClass to derive from this class.
+class OptionOpaqueDataTuples : public Option {
+public:
+
+ /// @brief Collection of opaque data tuples carried by the option.
+ typedef std::vector<OpaqueDataTuple> TuplesCollection;
+
+ /// @brief Constructor.
+ ///
+ /// This constructor creates an instance of an OpaqueDataTuple that can
+ /// be used for an option such as DHCPv6 Bootfile Parameters (60).
+ ///
+ /// @param u universe (v4 or v6).
+ /// @param type option type
+ OptionOpaqueDataTuples(Option::Universe u, const uint16_t type);
+
+ /// @brief Constructor.
+ ///
+ /// This constructor creates an instance of the option using a buffer with
+ /// on-wire data. It may throw an exception if the @c unpack method throws.
+ ///
+ /// @param u universe (v4 or v6)
+ /// @param type option type
+ /// @param begin Iterator pointing to the beginning of the buffer holding an
+ /// option.
+ /// @param end Iterator pointing to the end of the buffer holding an option.
+ OptionOpaqueDataTuples(Option::Universe u, const uint16_t type,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end);
+
+ /// @brief Renders option into the buffer in the wire format.
+ ///
+ /// @param [out] buf Buffer to which the option is rendered.
+ virtual void pack(isc::util::OutputBuffer& buf);
+
+ /// @brief Parses buffer holding an option.
+ ///
+ /// This function parses the buffer holding an option and initializes option
+ /// properties: the collection of tuples.
+ ///
+ /// @param begin Iterator pointing to the beginning of the buffer holding an
+ /// option.
+ /// @param end Iterator pointing to the end of the buffer holding an option.
+ virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end);
+
+ /// @brief Adds a new opaque data tuple to the option.
+ ///
+ /// @param tuple Tuple to be added.
+ /// @throw isc::BadValue if the type of the tuple doesn't match the
+ /// universe this option belongs to.
+ void addTuple(const OpaqueDataTuple& tuple);
+
+ /// @brief Replaces tuple at the specified index with a new tuple.
+ ///
+ /// This function replaces an opaque data tuple at the specified position
+ /// with the new tuple. If the specified index is out of range an exception
+ /// is thrown.
+ ///
+ /// @param at Index at which the tuple should be replaced.
+ /// @param tuple Tuple to be set.
+ /// @throw isc::OutOfRange if the tuple position is out of range.
+ /// @throw isc::BadValue if the type of the tuple doesn't match the
+ /// universe this option belongs to.
+ void setTuple(const size_t at, const OpaqueDataTuple& tuple);
+
+ /// @brief Returns opaque data tuple at the specified position.
+ ///
+ /// If the specified position is out of range an exception is thrown.
+ ///
+ /// @param at Index at which the tuple should be replaced.
+ /// @throw isc::OutOfRange if the tuple position is out of range.
+ OpaqueDataTuple getTuple(const size_t at) const;
+
+ /// @brief Returns the number of opaque data tuples added to the option.
+ size_t getTuplesNum() const {
+ return (tuples_.size());
+ }
+
+ /// @brief Returns collection of opaque data tuples carried in the option.
+ const TuplesCollection& getTuples() const {
+ return (tuples_);
+ }
+
+ /// @brief Checks if the object holds the opaque data tuple with the
+ /// specified string.
+ ///
+ /// @param tuple_str String representation of the tuple being searched.
+ /// @return true if the specified tuple exists for this option.
+ bool hasTuple(const std::string& tuple_str) const;
+
+ /// @brief Returns the full length of the option, including option header.
+ virtual uint16_t len();
+
+ /// @brief Returns text representation of the option.
+ ///
+ /// @param indent Number of space characters before text.
+ /// @return Text representation of the option.
+ virtual std::string toText(int indent = 0);
+
+private:
+
+ /// @brief Returns the tuple length field type for the given universe.
+ ///
+ /// This function returns the length field type which should be used
+ /// for the opaque data tuples being added to this option.
+ /// Currently this class is only used for a DHCPv6 option it may be expanded
+ /// for DHCPv4 in the future
+ ///
+ /// @return Tuple length field type for the universe this option belongs to.
+ OpaqueDataTuple::LengthFieldType getLengthFieldType() const {
+ return (OpaqueDataTuple::LENGTH_2_BYTES);
+ }
+
+ /// @brief Returns minimal length of the option for the given universe.
+ /// Currently this class is only used for a DHCPv6 option it may be expanded
+ /// for DHCPv4 in the future
+ uint16_t getMinimalLength() const {
+ return (4);
+ }
+
+ /// @brief Collection of opaque data tuples carried by the option.
+ TuplesCollection tuples_;
+
+};
+
+/// @brief Defines a pointer to the @c OptionOpaqueDataTuples.
+typedef boost::shared_ptr<OptionOpaqueDataTuples> OptionOpaqueDataTuplesPtr;
+
+}
+}
+
+#endif // OPTION_OPAQUE_DATA_TUPLES_H
libdhcp___unittests_SOURCES += iface_mgr_unittest.cc
libdhcp___unittests_SOURCES += iface_mgr_test_config.cc iface_mgr_test_config.h
libdhcp___unittests_SOURCES += libdhcp++_unittest.cc
+libdhcp___unittests_SOURCES += opaque_data_tuple_unittest.cc
libdhcp___unittests_SOURCES += option4_addrlst_unittest.cc
libdhcp___unittests_SOURCES += option4_client_fqdn_unittest.cc
libdhcp___unittests_SOURCES += option6_addrlst_unittest.cc
0x00, 0x01, 0x02
};
std::vector<uint8_t> bparam_buf(bparam_data,
- bparam_data + sizeof(bparam_data));;
+ bparam_data + sizeof(bparam_data));;
// The actual test starts here for all supported option codes.
LibDhcpTest::testStdOptionDefs6(D6O_CLIENTID, begin, end,
LibDhcpTest::testStdOptionDefs6(D6O_BOOTFILE_PARAM, bparam_buf.begin(),
bparam_buf.end(),
- typeid(OptionOpaqueDataTuples));
+ typeid(OptionOpaqueDataTuples));
LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_ARCH_TYPE, begin, end,
- typeid(OptionIntArray<uint16_t>));
+ typeid(OptionIntArray<uint16_t>));
LibDhcpTest::testStdOptionDefs6(D6O_NII, begin, begin + 3,
- typeid(OptionCustom));
+ typeid(OptionCustom));
LibDhcpTest::testStdOptionDefs6(D6O_RSOO, begin, end,
typeid(OptionCustom),
--- /dev/null
+// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <dhcp/option_opaque_data_tuples.h>
+#include <util/buffer.h>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::util;
+
+namespace {
+
+// This test checks that the DHCPv6 option constructor sets the default
+// properties to the expected values.
+TEST(OptionOpaqueDataTuples, constructor6) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ // Option length is 2 bytes for option code + 2 bytes for option size
+ EXPECT_EQ(4, data_tuple.len());
+ // There should be no tuples.
+ EXPECT_EQ(0, data_tuple.getTuplesNum());
+}
+
+// This test verifies that it is possible to append the opaque data tuple
+// to the option and then retrieve it.
+TEST(OptionOpaqueDataTuples, addTuple) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ // Initially there should be no tuples (for DHCPv6).
+ ASSERT_EQ(0, data_tuple.getTuplesNum());
+ // Create a new tuple and add it to the option.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ data_tuple.addTuple(tuple);
+ // The option should now hold one tuple.
+ ASSERT_EQ(1, data_tuple.getTuplesNum());
+ EXPECT_EQ("xyz", data_tuple.getTuple(0).getText());
+ // Add another tuple.
+ tuple = "abc";
+ data_tuple.addTuple(tuple);
+ // The option should now hold exactly two tuples in the order in which
+ // they were added.
+ ASSERT_EQ(2, data_tuple.getTuplesNum());
+ EXPECT_EQ("xyz", data_tuple.getTuple(0).getText());
+ EXPECT_EQ("abc", data_tuple.getTuple(1).getText());
+
+ // Check that hasTuple correctly identifies existing tuples.
+ EXPECT_TRUE(data_tuple.hasTuple("xyz"));
+ EXPECT_TRUE(data_tuple.hasTuple("abc"));
+ EXPECT_FALSE(data_tuple.hasTuple("other"));
+
+ // Attempt to add the tuple with 1 byte long length field should fail
+ // for DHCPv6 option.
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_1_BYTE);
+ EXPECT_THROW(data_tuple.addTuple(tuple2), isc::BadValue);
+}
+
+// This test checks that it is possible to replace existing tuple.
+TEST(OptionOpaqueDataTuples, setTuple) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ // Initially there should be no tuples (for DHCPv6).
+ ASSERT_EQ(0, data_tuple.getTuplesNum());
+ // Add a tuple
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ data_tuple.addTuple(tuple);
+
+ // Add another one.
+ tuple = "abc";
+ data_tuple.addTuple(tuple);
+ ASSERT_EQ(2, data_tuple.getTuplesNum());
+ ASSERT_EQ("abc", data_tuple.getTuple(1).getText());
+
+ // Try to replace them with new tuples.
+ tuple = "new_xyz";
+ ASSERT_NO_THROW(data_tuple.setTuple(0, tuple));
+ ASSERT_EQ(2, data_tuple.getTuplesNum());
+ EXPECT_EQ("new_xyz", data_tuple.getTuple(0).getText());
+
+ tuple = "new_abc";
+ ASSERT_NO_THROW(data_tuple.setTuple(1, tuple));
+ ASSERT_EQ(2, data_tuple.getTuplesNum());
+ EXPECT_EQ("new_abc", data_tuple.getTuple(1).getText());
+
+ // For out of range position, exception should be thrown.
+ tuple = "foo";
+ EXPECT_THROW(data_tuple.setTuple(2, tuple), isc::OutOfRange);
+}
+
+// Check that the returned length of the DHCPv6 option is correct.
+TEST(OptionOpaqueDataTuples, len6) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ ASSERT_EQ(4, data_tuple.len());
+ // Add first tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ ASSERT_NO_THROW(data_tuple.addTuple(tuple));
+ // The total length grows by 2 bytes of the length field and 3 bytes
+ // consumed by 'xyz'.
+ EXPECT_EQ(9, data_tuple.len());
+ // Add another tuple and check that the total size gets increased.
+ tuple = "abc";
+ data_tuple.addTuple(tuple);
+ EXPECT_EQ(14, data_tuple.len());
+}
+
+// Check that the DHCPv6 option is rendered to the buffer in wire format.
+TEST(OptionOpaqueDataTuples, pack6) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ ASSERT_EQ(0, data_tuple.getTuplesNum());
+ // Add tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "Hello world";
+ data_tuple.addTuple(tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ data_tuple.addTuple(tuple);
+
+ // Render the data to the buffer.
+ OutputBuffer buf(10);
+ ASSERT_NO_THROW(data_tuple.pack(buf));
+ ASSERT_EQ(22, buf.getLength());
+
+ // Prepare reference data.
+ const uint8_t ref[] = {
+ 0x00, 0x3C, 0x00, 0x12, // option 60, length 18
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ // Compare the buffer with reference data.
+ EXPECT_EQ(0, memcmp(static_cast<const void*>(ref),
+ static_cast<const void*>(buf.getData()),
+ buf.getLength()));
+}
+
+// This function checks that the DHCPv6 option with two opaque data tuples
+// is parsed correctly.
+TEST(OptionOpaqueDataTuples, unpack6) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0x00, 0x03, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionOpaqueDataTuplesPtr data_tuple;
+ ASSERT_NO_THROW(
+ data_tuple = OptionOpaqueDataTuplesPtr(new OptionOpaqueDataTuples(Option::V6,
+ 60,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_BOOTFILE_PARAM, data_tuple->getType());
+ ASSERT_EQ(2, data_tuple->getTuplesNum());
+ EXPECT_EQ("Hello world", data_tuple->getTuple(0).getText());
+ EXPECT_EQ("foo", data_tuple->getTuple(1).getText());
+}
+
+// This test checks that the DHCPv6 option with opaque data of size 0
+// is correctly parsed.
+TEST(OptionOpaqueDataTuples, unpack6EmptyTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0x00, 0x00 // tuple length is 0
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionOpaqueDataTuplesPtr data_tuple;
+ ASSERT_NO_THROW(
+ data_tuple = OptionOpaqueDataTuplesPtr(new OptionOpaqueDataTuples(Option::V6,
+ 60,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_BOOTFILE_PARAM, data_tuple->getType());
+ ASSERT_EQ(1, data_tuple->getTuplesNum());
+ EXPECT_TRUE(data_tuple->getTuple(0).getText().empty());
+}
+
+// This test checks that exception is thrown when parsing truncated DHCPv6
+// bootfile-param option
+TEST(OptionOpaqueDataTuples, unpack6Truncated) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0x00, 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C // worl (truncated d!)
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ EXPECT_THROW(OptionOpaqueDataTuples (Option::V6, 60, buf.begin(), buf.end()),
+ isc::dhcp::OpaqueDataTupleError);
+}
+
+// This test checks that the DHCPv6 bootfile-param option containing no opaque
+// data is parsed correctly.
+TEST(OptionOpaqueDataTuples, unpack6NoTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ };
+ OptionBuffer buf(buf_data,buf_data + sizeof(buf_data));
+
+ OptionOpaqueDataTuplesPtr data_tuple;
+ ASSERT_NO_THROW(
+ data_tuple = OptionOpaqueDataTuplesPtr(new OptionOpaqueDataTuples(Option::V6,
+ 60,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_BOOTFILE_PARAM, data_tuple->getType());
+ EXPECT_EQ(0, data_tuple->getTuplesNum());
+}
+
+// Verifies correctness of the text representation of the DHCPv6 option.
+TEST(OptionOpaqueDataTuples, toText6) {
+ OptionOpaqueDataTuples data_tuple(Option::V6, 60);
+ ASSERT_EQ(0, data_tuple.getTuplesNum());
+ // Lets add a tuple
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "Hello world";
+ data_tuple.addTuple(tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ data_tuple.addTuple(tuple);
+ // Check that the text representation of the option is as expected.
+ EXPECT_EQ("type=60, len=18,"
+ " data-len0=11, data0='Hello world',"
+ " data-len1=3, data1='foo'",
+ data_tuple.toText());
+
+ // Check that indentation works.
+ EXPECT_EQ(" type=60, len=18,"
+ " data-len0=11, data0='Hello world',"
+ " data-len1=3, data1='foo'",
+ data_tuple.toText(2));
+}
+
+} // end of anonymous namespace
+
-// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above