--- /dev/null
+// Copyright (C) 2014 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_vendor_class.h>
+
+namespace isc {
+namespace dhcp {
+
+OptionVendorClass::OptionVendorClass(Option::Universe u,
+ const uint32_t vendor_id)
+ : Option(u, getOptionCode(u)), vendor_id_(vendor_id) {
+ if (u == Option::V4) {
+ addTuple(OpaqueDataTuple(OpaqueDataTuple::LENGTH_1_BYTE));
+ }
+}
+
+ OptionVendorClass::OptionVendorClass(Option::Universe u,
+ OptionBufferConstIter begin,
+ OptionBufferConstIter end)
+ : Option(u, getOptionCode(u)) {
+ unpack(begin, end);
+}
+
+void
+OptionVendorClass::pack(isc::util::OutputBuffer& buf) {
+ packHeader(buf);
+
+ buf.writeUint32(getVendorId());
+
+ for (TuplesCollection::const_iterator it = tuples_.begin();
+ it != tuples_.end(); ++it) {
+ // For DHCPv4 V-I Vendor Class option, there is enterprise id before
+ // every tuple.
+ if ((getUniverse() == V4) && (it != tuples_.begin())) {
+ buf.writeUint32(getVendorId());
+ }
+ it->pack(buf);
+
+ }
+
+}
+
+void
+OptionVendorClass::unpack(OptionBufferConstIter begin,
+ OptionBufferConstIter end) {
+ if (std::distance(begin, end) < getMinimalLength() - getHeaderLen()) {
+ isc_throw(OutOfRange, "parsed Vendor Class option data truncated to"
+ " size " << std::distance(begin, end));
+ }
+ // Option must contain at least one enterprise id. It is ok to read 4-byte
+ // value here because we have checked that the buffer he minimal length.
+ vendor_id_ = isc::util::readUint32(&(*begin), distance(begin, end));
+ begin += sizeof(vendor_id_);
+
+ // 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();
+ // For DHCPv4 option, there is enterprise id before every opaque data
+ // tuple. Let's read it, unless we have already reached the end of
+ // buffer.
+ if ((getUniverse() == V4) && (begin + offset != end)) {
+ // Advance the offset by the size of enterprise id.
+ offset += sizeof(vendor_id_);
+ // If the offset already ran over the buffer length or there is
+ // no space left for the empty tuple (thus we add 1), we have
+ // to signal the option truncation.
+ if (offset + 1 >= std::distance(begin, end)) {
+ isc_throw(isc::OutOfRange, "truncated DHCPv4 V-I Vendor Class"
+ " option - it should contain enterprise id followed"
+ " by opaque data field tuple");
+ }
+ }
+ }
+}
+
+void
+OptionVendorClass::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
+OptionVendorClass::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
+OptionVendorClass::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");
+ }
+ return (tuples_[at]);
+}
+
+uint16_t
+OptionVendorClass::len() {
+ // The option starts with the header and enterprise id.
+ uint16_t length = getHeaderLen() + sizeof(uint32_t);
+ // Now iterate over existing tuples and add their size.
+ for (TuplesCollection::const_iterator it = tuples_.begin();
+ it != tuples_.end(); ++it) {
+ // For DHCPv4 V-I Vendor Class option, there is enterprise id before
+ // every tuple.
+ if ((getUniverse() == V4) && (it != tuples_.begin())) {
+ length += sizeof(uint32_t);
+ }
+ length += it->getTotalLength();
+
+ }
+
+ return (length);
+}
+
+} // namespace isc::dhcp
+} // namespace isc
--- /dev/null
+// Copyright (C) 2014 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_VENDOR_CLASS_H
+#define OPTION_VENDOR_CLASS_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 DHCPv6 Vendor Class and DHCPv4 V-I Vendor
+/// Class options.
+///
+/// The format of DHCPv6 Vendor Class option is described in section 22.16 of
+/// RFC3315 and the format of the DHCPv4 V-I Vendor Class option is described
+/// in section 3 of RFC3925. Each of these options carries enterprise id
+/// followed by the collection of tuples carring opaque data. A single tuple
+/// consists of the field holding opaque data length and the actual data.
+/// In case of the DHCPv4 V-I Vendor Class each tuple is preceded by the
+/// 4-byte long enterprise id. Also, the field which carries the length of
+/// the tuple is 1-byte long for DHCPv4 V-I Vendor Class and 2-bytes long
+/// for the DHCPv6 Vendor Class option.
+///
+/// Whether the encapsulated format is DHCPv4 V-I Vendor Class or DHCPv6
+/// Vendor Class option is controlled by the @c u (universe) parameter passed
+/// to the constructor.
+///
+/// @todo Currently, the enterprise id field is set to a value of the first
+/// enterprise id occurrence in the parsed option. At some point we should
+/// be able to differentiate between enterprise ids.
+class OptionVendorClass : public Option {
+public:
+
+ /// @brief Collection of opaque data tuples carried by the option.
+ typedef std::vector<OpaqueDataTuple> TuplesCollection;
+
+ /// @brief Constructor.
+ ///
+ /// @param u universe (v4 or v6).
+ /// @param vendor_id vendor enterprise id (unique 32-bit integer).
+ OptionVendorClass(Option::Universe u, const uint32_t vendor_id);
+
+ /// @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 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.
+ OptionVendorClass(Option::Universe u, 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: enterprise ids and 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 Returns enterprise id.
+ uint32_t getVendorId() const {
+ return (vendor_id_);
+ }
+
+ /// @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 Returns the full length of the option, including option header.
+ virtual uint16_t len();
+
+private:
+
+ /// @brief Returns option code appropriate for the specified universe.
+ ///
+ /// This function is called by the constructor to map the specified
+ /// universe to the option code.
+ ///
+ /// @param u universe (V4 or V6).
+ /// @return DHCPv4 V-I Vendor Class or DHCPv6 Vendor Class option code.
+ static uint16_t getOptionCode(Option::Universe u) {
+ return (u == V4 ? DHO_VIVCO_SUBOPTIONS : D6O_VENDOR_CLASS);
+ }
+
+ /// @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.
+ ///
+ /// @return Tuple length field type for the universe this option belongs to.
+ OpaqueDataTuple::LengthFieldType getLengthFieldType() const {
+ return (getUniverse() == V4 ? OpaqueDataTuple::LENGTH_1_BYTE :
+ OpaqueDataTuple::LENGTH_2_BYTES);
+ }
+
+ /// @brief Returns minimal length of the option for the given universe.
+ uint16_t getMinimalLength() const {
+ return (getUniverse() == Option::V4 ? 7 : 6);
+ }
+
+ /// @brief Enterprise ID.
+ uint32_t vendor_id_;
+
+ /// @brief Collection of opaque data tuples carried by the option.
+ TuplesCollection tuples_;
+
+};
+
+/// @brief Defines a pointer to the @c OptionVendorClass.
+typedef boost::shared_ptr<OptionVendorClass> OptionVendorClassPtr;
+
+}
+}
+
+#endif // OPTION_VENDOR_CLASS_H
--- /dev/null
+// Copyright (C) 2014 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_vendor_class.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 DHCPv4 option constructor sets the default
+// properties to the expected values. This constructor should add an
+// empty opaque data tuple (it is essentially the same as adding a 1-byte
+// long field which carries a value of 0).
+TEST(OptionVendorClass, constructor4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ EXPECT_EQ(1234, vendor_class.getVendorId());
+ // Option length is 1 byte for header + 1 byte for option size +
+ // 4 bytes of enterprise id + 1 byte for opaque data.
+ EXPECT_EQ(7, vendor_class.len());
+ // There should be one empty tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ(0, vendor_class.getTuple(0).getLength());
+}
+
+// This test checks that the DHCPv6 option constructor sets the default
+// properties to the expected values.
+TEST(OptionVendorClass, constructor6) {
+ OptionVendorClass vendor_class(Option::V6, 2345);
+ EXPECT_EQ(2345, vendor_class.getVendorId());
+ // Option length is 2 bytes for option code + 2 bytes for option size +
+ // 4 bytes of enterprise id.
+ EXPECT_EQ(8, vendor_class.len());
+ // There should be no tuples.
+ EXPECT_EQ(0, vendor_class.getTuplesNum());
+}
+
+// This test verifies that it is possible to append the opaque data tuple
+// to the option and then retrieve it.
+TEST(OptionVendorClass, addTuple) {
+ OptionVendorClass vendor_class(Option::V6, 2345);
+ // Initially there should be no tuples (for DHCPv6).
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
+ // Create a new tuple and add it to the option.
+ OpaqueDataTuple tuple;
+ tuple = "xyz";
+ vendor_class.addTuple(tuple);
+ // The option should now hold one tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+ // Add another tuple.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ // The option should now hold exactly two tuples in the order in which
+ // they were added.
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+ EXPECT_EQ("abc", vendor_class.getTuple(1).getText());
+
+ // Attempt to add the tuple with 1 byte long length field should fail
+ // for DHCPv6 option.
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_1_BYTE);
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
+}
+
+// This test checks that it is possible to replace existing tuple.
+TEST(OptionVendorClass, setTuple) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ // The DHCPv4 option should carry one empty tuple.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ ASSERT_TRUE(vendor_class.getTuple(0).getText().empty());
+ // Replace the empty tuple with non-empty one.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ // There should be one tuple with updated data.
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ EXPECT_EQ("xyz", vendor_class.getTuple(0).getText());
+
+ // Add another one.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ ASSERT_EQ("abc", vendor_class.getTuple(1).getText());
+
+ // Try to replace them with new tuples.
+ tuple = "new_xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("new_xyz", vendor_class.getTuple(0).getText());
+
+ tuple = "new_abc";
+ ASSERT_NO_THROW(vendor_class.setTuple(1, tuple));
+ ASSERT_EQ(2, vendor_class.getTuplesNum());
+ EXPECT_EQ("new_abc", vendor_class.getTuple(1).getText());
+
+ // For out of range position, exception should be thrown.
+ tuple = "foo";
+ EXPECT_THROW(vendor_class.setTuple(2, tuple), isc::OutOfRange);
+
+ // Attempt to add the tuple with 2 byte long length field should fail
+ // for DHCPv4 option.
+ OpaqueDataTuple tuple2(OpaqueDataTuple::LENGTH_2_BYTES);
+ EXPECT_THROW(vendor_class.addTuple(tuple2), isc::BadValue);
+}
+
+// Check that the returned length of the DHCPv4 option is correct.
+TEST(OptionVendorClass, len4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ ASSERT_EQ(7, vendor_class.len());
+ // Replace the default empty tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.setTuple(0, tuple));
+ // The total length should get increased by the size of 'xyz'.
+ EXPECT_EQ(10, vendor_class.len());
+ // Add another tuple.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ // The total size now grows by the additional enterprise id and the
+ // 1 byte of the tuple length field and 3 bytes of 'abc'.
+ EXPECT_EQ(18, vendor_class.len());
+}
+
+// Check that the returned length of the DHCPv6 option is correct.
+TEST(OptionVendorClass, len6) {
+ OptionVendorClass vendor_class(Option::V6, 1234);
+ ASSERT_EQ(8, vendor_class.len());
+ // Add first tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "xyz";
+ ASSERT_NO_THROW(vendor_class.addTuple(tuple));
+ // The total length grows by 2 bytes of the length field and 3 bytes
+ // consumed by 'xyz'.
+ EXPECT_EQ(13, vendor_class.len());
+ // Add another tuple and check that the total size gets increased.
+ tuple = "abc";
+ vendor_class.addTuple(tuple);
+ EXPECT_EQ(18, vendor_class.len());
+}
+
+// Check that the option is rendered to the buffer in wire format.
+TEST(OptionVendorClass, pack4) {
+ OptionVendorClass vendor_class(Option::V4, 1234);
+ ASSERT_EQ(1, vendor_class.getTuplesNum());
+ // By default, there is an empty tuple in the option. Let's replace
+ // it with the tuple with some data.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_1_BYTE);
+ tuple = "Hello world";
+ vendor_class.setTuple(0, tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+
+ // Render the data to the buffer.
+ OutputBuffer buf(10);
+ ASSERT_NO_THROW(vendor_class.pack(buf));
+ ASSERT_EQ(26, buf.getLength());
+
+ // Prepare reference data.
+ const uint8_t ref[] = {
+ 0x7C, 0x18, // option 124, length 24
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 3, // 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()), 26));
+}
+
+// Check that the DHCPv6 option is rendered to the buffer in wire format.
+TEST(OptionVendorClass, pack6) {
+ OptionVendorClass vendor_class(Option::V6, 1234);
+ ASSERT_EQ(0, vendor_class.getTuplesNum());
+ // Add tuple.
+ OpaqueDataTuple tuple(OpaqueDataTuple::LENGTH_2_BYTES);
+ tuple = "Hello world";
+ vendor_class.addTuple(tuple);
+ // And add another tuple so as resulting option is a bit more complex.
+ tuple = "foo";
+ vendor_class.addTuple(tuple);
+
+ // Render the data to the buffer.
+ OutputBuffer buf(10);
+ ASSERT_NO_THROW(vendor_class.pack(buf));
+ ASSERT_EQ(26, buf.getLength());
+
+ // Prepare reference data.
+ const uint8_t ref[] = {
+ 0x00, 0x10, 0x00, 0x16, // option 16, length 22
+ 0x00, 0x00, 0x04, 0xD2, // enterprise id 1234
+ 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 DHCPv4 option with two opaque data tuples
+// is parsed correctly.
+TEST(OptionVendorClass, unpack4) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 3, // tuple length is 3
+ 0x66, 0x6F, 0x6F // foo
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+}
+
+// This function checks that the DHCPv4 option with two opaque data tuples
+// is parsed correctly.
+TEST(OptionVendorClass, unpack6) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 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));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(2, vendor_class->getTuplesNum());
+ EXPECT_EQ("Hello world", vendor_class->getTuple(0).getText());
+ EXPECT_EQ("foo", vendor_class->getTuple(1).getText());
+}
+
+
+// This test checks that the DHCPv4 option with opaque data of size 0
+// is correctly parsed.
+TEST(OptionVendorClass, unpack4EmptyTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, // tuple length is 0
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V4,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(DHO_VIVCO_SUBOPTIONS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
+}
+
+// This test checks that the DHCPv6 option with opaque data of size 0
+// is correctly parsed.
+TEST(OptionVendorClass, unpack6EmptyTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x00, 0x00 // tuple length is 0
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ ASSERT_EQ(1, vendor_class->getTuplesNum());
+ EXPECT_TRUE(vendor_class->getTuple(0).getText().empty());
+}
+
+// This test checks that exception is thrown when parsing truncated DHCPv4
+// V-I Vendor Class option.
+TEST(OptionVendorClass, unpack4Truncated) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 0x0B, // tuple length is 11
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // Hello<space>
+ 0x77, 0x6F, 0x72, 0x6C, 0x64, // world
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ EXPECT_THROW(OptionVendorClass (Option::V4, buf.begin(), buf.end()),
+ isc::OutOfRange);
+}
+
+// This test checks that exception is thrown when parsing truncated DHCPv6
+// Vendor Class option.
+TEST(OptionVendorClass, unpack6Truncated) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2, // enterprise id 1234
+ 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(OptionVendorClass (Option::V6, buf.begin(), buf.end()),
+ isc::dhcp::OpaqueDataTupleError);
+}
+
+// This test checks that exception is thrown when parsing DHCPv4 V-I Vendor
+// Class option which doesn't have opaque data length.
+TEST(OptionVendorClass, unpack4NoTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ ASSERT_THROW(OptionVendorClass (Option::V4, buf.begin(), buf.end()),
+ isc::OutOfRange);
+}
+
+// This test checks that the DHCPv6 Vendor Class option containing no opaque
+// data is parsed correctly.
+TEST(OptionVendorClass, unpack6NoTuple) {
+ // Prepare data to decode.
+ const uint8_t buf_data[] = {
+ 0, 0, 0x4, 0xD2 // enterprise id 1234
+ };
+ OptionBuffer buf(buf_data, buf_data + sizeof(buf_data));
+
+ OptionVendorClassPtr vendor_class;
+ ASSERT_NO_THROW(
+ vendor_class = OptionVendorClassPtr(new OptionVendorClass(Option::V6,
+ buf.begin(),
+ buf.end()));
+ );
+ EXPECT_EQ(D6O_VENDOR_CLASS, vendor_class->getType());
+ EXPECT_EQ(1234, vendor_class->getVendorId());
+ EXPECT_EQ(0, vendor_class->getTuplesNum());
+}
+
+} // end of anonymous namespace
+