Parcourir la source

[trac3576] Clean up white space and add new files

Shawn Routhier il y a 9 ans
Parent
commit
d17f7e918d

+ 154 - 0
src/lib/dhcp/option_opaque_data_tuples.cc

@@ -0,0 +1,154 @@
+// 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

+ 174 - 0
src/lib/dhcp/option_opaque_data_tuples.h

@@ -0,0 +1,174 @@
+// 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

+ 1 - 0
src/lib/dhcp/tests/Makefile.am

@@ -51,6 +51,7 @@ libdhcp___unittests_SOURCES += hwaddr_unittest.cc
 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

+ 4 - 4
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -1031,7 +1031,7 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
         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,
@@ -1183,13 +1183,13 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
 
     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),

+ 258 - 0
src/lib/dhcp/tests/option_opaque_data_tuples_unittest.cc

@@ -0,0 +1,258 @@
+// 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
+

+ 1 - 1
src/lib/dhcpsrv/tests/cfg_option_def_unittest.cc

@@ -1,4 +1,4 @@
-// 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