BIND 10 master, updated. 28d885b457dda970d9aecc5de018ec1120143a10 [master] Merge branch 'trac2312'

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Nov 29 10:26:17 UTC 2012


The branch, master has been updated
       via  28d885b457dda970d9aecc5de018ec1120143a10 (commit)
       via  11e658493d1672d39194580ffb1dde98101608a9 (commit)
       via  2c10fa4964550d7402ee0fc93401f8ad39f29e87 (commit)
       via  c3e696cb4ba09b3cd53842d684f98262cf0cee7a (commit)
       via  d85d48eee9608db9f4c912e5cfe3ae1f2f726c05 (commit)
       via  ee7dbbde6311ec0e9519169871aa182dd7cc1440 (commit)
       via  863da09e4d4ef50693860dd95948ae5171ba5116 (commit)
       via  ff2167388ff8a86dfdae23fbd147602b102b0319 (commit)
       via  2dd98a113773481b77ca0600c1d1028e2b5fdd72 (commit)
       via  7cbe475737d43aaf4757b5df76d9bea75b0bf5a1 (commit)
       via  e4670244659f82b8fabdcaddce43d229a79f34f8 (commit)
       via  47e1dfee342b4b1fc269109d130bea5eeeff743f (commit)
       via  deaf3af3f2b12e34dee77b31ff28748cbaef3778 (commit)
       via  276a08d3371eef268c0e0b738d0c8583c3d8641d (commit)
       via  0a5970240b9e8ae08e4a06b67a0678a19b5def45 (commit)
       via  c12d1b3b6e90a0918e498282b2374ac294258cdf (commit)
       via  a6c3266fe1acdfa5393f1cd2aa2a502615800943 (commit)
       via  c02e37894d858a0c3f70deb90ffba14c0ae58b54 (commit)
       via  f47316a1d2b49791b785cf7b8b5e11a13a7a0fbe (commit)
       via  8f0e9452fd3cc94ef44352839e91e2a4a1d91c55 (commit)
       via  bfc050ed6c9daf2537b5d7aad8fb6f4c6327e6e5 (commit)
       via  7c73dd5e84c12f286970ae22c31aa4513a495646 (commit)
       via  f3da5ed9ef5a25ba65096620b1e7a7e21e04a2c2 (commit)
       via  f957fa651d7cb9b7e8364a58a7c438c367abb659 (commit)
       via  08d0d10dd79cfa9b4f5b4d42bbfef15846d77d68 (commit)
      from  d3c5e8d90a97ce403e2e0e079094d20b43fc8030 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 28d885b457dda970d9aecc5de018ec1120143a10
Merge: d3c5e8d 11e6584
Author: Marcin Siodelski <marcin at isc.org>
Date:   Thu Nov 29 11:03:05 2012 +0100

    [master] Merge branch 'trac2312'

commit 11e658493d1672d39194580ffb1dde98101608a9
Author: Marcin Siodelski <marcin at isc.org>
Date:   Thu Nov 29 10:16:34 2012 +0100

    [2312] Added missing check for empty vector.

-----------------------------------------------------------------------

Summary of changes:
 src/lib/dhcp/Makefile.am                         |    3 +-
 src/lib/dhcp/option6_int.h                       |    4 +-
 src/lib/dhcp/option6_int_array.h                 |    4 +-
 src/lib/dhcp/option_custom.cc                    |  370 +++++++++
 src/lib/dhcp/option_custom.h                     |  235 ++++++
 src/lib/dhcp/option_data_types.cc                |  227 ++++++
 src/lib/dhcp/option_data_types.h                 |  215 ++++-
 src/lib/dhcp/option_definition.cc                |  379 ++++-----
 src/lib/dhcp/option_definition.h                 |  111 +--
 src/lib/dhcp/tests/Makefile.am                   |    1 +
 src/lib/dhcp/tests/option_custom_unittest.cc     |  906 ++++++++++++++++++++++
 src/lib/dhcp/tests/option_definition_unittest.cc |   55 --
 12 files changed, 2147 insertions(+), 363 deletions(-)
 create mode 100644 src/lib/dhcp/option_custom.cc
 create mode 100644 src/lib/dhcp/option_custom.h
 create mode 100644 src/lib/dhcp/option_data_types.cc
 create mode 100644 src/lib/dhcp/tests/option_custom_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/lib/dhcp/Makefile.am b/src/lib/dhcp/Makefile.am
index 1c74446..e9333e2 100644
--- a/src/lib/dhcp/Makefile.am
+++ b/src/lib/dhcp/Makefile.am
@@ -21,8 +21,9 @@ libb10_dhcp___la_SOURCES += iface_mgr_linux.cc
 libb10_dhcp___la_SOURCES += iface_mgr_sun.cc
 libb10_dhcp___la_SOURCES += libdhcp++.cc libdhcp++.h
 libb10_dhcp___la_SOURCES += option.cc option.h
-libb10_dhcp___la_SOURCES += option_data_types.h
+libb10_dhcp___la_SOURCES += option_data_types.cc option_data_types.h
 libb10_dhcp___la_SOURCES += option_definition.cc option_definition.h
+libb10_dhcp___la_SOURCES += option_custom.cc option_custom.h
 libb10_dhcp___la_SOURCES += option6_ia.cc option6_ia.h
 libb10_dhcp___la_SOURCES += option6_iaaddr.cc option6_iaaddr.h
 libb10_dhcp___la_SOURCES += option6_addrlst.cc option6_addrlst.h
diff --git a/src/lib/dhcp/option6_int.h b/src/lib/dhcp/option6_int.h
index 0dfa4dd..d61509b 100644
--- a/src/lib/dhcp/option6_int.h
+++ b/src/lib/dhcp/option6_int.h
@@ -89,7 +89,7 @@ public:
         // Depending on the data type length we use different utility functions
         // writeUint16 or writeUint32 which write the data in the network byte
         // order to the provided buffer. The same functions can be safely used
-        // for either unsiged or signed integers so there is not need to create
+        // for either unsigned or signed integers so there is not need to create
         // special cases for intX_t types.
         switch (OptionDataTypeTraits<T>::len) {
         case 1:
@@ -128,7 +128,7 @@ public:
         // Depending on the data type length we use different utility functions
         // readUint16 or readUint32 which read the data laid in the network byte
         // order from the provided buffer. The same functions can be safely used
-        // for either unsiged or signed integers so there is not need to create
+        // for either unsigned or signed integers so there is not need to create
         // special cases for intX_t types.
         int data_size_len = OptionDataTypeTraits<T>::len;
         switch (data_size_len) {
diff --git a/src/lib/dhcp/option6_int_array.h b/src/lib/dhcp/option6_int_array.h
index c37c107..3b64213 100644
--- a/src/lib/dhcp/option6_int_array.h
+++ b/src/lib/dhcp/option6_int_array.h
@@ -118,7 +118,7 @@ public:
             // Depending on the data type length we use different utility functions
             // writeUint16 or writeUint32 which write the data in the network byte
             // order to the provided buffer. The same functions can be safely used
-            // for either unsiged or signed integers so there is not need to create
+            // for either unsigned or signed integers so there is not need to create
             // special cases for intX_t types.
             switch (OptionDataTypeTraits<T>::len) {
             case 1:
@@ -164,7 +164,7 @@ public:
             // Depending on the data type length we use different utility functions
             // readUint16 or readUint32 which read the data laid in the network byte
             // order from the provided buffer. The same functions can be safely used
-            // for either unsiged or signed integers so there is not need to create
+            // for either unsigned or signed integers so there is not need to create
             // special cases for intX_t types.
             int data_size_len = OptionDataTypeTraits<T>::len;
             switch (data_size_len) {
diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc
new file mode 100644
index 0000000..8b3ef11
--- /dev/null
+++ b/src/lib/dhcp/option_custom.cc
@@ -0,0 +1,370 @@
+// Copyright (C) 2012 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 <dhcp/libdhcp++.h>
+#include <dhcp/option_data_types.h>
+#include <dhcp/option_custom.h>
+#include <util/encode/hex.h>
+
+namespace isc {
+namespace dhcp {
+
+OptionCustom::OptionCustom(const OptionDefinition& def,
+                             Universe u,
+                             const OptionBuffer& data)
+    : Option(u, def.getCode(), data.begin(), data.end()),
+      definition_(def) {
+    createBuffers();
+}
+
+OptionCustom::OptionCustom(const OptionDefinition& def,
+                             Universe u,
+                             OptionBufferConstIter first,
+                             OptionBufferConstIter last)
+    : Option(u, def.getCode(), first, last),
+      definition_(def) {
+    createBuffers();
+}
+
+void
+OptionCustom::checkIndex(const uint32_t index) const {
+    if (index >= buffers_.size()) {
+        isc_throw(isc::OutOfRange, "specified data field index " << index
+                  << " is out of rangex.");
+    }
+}
+
+void
+OptionCustom::createBuffers() {
+    // Check that the option definition is correct as we are going
+    // to use it to split the data_ buffer into set of sub buffers.
+    definition_.validate();
+
+    std::vector<OptionBuffer> buffers;
+    OptionBuffer::iterator data = data_.begin();
+
+    OptionDataType data_type = definition_.getType();
+    if (data_type == OPT_RECORD_TYPE) {
+        // An option comprises a record of data fields. We need to
+        // get types of these data fields to allocate enough space
+        // for each buffer.
+        const OptionDefinition::RecordFieldsCollection& fields =
+            definition_.getRecordFields();
+
+        // Go over all data fields within a record.
+        for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
+             field != fields.end(); ++field) {
+            // For fixed-size data type such as boolean, integer, even
+            // IP address we can use the utility function to get the required
+            // buffer size.
+            int data_size = OptionDataTypeUtil::getDataTypeLen(*field);
+
+            // For variable size types (such as string) the function above
+            // will return 0 so we need to do a runtime check. Since variable
+            // length data fields may be laid only at the end of an option we
+            // consume the rest of this option. Note that validate() function
+            // in OptionDefinition object should have checked whether the
+            // data fields layout is correct (that the variable string fields
+            // are laid at the end).
+            if (data_size == 0) {
+                data_size = std::distance(data, data_.end());
+                if (data_size == 0) {
+                    // If we reached the end of buffer we assume that this option is
+                    // truncated because there is no remaining data to initialize
+                    // an option field.
+                    if (data_size == 0) {
+                        isc_throw(OutOfRange, "option buffer truncated");
+                    }
+                }
+            } else {
+                // Our data field requires that there is a certain chunk of
+                // data left in the buffer. If not, option is truncated.
+                if (std::distance(data, data_.end()) < data_size) {
+                    isc_throw(OutOfRange, "option buffer truncated");
+                }
+            }
+            // Store the created buffer.
+            buffers.push_back(OptionBuffer(data, data + data_size));
+            // Proceed to the next data field.
+            data += data_size;
+        }
+    } else if (data_type != OPT_EMPTY_TYPE) {
+        // If data_type value is other than OPT_RECORD_TYPE, our option is
+        // empty (have no data at all) or it comprises one or more
+        // data fields of the same type. The type of those fields
+        // is held in the data_type variable so let's use it to determine
+        // a size of buffers.
+        int data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
+        // The check below will fail if the input buffer is too short
+        // for the data size being held by this option.
+        // Note that data_size returned by getDataTypeLen may be zero
+        // if variable length data is being held by the option but
+        // this will not cause this check to throw exception.
+        if (std::distance(data, data_.end()) < data_size) {
+            isc_throw(OutOfRange, "option buffer truncated");
+        }
+        // For an array of values we are taking different path because
+        // we have to handle multiple buffers.
+        if (definition_.getArrayType()) {
+            // We don't perform other checks for data types that can't be
+            // used together with array indicator such as strings, empty field
+            // etc. This is because OptionDefinition::validate function should
+            // have checked this already. Thus data_size must be greater than
+            // zero.
+            assert(data_size > 0);
+            // Get equal chunks of data and store as collection of buffers.
+            // Truncate any remaining part which length is not divisible by
+            // data_size. Note that it is ok to truncate the data if and only
+            // if the data buffer is long enough to keep at least one value.
+            // This has been checked above already.
+            do {
+                buffers.push_back(OptionBuffer(data, data + data_size));
+                data += data_size;
+            } while (std::distance(data, data_.end()) >= data_size);
+        } else {
+            // For non-arrays the data_size can be zero because
+            // getDataTypeLen returns zero for variable size data types
+            // such as strings. Simply take whole buffer.
+            if (data_size == 0) {
+                data_size = std::distance(data, data_.end());
+            }
+            if (data_size > 0) {
+                buffers.push_back(OptionBuffer(data, data + data_size));
+            } else {
+                isc_throw(OutOfRange, "option buffer truncated");
+            }
+        }
+    }
+    // If everything went ok we can replace old buffer set with new ones.
+    std::swap(buffers_, buffers);
+}
+
+std::string
+OptionCustom::dataFieldToText(const OptionDataType data_type,
+                              const uint32_t index) const {
+    std::ostringstream text;
+
+    // Get the value of the data field.
+    switch (data_type) {
+    case OPT_BINARY_TYPE:
+        text << util::encode::encodeHex(readBinary(index));
+        break;
+    case OPT_BOOLEAN_TYPE:
+        text << (readBoolean(index) ? "true" : "false");
+        break;
+    case OPT_INT8_TYPE:
+        text << readInteger<int8_t>(index);
+        break;
+    case OPT_INT16_TYPE:
+        text << readInteger<int16_t>(index);
+        break;
+    case OPT_INT32_TYPE:
+        text << readInteger<int32_t>(index);
+        break;
+    case OPT_UINT8_TYPE:
+        text << readInteger<uint8_t>(index);
+        break;
+    case OPT_UINT16_TYPE:
+        text << readInteger<uint16_t>(index);
+        break;
+    case OPT_UINT32_TYPE:
+        text << readInteger<uint32_t>(index);
+        break;
+    case OPT_IPV4_ADDRESS_TYPE:
+    case OPT_IPV6_ADDRESS_TYPE:
+        text << readAddress(index).toText();
+        break;
+    case OPT_STRING_TYPE:
+        text << readString(index);
+        break;
+    default:
+        ;
+    }
+
+    // Append data field type in brackets.
+    text << " ( " << OptionDataTypeUtil::getDataTypeName(data_type) << " ) ";
+
+    return (text.str());
+}
+
+void
+OptionCustom::pack4(isc::util::OutputBuffer& buf) {
+    if (len() > 255) {
+        isc_throw(OutOfRange, "DHCPv4 Option " << type_
+                  << " value is too high. At most 255 is supported.");
+    }
+
+    buf.writeUint8(type_);
+    buf.writeUint8(len() - getHeaderLen());
+
+    // Write data from buffers.
+    for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
+         it != buffers_.end(); ++it) {
+        // In theory the createBuffers function should have taken
+        // care that there are no empty buffers added to the
+        // collection but it is almost always good to make sure.
+        if (!it->empty()) {
+            buf.writeData(&(*it)[0], it->size());
+        }
+    }
+
+    // Write suboptions.
+    packOptions(buf);
+}
+
+void
+OptionCustom::pack6(isc::util::OutputBuffer& buf) {
+    buf.writeUint16(type_);
+    buf.writeUint16(len() - getHeaderLen());
+
+    // Write data from buffers.
+    for (std::vector<OptionBuffer>::const_iterator it = buffers_.begin();
+         it != buffers_.end(); ++it) {
+        if (!it->empty()) {
+            buf.writeData(&(*it)[0], it->size());
+        }
+    }
+
+    packOptions(buf);
+}
+
+asiolink::IOAddress
+OptionCustom::readAddress(const uint32_t index) const {
+    checkIndex(index);
+
+    // The address being read can be either IPv4 or IPv6. The decision
+    // is made based on the buffer length. If it holds 4 bytes it is IPv4
+    // address, if it holds 16 bytes it is IPv6.
+    if (buffers_[index].size() == asiolink::V4ADDRESS_LEN) {
+        return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET));
+    } else if (buffers_[index].size() == asiolink::V6ADDRESS_LEN) {
+        return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
+    } else {
+        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                  << " IP address. Invalid buffer length " << buffers_[index].size());
+    }
+}
+
+const OptionBuffer&
+OptionCustom::readBinary(const uint32_t index) const {
+    checkIndex(index);
+    return (buffers_[index]);
+}
+
+bool
+OptionCustom::readBoolean(const uint32_t index) const {
+    checkIndex(index);
+    return (OptionDataTypeUtil::readBool(buffers_[index]));
+}
+
+std::string
+OptionCustom::readString(const uint32_t index) const {
+    checkIndex(index);
+    return (OptionDataTypeUtil::readString(buffers_[index]));
+}
+
+void
+OptionCustom::unpack(OptionBufferConstIter begin,
+                     OptionBufferConstIter end) {
+    data_ = OptionBuffer(begin, end);
+    // Chop the buffer stored in data_ into set of sub buffers.
+    createBuffers();
+}
+
+uint16_t
+OptionCustom::len() {
+    // The length of the option is a sum of option header ...
+    int length = getHeaderLen();
+
+    // ... lengths of all buffers that hold option data ...
+    for (std::vector<OptionBuffer>::const_iterator buf = buffers_.begin();
+         buf != buffers_.end(); ++buf) {
+        length += buf->size();
+    }
+
+    // ... and lengths of all suboptions
+    for (OptionCustom::OptionCollection::iterator it = options_.begin();
+         it != options_.end();
+         ++it) {
+        length += (*it).second->len();
+    }
+
+    return (length);
+}
+
+void OptionCustom::setData(const OptionBufferConstIter first,
+                     const OptionBufferConstIter last) {
+    // We will copy entire option buffer, so we have to resize data_.
+    data_.resize(std::distance(first, last));
+    std::copy(first, last, data_.begin());
+
+    // Chop the data_ buffer into set of buffers that represent
+    // option fields data.
+    createBuffers();
+}
+
+std::string OptionCustom::toText(int indent) {
+    std::stringstream tmp;
+
+    for (int i = 0; i < indent; ++i)
+        tmp << " ";
+
+    tmp << "type=" << type_ << ", len=" << len()-getHeaderLen()
+        << ", data fields:" << std::endl;
+
+    OptionDataType data_type = definition_.getType();
+    if (data_type == OPT_RECORD_TYPE) {
+        const OptionDefinition::RecordFieldsCollection& fields =
+            definition_.getRecordFields();
+
+        // For record types we iterate over fields defined in
+        // option definition and match the appropriate buffer
+        // with them.
+        for (OptionDefinition::RecordFieldsConstIter field = fields.begin();
+             field != fields.end(); ++field) {
+            for (int j = 0; j < indent + 2; ++j) {
+                tmp << " ";
+            }
+            tmp << "#" << std::distance(fields.begin(), field) << " "
+                << dataFieldToText(*field, std::distance(fields.begin(),
+                                                         field))
+                << std::endl;
+        }
+    } else {
+        // For non-record types we iterate over all buffers
+        // and print the data type set globally for an option
+        // definition. We take the same code path for arrays
+        // and non-arrays as they only differ in such a way that
+        // non-arrays have just single data field.
+        for (unsigned int i = 0; i < getDataFieldsNum(); ++i) {
+            for (int j = 0; j < indent + 2; ++j) {
+                tmp << " ";
+            }
+            tmp << "#" << i << " "
+                << dataFieldToText(definition_.getType(), i)
+                << std::endl;
+        }
+    }
+
+    // print suboptions
+    for (OptionCollection::const_iterator opt = options_.begin();
+         opt != options_.end();
+         ++opt) {
+        tmp << (*opt).second->toText(indent+2);
+    }
+    return tmp.str();
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option_custom.h b/src/lib/dhcp/option_custom.h
new file mode 100644
index 0000000..ad248c5
--- /dev/null
+++ b/src/lib/dhcp/option_custom.h
@@ -0,0 +1,235 @@
+// Copyright (C) 2012 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_CUSTOM_H
+#define OPTION_CUSTOM_H
+
+#include <dhcp/option.h>
+#include <dhcp/option_definition.h>
+#include <util/io_utilities.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Option with defined data fields represented as buffers that can
+/// be accessed using data field index.
+///
+/// This class represents an option which has defined structure: data fields
+/// of specific types and order. Those fields can be accessed using indexes,
+/// where index 0 represents first data field within an option. The last
+/// field can be accessed using index equal to 'number of fields' - 1.
+/// Internally, the option data is stored as a collection of OptionBuffer
+/// objects, each representing data for a particular data field. This data
+/// can be converted to the actual data type using methods implemented
+/// within this class. This class is used to represent those options that
+/// can't be represented by any other specialized class (this excludes the
+/// Option class which is generic and can be used to represent any option).
+class OptionCustom : public Option {
+public:
+
+    /// @brief Constructor, used for options to be sent.
+    ///
+    /// This constructor creates an instance of an option from the whole
+    /// supplied buffer. This constructor is mainly used to create an
+    /// instances of options to be stored in outgoing DHCP packets.
+    /// The buffer used to create the instance of an option can be
+    /// created from the option data specified in server's configuration.
+    ///
+    /// @param def option definition.
+    /// @param u specifies universe (V4 or V6).
+    /// @param data content of the option.
+    ///
+    /// @throw OutOfRange if option buffer is truncated.
+    ///
+    /// @todo list all exceptions thrown by ctor.
+    OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);
+
+    /// @brief Constructor, used for received options.
+    ///
+    /// This constructor creates an instance an option from the portion
+    /// of the buffer specified by iterators. This is mainly useful when
+    /// parsing received packets. Such packets are represented by a single
+    /// buffer holding option data and all sub options. Methods that are
+    /// parsing a packet, supply relevant portions of the packet buffer
+    /// to this constructor to create option instances out of it.
+    ///
+    /// @param def option definition.
+    /// @param u specifies universe (V4 or V6).
+    /// @param first iterator to the first element that should be copied.
+    /// @param last iterator to the next element after the last one
+    /// to be copied.
+    ///
+    /// @throw OutOfRange if option buffer is truncated.
+    ///
+    /// @todo list all exceptions thrown by ctor.
+    OptionCustom(const OptionDefinition& def, Universe u,
+                 OptionBufferConstIter first, OptionBufferConstIter last);
+
+    /// @brief Return a number of the data fields.
+    ///
+    /// @return number of data fields held by the option.
+    uint32_t getDataFieldsNum() const { return (buffers_.size()); }
+
+    /// @brief Read a buffer as IP address.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @return IP address read from a buffer.
+    /// @throw isc::OutOfRange if index is out of range.
+    asiolink::IOAddress readAddress(const uint32_t index) const;
+
+    /// @brief Read a buffer as binary data.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @return read buffer holding binary data.
+    const OptionBuffer& readBinary(const uint32_t index) const;
+
+    /// @brief Read a buffer as boolean value.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @return read boolean value.
+    bool readBoolean(const uint32_t index) const;
+
+    /// @brief Read a buffer as integer value.
+    ///
+    /// @param index buffer index.
+    /// @tparam integer type of a value being returned.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @return read integer value.
+    template<typename T>
+    T readInteger(const uint32_t index) const {
+        checkIndex(index);
+
+        // Check that the requested return type is a supported integer.
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
+                      " by readInteger is not supported integer type");
+        }
+
+        // Get the option definition type.
+        OptionDataType data_type = definition_.getType();
+        if (data_type == OPT_RECORD_TYPE) {
+            const OptionDefinition::RecordFieldsCollection& record_fields =
+                definition_.getRecordFields();
+            // When we initialized buffers we have already checked that
+            // the number of these buffers is equal to number of option
+            // fields in the record so the condition below should be met.
+            assert(index < record_fields.size());
+            // Get the data type to be returned.
+            data_type = record_fields[index];
+        }
+
+        // Requested data type must match the data type in a record.
+        if (OptionDataTypeTraits<T>::type != data_type) {
+            isc_throw(isc::dhcp::InvalidDataType,
+                      "unable to read option field with index " << index
+                      << " as integer value. The field's data type"
+                      << data_type << " does not match the integer type"
+                      << "returned by the readInteger function.");
+        }
+        // When we created the buffer we have checked that it has a
+        // valid size so this condition here should be always fulfiled.
+        assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
+        // Read an integer value.
+        return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
+    }
+
+    /// @brief Read a buffer as string value.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @return string value read from buffer.
+    /// @throw isc::OutOfRange if index is out of range.
+    std::string readString(const uint32_t index) const;
+
+    /// @brief Parses received buffer.
+    ///
+    /// @param begin iterator to first byte of option data
+    /// @param end iterator to end of option data (first byte after option end)
+    virtual void unpack(OptionBufferConstIter begin,
+                        OptionBufferConstIter end);
+
+    /// @brief Returns string representation of the option.
+    ///
+    /// @param indent number of spaces before printed text.
+    ///
+    /// @return string with text representation.
+    virtual std::string toText(int indent = 0);
+
+    /// @brief Returns length of the complete option (data length +
+    ///        DHCPv4/DHCPv6 option header)
+    ///
+    /// @return length of the option
+    virtual uint16_t len();
+
+    /// @brief Sets content of this option from buffer.
+    ///
+    /// Option will be resized to length of buffer.
+    ///
+    /// @param first iterator pointing begining of buffer to copy.
+    /// @param last iterator pointing to end of buffer to copy.
+    void setData(const OptionBufferConstIter first,
+                 const OptionBufferConstIter last);
+
+protected:
+
+    /// @brief Writes DHCPv4 option in a wire format to a buffer.
+    ///
+    /// @param buf output buffer (option will be stored there).
+    virtual void pack4(isc::util::OutputBuffer& buf);
+
+    /// @brief Writes DHCPv6 option in a wire format to a buffer.
+    ///
+    /// @param buf output buffer (built options will be stored here)
+    virtual void pack6(isc::util::OutputBuffer& buf);
+
+private:
+
+    /// @brief Check if data field index is valid.
+    ///
+    /// @param index Data field index to check.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    void checkIndex(const uint32_t index) const;
+
+    /// @brief Create collection of buffers representing data field values.
+    void createBuffers();
+
+    /// @brief Return a text representation of a data field.
+    ///
+    /// @param data_type data type of a field.
+    /// @param index data field buffer index within a custom option.
+    ///
+    /// @return text representation of a data field.
+    std::string dataFieldToText(const OptionDataType data_type,
+                                const uint32_t index) const;
+
+    /// Option definition used to create an option.
+    OptionDefinition definition_;
+
+    /// The collection of buffers holding data for option fields.
+    /// The order of buffers corresponds to the order of option
+    /// fields.
+    std::vector<OptionBuffer> buffers_;
+};
+
+} // namespace isc::dhcp
+} // namespace isc
+
+#endif // OPTION_CUSTOM_H
diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc
new file mode 100644
index 0000000..46aa663
--- /dev/null
+++ b/src/lib/dhcp/option_data_types.cc
@@ -0,0 +1,227 @@
+// Copyright (C) 2012 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 <dhcp/option_data_types.h>
+#include <util/encode/hex.h>
+
+namespace isc {
+namespace dhcp {
+
+OptionDataTypeUtil::OptionDataTypeUtil() {
+    data_types_["empty"] = OPT_EMPTY_TYPE;
+    data_types_["binary"] = OPT_BINARY_TYPE;
+    data_types_["boolean"] = OPT_BOOLEAN_TYPE;
+    data_types_["int8"] = OPT_INT8_TYPE;
+    data_types_["int16"] = OPT_INT16_TYPE;
+    data_types_["int32"] = OPT_INT32_TYPE;
+    data_types_["uint8"] = OPT_UINT8_TYPE;
+    data_types_["uint16"] = OPT_UINT16_TYPE;
+    data_types_["uint32"] = OPT_UINT32_TYPE;
+    data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE;
+    data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE;
+    data_types_["string"] = OPT_STRING_TYPE;
+    data_types_["fqdn"] = OPT_FQDN_TYPE;
+    data_types_["record"] = OPT_RECORD_TYPE;
+
+    data_type_names_[OPT_EMPTY_TYPE] = "empty";
+    data_type_names_[OPT_BINARY_TYPE] = "binary";
+    data_type_names_[OPT_BOOLEAN_TYPE] = "boolean";
+    data_type_names_[OPT_INT8_TYPE] = "int8";
+    data_type_names_[OPT_INT16_TYPE] = "int16";
+    data_type_names_[OPT_INT32_TYPE] = "int32";
+    data_type_names_[OPT_UINT8_TYPE] = "uint8";
+    data_type_names_[OPT_UINT16_TYPE] = "uint16";
+    data_type_names_[OPT_UINT32_TYPE] = "uint32";
+    data_type_names_[OPT_IPV4_ADDRESS_TYPE] = "ipv4-address";
+    data_type_names_[OPT_IPV6_ADDRESS_TYPE] = "ipv6-address";
+    data_type_names_[OPT_STRING_TYPE] = "string";
+    data_type_names_[OPT_FQDN_TYPE] = "fqdn";
+    data_type_names_[OPT_RECORD_TYPE] = "record";
+    // The "unknown" data type is declared here so as
+    // it can be returned by reference by a getDataTypeName
+    // function it no other type is suitable. Other than that
+    // this is unused.
+    data_type_names_[OPT_UNKNOWN_TYPE] = "unknown";
+}
+
+OptionDataType
+OptionDataTypeUtil::getDataType(const std::string& data_type) {
+    return (OptionDataTypeUtil::instance().getDataTypeImpl(data_type));
+}
+
+OptionDataType
+OptionDataTypeUtil::getDataTypeImpl(const std::string& data_type) const {
+    std::map<std::string, OptionDataType>::const_iterator data_type_it =
+        data_types_.find(data_type);
+    if (data_type_it != data_types_.end()) {
+        return (data_type_it->second);
+    }
+    return (OPT_UNKNOWN_TYPE);
+}
+
+int
+OptionDataTypeUtil::getDataTypeLen(const OptionDataType data_type) {
+    switch (data_type) {
+    case OPT_BOOLEAN_TYPE:
+    case OPT_INT8_TYPE:
+    case OPT_UINT8_TYPE:
+        return (1);
+
+    case OPT_INT16_TYPE:
+    case OPT_UINT16_TYPE:
+        return (2);
+
+    case OPT_INT32_TYPE:
+    case OPT_UINT32_TYPE:
+        return (4);
+
+    case OPT_IPV4_ADDRESS_TYPE:
+        return (asiolink::V4ADDRESS_LEN);
+
+    case OPT_IPV6_ADDRESS_TYPE:
+        return (asiolink::V6ADDRESS_LEN);
+
+    default:
+        ;
+    }
+    return (0);
+}
+
+const std::string&
+OptionDataTypeUtil::getDataTypeName(const OptionDataType data_type) {
+    return (OptionDataTypeUtil::instance().getDataTypeNameImpl(data_type));
+}
+
+const std::string&
+OptionDataTypeUtil::getDataTypeNameImpl(const OptionDataType data_type) const {
+    std::map<OptionDataType, std::string>::const_iterator data_type_it =
+        data_type_names_.find(data_type);
+    if (data_type_it != data_type_names_.end()) {
+        return (data_type_it->second);
+    }
+    return (data_type_names_.find(OPT_UNKNOWN_TYPE)->second);
+}
+
+OptionDataTypeUtil&
+OptionDataTypeUtil::instance() {
+    static OptionDataTypeUtil instance;
+    return (instance);
+}
+
+asiolink::IOAddress
+OptionDataTypeUtil::readAddress(const std::vector<uint8_t>& buf,
+                                const short family) {
+    using namespace isc::asiolink;
+    if (family == AF_INET) {
+        if (buf.size() < V4ADDRESS_LEN) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " IPv4 address. Invalid buffer size: " << buf.size());
+        }
+        return (IOAddress::fromBytes(AF_INET, &buf[0]));
+    } else if (family == AF_INET6) {
+        if (buf.size() < V6ADDRESS_LEN) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " IPv6 address. Invalid buffer size: " << buf.size());
+        }
+        return (IOAddress::fromBytes(AF_INET6, &buf[0]));
+    } else {
+        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                  "IP address. Invalid family: " << family);
+    }
+}
+
+void
+OptionDataTypeUtil::writeAddress(const asiolink::IOAddress& address,
+                                 std::vector<uint8_t>& buf) {
+    // @todo There is a ticket 2396 submitted, which adds the
+    // functionality to return a buffer representation of
+    // IOAddress. If so, this function can be simplified.
+    if (address.getAddress().is_v4()) {
+        asio::ip::address_v4::bytes_type addr_bytes =
+            address.getAddress().to_v4().to_bytes();
+        // Increase the buffer size by the size of IPv4 address.
+        buf.resize(buf.size() + addr_bytes.size());
+        std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+                           buf.end());
+    } else if (address.getAddress().is_v6()) {
+        asio::ip::address_v6::bytes_type addr_bytes =
+            address.getAddress().to_v6().to_bytes();
+        // Incresase the buffer size by the size of IPv6 address.
+        buf.resize(buf.size() + addr_bytes.size());
+        std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
+                           buf.end());
+    } else {
+        isc_throw(BadDataTypeCast, "the address " << address.toText()
+                  << " is neither valid IPv4 not IPv6 address.");
+    }
+}
+
+void
+OptionDataTypeUtil::writeBinary(const std::string& hex_str,
+                                std::vector<uint8_t>& buf) {
+    // Binary value means that the value is encoded as a string
+    // of hexadecimal digits. We need to decode this string
+    // to the binary format here.
+    OptionBuffer binary;
+    try {
+        util::encode::decodeHex(hex_str, binary);
+    } catch (const Exception& ex) {
+        isc_throw(BadDataTypeCast, "unable to cast " << hex_str
+                  << " to binary data type: " << ex.what());
+    }
+    // Decode was successful so append decoded binary value
+    // to the buffer.
+    buf.insert(buf.end(), binary.begin(), binary.end());
+}
+
+bool
+OptionDataTypeUtil::readBool(const std::vector<uint8_t>& buf) {
+    if (buf.size() < 1) {
+        isc_throw(BadDataTypeCast, "unable to read the buffer as boolean"
+                  << " value. Invalid buffer size " << buf.size());
+    }
+    if (buf[0] == 1) {
+        return (true);
+    } else if (buf[0] == 0) {
+        return (false);
+    }
+    isc_throw(BadDataTypeCast, "unable to read the buffer as boolean"
+              << " value. Invalid value " << static_cast<int>(buf[0]));
+}
+
+void
+OptionDataTypeUtil::writeBool(const bool value,
+                              std::vector<uint8_t>& buf) {
+    buf.push_back(static_cast<uint8_t>(value ? 1 : 0));
+}
+
+std::string
+OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
+    std::string value;
+    if (buf.size() > 0) {
+        value.insert(value.end(), buf.begin(), buf.end());
+    }
+    return (value);
+}
+
+void
+OptionDataTypeUtil::writeString(const std::string& value,
+                                std::vector<uint8_t>& buf) {
+    if (value.size() > 0) {
+        buf.insert(buf.end(), value.begin(), value.end());
+    }
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h
index 99b4220..afc6d93 100644
--- a/src/lib/dhcp/option_data_types.h
+++ b/src/lib/dhcp/option_data_types.h
@@ -16,7 +16,9 @@
 #define OPTION_DATA_TYPES_H
 
 #include <asiolink/io_address.h>
+#include <dhcp/option.h>
 #include <exceptions/exceptions.h>
+#include <util/io_utilities.h>
 
 #include <stdint.h>
 
@@ -39,6 +41,13 @@ public:
 
 
 /// @brief Data types of DHCP option fields.
+///
+/// @warning The order of data types matters: OPT_UNKNOWN_TYPE
+/// must always be the last position. Also, OPT_RECORD_TYPE
+/// must be at last but one position. This is because some
+/// functions perform sanity checks on data type values using
+/// '>' operators, assuming that all values beyond the
+/// OPT_RECORD_TYPE are invalid.
 enum OptionDataType {
     OPT_EMPTY_TYPE,
     OPT_BINARY_TYPE,
@@ -75,7 +84,7 @@ struct OptionDataTypeTraits {
 template<>
 struct OptionDataTypeTraits<OptionBuffer> {
     static const bool valid = true;
-    static const int len = sizeof(OptionBuffer);
+    static const int len = 0;
     static const bool integer_type = false;
     static const OptionDataType type = OPT_BINARY_TYPE;
 };
@@ -172,6 +181,210 @@ struct OptionDataTypeTraits<std::string> {
     static const OptionDataType type = OPT_STRING_TYPE;
 };
 
+/// @brief Utility class for option data types.
+///
+/// This class provides a set of utility functions to operate on
+/// supported DHCP option data types. It includes conversion
+/// between enumerator values representing data types and data
+/// type names. It also includes a set of functions that write
+/// data into option buffers and read data from option buffers.
+/// The data being written and read are converted from/to actual
+/// data types.
+/// @note This is a singleton class but it can be accessed via
+/// static methods only.
+class OptionDataTypeUtil {
+public:
+
+    /// @brief Return option data type from its name.
+    ///
+    /// @param data_type data type name.
+    /// @return option data type.
+    static OptionDataType getDataType(const std::string& data_type);
+
+    /// @brief Return option data type name from the data type enumerator.
+    ///
+    /// @param data_type option data type.
+    /// @return option data type name.
+    static const std::string& getDataTypeName(const OptionDataType data_type);
+
+    /// @brief Get data type buffer length.
+    ///
+    /// This function returns the size of a particular data type.
+    /// Values retured by this function correspond to the data type
+    /// sizes defined in OptionDataTypeTraits (IPV4_ADDRESS_TYPE and
+    /// IPV6_ADDRESS_TYPE are exceptions here) so they rather indicate
+    /// the fixed length of the data being written into the buffer,
+    /// not the size of the particular data type. Thus for data types
+    /// such as string, binary etc. for which the buffer length can't
+    /// be determined this function returns 0.
+    /// In addition, this function returns the data sizes for
+    /// IPV4_ADDRESS_TYPE and IPV6_ADDRESS_TYPE as their buffer
+    /// representations have fixed data lengths: 4 and 16 respectively.
+    ///
+    /// @param data_type data type which size is to be returned.
+    /// @return data type size or zero for variable length types.
+    static int getDataTypeLen(const OptionDataType data_type);
+
+    /// @brief Read IPv4 or IPv6 addres from a buffer.
+    ///
+    /// @param buf input buffer.
+    /// @param family address family: AF_INET or AF_INET6.
+    /// 
+    /// @return address being read.
+    static asiolink::IOAddress readAddress(const std::vector<uint8_t>& buf,
+                                           const short family);
+
+    /// @brief Append IPv4 or IPv6 address to a buffer.
+    ///
+    /// @param address IPv4 or IPv6 address.
+    /// @param [out] buf output buffer.
+    static void writeAddress(const asiolink::IOAddress& address,
+                             std::vector<uint8_t>& buf);
+
+    /// @brief Append hex-encoded binary values to a buffer.
+    ///
+    /// @param hex_str string representing a binary value encoded
+    /// with hexadecimal digits (without 0x prefix).
+    /// @param [out] buf output buffer.
+    static void writeBinary(const std::string& hex_str,
+                            std::vector<uint8_t>& buf);
+
+    /// @brief Read boolean value from a buffer.
+    ///
+    /// @param buf input buffer.
+    /// @return boolean value read from a buffer.
+    static bool readBool(const std::vector<uint8_t>& buf);
+
+    /// @brief Append boolean value into a buffer.
+    ///
+    /// The bool value is encoded in a buffer in such a way that
+    /// "1" means "true" and "0" means "false".
+    ///
+    /// @param value boolean value to be written.
+    /// @param [out] buf output buffer.
+    static void writeBool(const bool value, std::vector<uint8_t>& buf);
+
+    /// @brief Read integer value from a buffer.
+    ///
+    /// @param buf input buffer.
+    /// @tparam integer type of the returned value.
+    /// @return integer value being read.
+    template<typename T>
+    static T readInt(const std::vector<uint8_t>& buf) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(isc::dhcp::InvalidDataType, "specified data type to be returned"
+                      " by readInteger is unsupported integer type");
+        }
+
+        assert(buf.size() == OptionDataTypeTraits<T>::len);
+        T value;
+        switch (OptionDataTypeTraits<T>::len) {
+        case 1:
+            value = *(buf.begin());
+            break;
+        case 2:
+            // Calling readUint16 works either for unsigned
+            // or signed types.
+            value = isc::util::readUint16(&(*buf.begin()));
+            break;
+        case 4:
+            // Calling readUint32 works either for unsigned
+            // or signed types.
+            value = isc::util::readUint32(&(*buf.begin()));
+            break;
+        default:
+            // This should not happen because we made checks on data types
+            // but it does not hurt to keep throw statement here.
+            isc_throw(isc::dhcp::InvalidDataType,
+                      "invalid size of the data type to be read as integer.");
+        }
+        return (value);
+    }
+
+    /// @brief Append integer or unsigned integer value to a buffer.
+    ///
+    /// @param value an integer value to be written into a buffer.
+    /// @param [out] buf output buffer.
+    /// @tparam data type of the value.
+    template<typename T>
+    static void writeInt(const T value,
+                         std::vector<uint8_t>& buf) {
+        if (!OptionDataTypeTraits<T>::integer_type) {
+            isc_throw(InvalidDataType, "provided data type is not the supported.");
+        }
+        switch (OptionDataTypeTraits<T>::len) {
+        case 1:
+            buf.push_back(static_cast<uint8_t>(value));
+            break;
+        case 2:
+            buf.resize(buf.size() + 2);
+            isc::util::writeUint16(static_cast<uint16_t>(value), &buf[buf.size() - 2]);
+            break;
+        case 4:
+            buf.resize(buf.size() + 4);
+            isc::util::writeUint32(static_cast<uint32_t>(value), &buf[buf.size() - 4]);
+            break;
+        default:
+            // The cases above cover whole range of possible data lengths because
+            // we check at the beginning of this function that given data type is
+            // a supported integer type which can be only 1,2 or 4 bytes long.
+            ;
+        }
+    }
+
+    /// @brief Read string value from a buffer.
+    ///
+    /// @param buf input buffer.
+    ///
+    /// @return string value being read.
+    static std::string readString(const std::vector<uint8_t>& buf);
+
+    /// @brief Write UTF8-encoded string into a buffer.
+    ///
+    /// @param value string value to be written into a buffer.
+    /// @param [out] buf output buffer.
+    static void writeString(const std::string& value,
+                            std::vector<uint8_t>& buf);
+private:
+
+    /// The container holding mapping of data type names to
+    /// data types enumerator.
+    std::map<std::string, OptionDataType> data_types_;
+
+    /// The container holding mapping of data types to data
+    /// type names.
+    std::map<OptionDataType, std::string> data_type_names_;
+
+    /// @brief Private constructor.
+    ///
+    /// This constructor is private because this class should
+    /// be used as singleton (through static public functions).
+    OptionDataTypeUtil();
+
+    /// @brief Return instance of OptionDataTypeUtil
+    ///
+    /// This function is used by some of the public static functions
+    /// to create an instance of OptionDataTypeUtil class.
+    /// When instance is called it calls the class'es constructor
+    /// and initializes some of the private data members.
+    ///
+    /// @return instance of OptionDataTypeUtil singleton.
+    static OptionDataTypeUtil& instance();
+
+    /// @brief Return option data type from its name.
+    ///
+    /// @param data_type data type name.
+    /// @return option data type.
+    OptionDataType getDataTypeImpl(const std::string& data_type) const;
+
+    /// @brief Return option data type name from the data type enumerator.
+    ///
+    /// @param data_type option data type.
+    /// @return option data type name.
+    const std::string& getDataTypeNameImpl(const OptionDataType data_type) const;
+};
+
+
 } // isc::dhcp namespace
 } // isc namespace
 
diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc
index 62a81c2..58d0c4b 100644
--- a/src/lib/dhcp/option_definition.cc
+++ b/src/lib/dhcp/option_definition.cc
@@ -28,214 +28,6 @@ using namespace isc::util;
 namespace isc {
 namespace dhcp {
 
-OptionDefinition::DataTypeUtil::DataTypeUtil() {
-    data_types_["empty"] = OPT_EMPTY_TYPE;
-    data_types_["binary"] = OPT_BINARY_TYPE;
-    data_types_["boolean"] = OPT_BOOLEAN_TYPE;
-    data_types_["int8"] = OPT_INT8_TYPE;
-    data_types_["int16"] = OPT_INT16_TYPE;
-    data_types_["int32"] = OPT_INT32_TYPE;
-    data_types_["uint8"] = OPT_UINT8_TYPE;
-    data_types_["uint16"] = OPT_UINT16_TYPE;
-    data_types_["uint32"] = OPT_UINT32_TYPE;
-    data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE;
-    data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE;
-    data_types_["string"] = OPT_STRING_TYPE;
-    data_types_["fqdn"] = OPT_FQDN_TYPE;
-    data_types_["record"] = OPT_RECORD_TYPE;
-}
-
-OptionDataType
-OptionDefinition::DataTypeUtil::getOptionDataType(const std::string& data_type) {
-    std::map<std::string, OptionDataType>::const_iterator data_type_it =
-        data_types_.find(data_type);
-    if (data_type_it != data_types_.end()) {
-        return (data_type_it->second);
-    }
-    return (OPT_UNKNOWN_TYPE);
-}
-
-template<typename T>
-T OptionDefinition::DataTypeUtil::lexicalCastWithRangeCheck(const std::string& value_str) const {
-    // Lexical cast in case of our data types make sense only
-    // for uintX_t, intX_t and bool type.
-    if (!OptionDataTypeTraits<T>::integer_type &&
-        OptionDataTypeTraits<T>::type != OPT_BOOLEAN_TYPE) {
-        isc_throw(BadDataTypeCast, "unable to do lexical cast to non-integer and"
-                  << " non-boolean data type");
-    }
-    // We use the 64-bit value here because it has wider range than
-    // any other type we use here and it allows to detect out of
-    // bounds conditions e.g. negative value specified for uintX_t
-    // data type. Obviously if the value exceeds the limits of int64
-    // this function will not handle that properly.
-    int64_t result = 0;
-    try {
-        result = boost::lexical_cast<int64_t>(value_str);
-    } catch (const boost::bad_lexical_cast& ex) {
-        // Prepare error message here.
-        std::string data_type_str = "boolean";
-        if (OptionDataTypeTraits<T>::integer_type) {
-            data_type_str = "integer";
-        }
-        isc_throw(BadDataTypeCast, "unable to do lexical cast to " << data_type_str
-                  << " data type for value " << value_str << ": " << ex.what());
-    }
-    // Perform range checks for integer values only (exclude bool values).
-    if (OptionDataTypeTraits<T>::integer_type) {
-        if (result > numeric_limits<T>::max() ||
-            result < numeric_limits<T>::min()) {
-            isc_throw(BadDataTypeCast, "unable to do lexical cast for value "
-                      << value_str << ". This value is expected to be in the range of "
-                      << numeric_limits<T>::min() << ".." << numeric_limits<T>::max());
-        }
-    }
-    return (static_cast<T>(result));
-}
-
-void
-OptionDefinition::DataTypeUtil::writeToBuffer(const std::string& value,
-                                              const OptionDataType type,
-                                              OptionBuffer& buf) {
-    // We are going to write value given by value argument to the buffer.
-    // The actual type of the value is given by second argument. Check
-    // this argument to determine how to write this value to the buffer.
-    switch (type) {
-    case OPT_BINARY_TYPE:
-        {
-            // Binary value means that the value is encoded as a string
-            // of hexadecimal deigits. We need to decode this string
-            // to the binary format here.
-            OptionBuffer binary;
-            try {
-                util::encode::decodeHex(value, binary);
-            } catch (const Exception& ex) {
-                isc_throw(BadDataTypeCast, "unable to cast " << value
-                          << " to binary data type: " << ex.what());
-            }
-            // Decode was successful so append decoded binary value
-            // to the buffer.
-            buf.insert(buf.end(), binary.begin(), binary.end());
-            return;
-        }
-    case OPT_BOOLEAN_TYPE:
-        {
-            // We encode the true value as 1 and false as 0 on 8 bits.
-            // That way we actually waist 7 bits but it seems to be the
-            // simpler way to encode boolean.
-            // @todo Consider if any other encode methods can be used.
-            bool bool_value = lexicalCastWithRangeCheck<bool>(value);
-            if (bool_value) {
-                buf.push_back(static_cast<uint8_t>(1));
-            } else {
-                buf.push_back(static_cast<uint8_t>(0));
-            }
-            return;
-        }
-    case OPT_INT8_TYPE:
-        {
-            // Buffer holds the uin8_t values so we need to cast the signed
-            // value to unsigned but the bits values remain untouched.
-            buf.push_back(static_cast<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value)));
-            return;
-        }
-    case OPT_INT16_TYPE:
-        {
-            // Write the int16 value as uint16 value is ok because the bit values
-            // remain untouched.
-            int16_t int_value = lexicalCastWithRangeCheck<int16_t>(value);
-            buf.resize(buf.size() + 2);
-            writeUint16(static_cast<uint16_t>(int_value), &buf[buf.size() - 2]);
-            return;
-        }
-    case OPT_INT32_TYPE:
-        {
-            int32_t int_value = lexicalCastWithRangeCheck<int32_t>(value);
-            buf.resize(buf.size() + 4);
-            writeUint32(static_cast<uint32_t>(int_value), &buf[buf.size() - 4]);
-            return;
-        }
-    case OPT_UINT8_TYPE:
-        {
-            buf.push_back(lexicalCastWithRangeCheck<uint8_t>(value));
-            return;
-        }
-    case OPT_UINT16_TYPE:
-        {
-            uint16_t uint_value = lexicalCastWithRangeCheck<uint16_t>(value);
-            buf.resize(buf.size() + 2);
-            writeUint16(uint_value, &buf[buf.size() - 2]);
-            return;
-        }
-    case OPT_UINT32_TYPE:
-        {
-            uint32_t uint_value = lexicalCastWithRangeCheck<uint32_t>(value);
-            buf.resize(buf.size() + 4);
-            writeUint32(uint_value, &buf[buf.size() - 4]);
-            return;
-        }
-    case OPT_IPV4_ADDRESS_TYPE:
-        {
-            // The easiest way to get the binary form of IPv4 address is
-            // to create IOAddress object from string and use its accessors
-            // to retrieve the binary form.
-            asiolink::IOAddress address(value);
-            if (!address.getAddress().is_v4()) {
-                isc_throw(BadDataTypeCast, "provided address " << address.toText()
-                          << " is not a valid IPV4 address");
-            }
-            asio::ip::address_v4::bytes_type addr_bytes =
-                address.getAddress().to_v4().to_bytes();
-            // Increase the buffer size by the size of IPv4 address.
-            buf.resize(buf.size() + addr_bytes.size());
-            std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
-                               buf.end());
-            return;
-        }
-    case OPT_IPV6_ADDRESS_TYPE:
-        {
-            asiolink::IOAddress address(value);
-            if (!address.getAddress().is_v6()) {
-                isc_throw(BadDataTypeCast, "provided address " << address.toText()
-                          << " is not a valid IPV6 address");
-            }
-            asio::ip::address_v6::bytes_type addr_bytes =
-                address.getAddress().to_v6().to_bytes();
-            // Incresase the buffer size by the size of IPv6 address.
-            buf.resize(buf.size() + addr_bytes.size());
-            std::copy_backward(addr_bytes.begin(), addr_bytes.end(),
-                               buf.end());
-            return;
-        }
-    case OPT_STRING_TYPE:
-        if (value.size() > 0) {
-            // Increase the size of the storage by the size of the string.
-            buf.resize(buf.size() + value.size());
-            // Assuming that the string is already UTF8 encoded.
-            std::copy_backward(value.c_str(), value.c_str() + value.size(),
-                               buf.end());
-            return;
-        }
-    case OPT_FQDN_TYPE:
-        {
-            // FQDN implementation is not terribly complicated but will require
-            // creation of some additional logic (maybe object) that will parse
-            // the fqdn into labels.
-            isc_throw(isc::NotImplemented, "write of FQDN record into option buffer"
-                      " is not supported yet");
-            return;
-        }
-    default:
-        // We hit this point because invalid option data type has been specified
-        // This may be the case because 'empty' or 'record' data type has been
-        // specified. We don't throw exception here because it will be thrown
-        // at the exit point from this function.
-        ;
-    }
-    isc_throw(isc::BadValue, "attempt to write invalid option data field type"
-              " into the option buffer: " << type);
-
-}
 
 OptionDefinition::OptionDefinition(const std::string& name,
                                  const uint16_t code,
@@ -248,7 +40,7 @@ OptionDefinition::OptionDefinition(const std::string& name,
     // Data type is held as enum value by this class.
     // Use the provided option type string to get the
     // corresponding enum value.
-    type_ = DataTypeUtil::instance().getOptionDataType(type);
+    type_ = OptionDataTypeUtil::getDataType(type);
 }
 
 OptionDefinition::OptionDefinition(const std::string& name,
@@ -263,7 +55,7 @@ OptionDefinition::OptionDefinition(const std::string& name,
 
 void
 OptionDefinition::addRecordField(const std::string& data_type_name) {
-    OptionDataType data_type = DataTypeUtil::instance().getOptionDataType(data_type_name);
+    OptionDataType data_type = OptionDataTypeUtil::getDataType(data_type_name);
     addRecordField(data_type);
 }
 
@@ -273,8 +65,10 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
         isc_throw(isc::InvalidOperation, "'record' option type must be used"
                   " to add data fields to the record");
     }
-    if (data_type >= OPT_UNKNOWN_TYPE) {
-        isc_throw(isc::BadValue, "attempted to add invalid data type to the record");
+    if (data_type >= OPT_RECORD_TYPE ||
+        data_type == OPT_ANY_ADDRESS_TYPE ||
+        data_type == OPT_EMPTY_TYPE) {
+        isc_throw(isc::BadValue, "attempted to add invalid data type to the record.");
     }
     record_fields_.push_back(data_type);
 }
@@ -353,10 +147,10 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
         if (values.size() == 0) {
             isc_throw(InvalidOptionValue, "no option value specified");
         }
-        DataTypeUtil::instance().writeToBuffer(values[0], type_, buf);
+        writeToBuffer(values[0], type_, buf);
     } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
         for (size_t i = 0; i < values.size(); ++i) {
-            DataTypeUtil::instance().writeToBuffer(values[i], type_, buf);
+            writeToBuffer(values[i], type_, buf);
         }
     } else if (type_ == OPT_RECORD_TYPE) {
         const RecordFieldsCollection& records = getRecordFields();
@@ -366,7 +160,7 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                       << " provided.");
         }
         for (size_t i = 0; i < records.size(); ++i) {
-            DataTypeUtil::instance().writeToBuffer(values[i], records[i], buf);
+            writeToBuffer(values[i], records[i], buf);
         }
     }
     return (optionFactory(u, type, buf.begin(), buf.end()));
@@ -385,18 +179,24 @@ OptionDefinition::validate() const {
     std::ostringstream err_str;
     if (name_.empty()) {
         // Option name must not be empty.
-        err_str << "option name must not be empty";
+        err_str << "option name must not be empty.";
     } else if (name_.find(" ") != string::npos) {
         // Option name must not contain spaces.
-        err_str << "option name must not contain spaces";
+        err_str << "option name must not contain spaces.";
     } else if (type_ >= OPT_UNKNOWN_TYPE) {
         // Option definition must be of a known type.
-        err_str << "option type value " << type_ << " is out of range";
-    } else if (type_ == OPT_STRING_TYPE && array_type_) {
-        // Array of strings is not allowed because there is no way
-        // to determine the size of a particular string and thus there
-        // it no way to tell when other data fields begin.
-        err_str << "array of strings is not a valid option definition";
+        err_str << "option type value " << type_ << " is out of range.";
+    } else if (array_type_) {
+        if (type_ == OPT_STRING_TYPE) {
+            // Array of strings is not allowed because there is no way
+            // to determine the size of a particular string and thus there
+            // it no way to tell when other data fields begin.
+            err_str << "array of strings is not a valid option definition.";
+        } else if (type_ == OPT_BINARY_TYPE) {
+            err_str << "array of binary values is not a valid option definition.";
+        } else if (type_ == OPT_EMPTY_TYPE) {
+            err_str << "array of empty value is not a valid option definition.";
+        }
     } else if (type_ == OPT_RECORD_TYPE) {
         // At least two data fields should be added to the record. Otherwise
         // non-record option definition could be used.
@@ -406,8 +206,8 @@ OptionDefinition::validate() const {
                     << " least 2 fields.";
         } else {
             // If the number of fields is valid we have to check if their order
-            // is valid too. We check that string data fields are not laid before
-            // other fields. But we allow that they are laid at the end of
+            // is valid too. We check that string or binary data fields are not
+            // laid before other fields. But we allow that they are laid at the end of
             // an option.
             const RecordFieldsCollection& fields = getRecordFields();
             for (RecordFieldsConstIter it = fields.begin();
@@ -418,6 +218,17 @@ OptionDefinition::validate() const {
                             << " of other types.";
                     break;
                 }
+                if (*it == OPT_BINARY_TYPE &&
+                    it < fields.end() - 1) {
+                    err_str << "binary data field can't be laid before data fields"
+                            << " of other types.";
+                }
+                /// Empty type is not allowed within a record.
+                if (*it == OPT_EMPTY_TYPE) {
+                    err_str << "empty data type can't be stored as a field in an"
+                            << " option record.";
+                    break;
+                }
             }
         }
 
@@ -455,6 +266,124 @@ OptionDefinition::haveIAAddr6Format() const {
     return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE));
 }
 
+template<typename T>
+T OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) const {
+    // Lexical cast in case of our data types make sense only
+    // for uintX_t, intX_t and bool type.
+    if (!OptionDataTypeTraits<T>::integer_type &&
+        OptionDataTypeTraits<T>::type != OPT_BOOLEAN_TYPE) {
+        isc_throw(BadDataTypeCast, "unable to do lexical cast to non-integer and"
+                  << " non-boolean data type");
+    }
+    // We use the 64-bit value here because it has wider range than
+    // any other type we use here and it allows to detect out of
+    // bounds conditions e.g. negative value specified for uintX_t
+    // data type. Obviously if the value exceeds the limits of int64
+    // this function will not handle that properly.
+    int64_t result = 0;
+    try {
+        result = boost::lexical_cast<int64_t>(value_str);
+    } catch (const boost::bad_lexical_cast& ex) {
+        // Prepare error message here.
+        std::string data_type_str = "boolean";
+        if (OptionDataTypeTraits<T>::integer_type) {
+            data_type_str = "integer";
+        }
+        isc_throw(BadDataTypeCast, "unable to do lexical cast to " << data_type_str
+                  << " data type for value " << value_str << ": " << ex.what());
+    }
+    // Perform range checks for integer values only (exclude bool values).
+    if (OptionDataTypeTraits<T>::integer_type) {
+        if (result > numeric_limits<T>::max() ||
+            result < numeric_limits<T>::min()) {
+            isc_throw(BadDataTypeCast, "unable to do lexical cast for value "
+                      << value_str << ". This value is expected to be in the range of "
+                      << numeric_limits<T>::min() << ".." << numeric_limits<T>::max());
+        }
+    }
+    return (static_cast<T>(result));
+}
+
+void
+OptionDefinition::writeToBuffer(const std::string& value,
+                                const OptionDataType type,
+                                OptionBuffer& buf) const {
+    // We are going to write value given by value argument to the buffer.
+    // The actual type of the value is given by second argument. Check
+    // this argument to determine how to write this value to the buffer.
+    switch (type) {
+    case OPT_BINARY_TYPE:
+        OptionDataTypeUtil::writeBinary(value, buf);
+        return;
+    case OPT_BOOLEAN_TYPE:
+        // We encode the true value as 1 and false as 0 on 8 bits.
+        // That way we actually waste 7 bits but it seems to be the
+        // simpler way to encode boolean.
+        // @todo Consider if any other encode methods can be used.
+        OptionDataTypeUtil::writeBool(lexicalCastWithRangeCheck<bool>(value), buf);
+        return;
+    case OPT_INT8_TYPE:
+        OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value),
+                                              buf);
+        return;
+    case OPT_INT16_TYPE:
+        OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<int16_t>(value),
+                                               buf);
+        return;
+    case OPT_INT32_TYPE:
+        OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<int32_t>(value),
+                                               buf);
+        return;
+    case OPT_UINT8_TYPE:
+        OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<uint8_t>(value),
+                                              buf);
+        return;
+    case OPT_UINT16_TYPE:
+        OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<uint16_t>(value),
+                                               buf);
+        return;
+    case OPT_UINT32_TYPE:
+        OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<uint32_t>(value),
+                                               buf);
+        return;
+    case OPT_IPV4_ADDRESS_TYPE:
+    case OPT_IPV6_ADDRESS_TYPE:
+        {
+            asiolink::IOAddress address(value);
+            if (address.getFamily() != AF_INET &&
+                address.getFamily() != AF_INET6) {
+                isc_throw(BadDataTypeCast, "provided address " << address.toText()
+                          << " is not a valid "
+                          << (address.getAddress().is_v4() ? "IPv4" : "IPv6")
+                          << " address");
+            }
+            OptionDataTypeUtil::writeAddress(address, buf);
+            return;
+        }
+    case OPT_STRING_TYPE:
+        OptionDataTypeUtil::writeString(value, buf);
+        return;
+    case OPT_FQDN_TYPE:
+        {
+            // FQDN implementation is not terribly complicated but will require
+            // creation of some additional logic (maybe object) that will parse
+            // the fqdn into labels.
+            isc_throw(isc::NotImplemented, "write of FQDN record into option buffer"
+                      " is not supported yet");
+            return;
+        }
+    default:
+        // We hit this point because invalid option data type has been specified
+        // This may be the case because 'empty' or 'record' data type has been
+        // specified. We don't throw exception here because it will be thrown
+        // at the exit point from this function.
+        ;
+    }
+    isc_throw(isc::BadValue, "attempt to write invalid option data field type"
+              " into the option buffer: " << type);
+
+}
+
 OptionPtr
 OptionDefinition::factoryAddrList4(uint16_t type,
                                   OptionBufferConstIter begin,
diff --git a/src/lib/dhcp/option_definition.h b/src/lib/dhcp/option_definition.h
index 18f79d2..3d48ef2 100644
--- a/src/lib/dhcp/option_definition.h
+++ b/src/lib/dhcp/option_definition.h
@@ -131,83 +131,6 @@ public:
     /// Const iterator for record data fields.
     typedef std::vector<OptionDataType>::const_iterator RecordFieldsConstIter;
 
-private:
-
-    /// @brief Utility class for operations on OptionDataTypes.
-    ///
-    /// This class is implemented as the singleton because the list of
-    /// supported data types need only be loaded only once into memory as it
-    /// can persist for all option definitions.
-    ///
-    /// @todo This class can be extended to return the string value
-    /// representing the data type from the enum value.
-    class DataTypeUtil {
-    public:
-
-        /// @brief Return the sole instance of this class.
-        ///
-        /// @return instance of this class.
-        static DataTypeUtil& instance() {
-            static DataTypeUtil instance;
-            return (instance);
-        }
-
-        /// @brief Convert type given as string value to option data type.
-        ///
-        /// @param data_type_name data type string.
-        ///
-        /// @return option data type.
-        OptionDataType getOptionDataType(const std::string& data_type_name);
-
-        /// @brief Perform lexical cast of the value and validate its range.
-        ///
-        /// This function performs lexical cast of a string value to integer
-        /// or boolean value and checks if the resulting value is within a
-        /// range of a target type. Note that range checks are not performed
-        /// on boolean values. The target type should be one of the supported
-        /// integer types or bool.
-        ///
-        /// @param value_str input value given as string.
-        /// @tparam T target type for lexical cast.
-        ///
-        /// @return cast value.
-        /// @throw BadDataTypeCast if cast was not successful.
-        template<typename T>
-        T lexicalCastWithRangeCheck(const std::string& value_str) const;
-
-        /// @brief Write the string value into the provided buffer.
-        ///
-        /// This method writes the given value to the specified buffer.
-        /// The provided string value may represent data of different types.
-        /// The actual data type is specified with the second argument.
-        /// Based on a value of this argument, this function will first
-        /// try to cast the string value to the particular data type and
-        /// if it is successful it will store the data in the buffer
-        /// in a binary format.
-        ///
-        /// @param value string representation of the value to be written.
-        /// @param type the actual data type to be stored.
-        /// @param [in, out] buf buffer where the value is to be stored.
-        ///
-        /// @throw BadDataTypeCast if data write was unsuccessful.
-        void writeToBuffer(const std::string& value, const OptionDataType type,
-                           OptionBuffer& buf);
-
-    private:
-        /// @brief Private constructor.
-        ///
-        /// Constructor initializes the internal data structures, e.g.
-        /// mapping between data type name and the corresponding enum.
-        /// This constructor is private to ensure that exactly one
-        /// instance of this class can be created using \ref instance
-        /// function.
-        DataTypeUtil();
-
-        /// Map of data types, maps name of the type to enum value.
-        std::map<std::string, OptionDataType> data_types_;
-    };
-
-public:
     /// @brief Constructor.
     ///
     /// @param name option name.
@@ -471,6 +394,40 @@ private:
         return (type == type_);
     }
 
+    /// @brief Perform lexical cast of the value and validate its range.
+    ///
+    /// This function performs lexical cast of a string value to integer
+    /// or boolean value and checks if the resulting value is within a
+    /// range of a target type. Note that range checks are not performed
+    /// on boolean values. The target type should be one of the supported
+    /// integer types or bool.
+    ///
+    /// @param value_str input value given as string.
+    /// @tparam T target type for lexical cast.
+    ///
+    /// @return cast value.
+    /// @throw BadDataTypeCast if cast was not successful.
+    template<typename T>
+    T lexicalCastWithRangeCheck(const std::string& value_str) const;
+
+    /// @brief Write the string value into the provided buffer.
+    ///
+    /// This method writes the given value to the specified buffer.
+    /// The provided string value may represent data of different types.
+    /// The actual data type is specified with the second argument.
+    /// Based on a value of this argument, this function will first
+    /// try to cast the string value to the particular data type and
+    /// if it is successful it will store the data in the buffer
+    /// in a binary format.
+    ///
+    /// @param value string representation of the value to be written.
+    /// @param type the actual data type to be stored.
+    /// @param [in, out] buf buffer where the value is to be stored.
+    ///
+    /// @throw BadDataTypeCast if data write was unsuccessful.
+    void writeToBuffer(const std::string& value, const OptionDataType type,
+                       OptionBuffer& buf) const;
+
     /// @brief Sanity check universe value.
     ///
     /// @param expected_universe expected universe value.
diff --git a/src/lib/dhcp/tests/Makefile.am b/src/lib/dhcp/tests/Makefile.am
index 945f822..e66d700 100644
--- a/src/lib/dhcp/tests/Makefile.am
+++ b/src/lib/dhcp/tests/Makefile.am
@@ -36,6 +36,7 @@ libdhcp___unittests_SOURCES += option6_iaaddr_unittest.cc
 libdhcp___unittests_SOURCES += option6_int_array_unittest.cc
 libdhcp___unittests_SOURCES += option6_int_unittest.cc
 libdhcp___unittests_SOURCES += option_definition_unittest.cc
+libdhcp___unittests_SOURCES += option_custom_unittest.cc
 libdhcp___unittests_SOURCES += option_unittest.cc
 libdhcp___unittests_SOURCES += pkt4_unittest.cc
 libdhcp___unittests_SOURCES += pkt6_unittest.cc
diff --git a/src/lib/dhcp/tests/option_custom_unittest.cc b/src/lib/dhcp/tests/option_custom_unittest.cc
new file mode 100644
index 0000000..34969ee
--- /dev/null
+++ b/src/lib/dhcp/tests/option_custom_unittest.cc
@@ -0,0 +1,906 @@
+// Copyright (C) 2012 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 <asiolink/io_address.h>
+#include <dhcp/option_custom.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::dhcp;
+
+namespace {
+
+/// @brief OptionCustomTest test class.
+class OptionCustomTest : public ::testing::Test {
+public:
+    /// @brief Constructor.
+    OptionCustomTest() { }
+
+    /// @brief Write IP address into a buffer.
+    ///
+    /// @param address address to be written.
+    /// @param [out] buf output buffer.
+    void writeAddress(const asiolink::IOAddress& address,
+                      std::vector<uint8_t>& buf) {
+        short family = address.getFamily();
+        if (family == AF_INET) {
+            asio::ip::address_v4::bytes_type buf_addr =
+                address.getAddress().to_v4().to_bytes();
+            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
+        } else if (family == AF_INET6) {
+            asio::ip::address_v6::bytes_type buf_addr =
+                address.getAddress().to_v6().to_bytes();
+            buf.insert(buf.end(), buf_addr.begin(), buf_addr.end());
+        }
+    }
+
+    /// @brief Write integer (signed or unsigned) into a buffer.
+    ///
+    /// @param value integer value.
+    /// @param [out] buf output buffer.
+    /// @tparam integer type.
+    template<typename T>
+    void writeInt(T value, std::vector<uint8_t>& buf) {
+        for (int i = 0; i < sizeof(T); ++i) {
+            buf.push_back(value >> ((sizeof(T) - i - 1) * 8) & 0xFF);
+        }
+    }
+
+    /// @brief Write a string into a buffer.
+    ///
+    /// @param value string to be written into a buffer.
+    /// @param buf output buffer.
+    void writeString(const std::string& value,
+                     std::vector<uint8_t>& buf) {
+        buf.resize(buf.size() + value.size());
+        std::copy_backward(value.c_str(), value.c_str() + value.size(),
+                           buf.end());
+    }
+};
+
+// The purpose of this test is to check that parameters passed to
+// a custom option's constructor are used to initialize class
+// members.
+TEST_F(OptionCustomTest, constructor) {
+    // Create option definition for a DHCPv6 option.
+    OptionDefinition opt_def1("OPTION_FOO", 1000, "boolean", true);
+
+    // Initialize some dummy buffer that holds single boolean value.
+    OptionBuffer buf;
+    buf.push_back(1);
+
+    // Create DHCPv6 option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def1, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // Check if constructor initialized the universe and type correctly.
+    EXPECT_EQ(Option::V6, option->getUniverse());
+    EXPECT_EQ(1000, option->getType());
+
+    // Do another round of testing for DHCPv4 option.
+    OptionDefinition opt_def2("OPTION_FOO", 232, "boolean");
+
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def2, Option::V4, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    EXPECT_EQ(Option::V4, option->getUniverse());
+    EXPECT_EQ(232, option->getType());
+}
+
+// The purpose of this test is to verify that 'empty' option definition can
+// be used to create an instance of custom option.
+TEST_F(OptionCustomTest, emptyData) {
+    OptionDefinition opt_def("OPTION_FOO", 232, "empty");
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, OptionBuffer()));
+    );
+    ASSERT_TRUE(option);
+
+    // Option is 'empty' so no data fields are expected.
+    EXPECT_EQ(0, option->getDataFieldsNum());
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// a binary value can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, binaryData) {
+    OptionDefinition opt_def("OPTION_FOO", 231, "binary");
+
+    // Create a buffer holding some binary data. This data will be
+    // used as reference when we read back the data from a created
+    // option.
+    OptionBuffer buf_in(14);
+    for (int i = 0; i < 14; ++i) {
+        buf_in[i] = i;
+    }
+    // Use scoped pointer because it allows to declare the option
+    // in the function scope and initialize it under ASSERT.
+    boost::scoped_ptr<OptionCustom> option;
+    // Custom option may throw exception if the provided buffer is
+    // malformed.
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf_in));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // The custom option should hold just one buffer that can be
+    // accessed using index 0.
+    OptionBuffer buf_out;
+    ASSERT_NO_THROW(buf_out = option->readBinary(0));
+
+    // Read buffer must match exactly with the buffer used to
+    // create option instance.
+    ASSERT_EQ(buf_in.size(), buf_out.size());
+    EXPECT_TRUE(std::equal(buf_in.begin(), buf_in.end(), buf_out.begin()));
+
+    // Check that option with "no data" is rejected.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, OptionBuffer())),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that an option definition comprising
+// a single boolean value can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, booleanData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean");
+
+    OptionBuffer buf;
+    // Push back the value that represents 'false'.
+    buf.push_back(0);
+    // Push back the 'true' value. Note that this value should
+    // be ignored by custom option because it holds single boolean
+    // value (according to option definition).
+    buf.push_back(1);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // Initialize the value to true because we want to make sure
+    // that it is modified to 'false' by readBoolean below.
+    bool value = true;
+
+    // Read the boolean value from only one available buffer indexed
+    // with 0. It is expected to be 'false'.
+    ASSERT_NO_THROW(value = option->readBoolean(0));
+    EXPECT_FALSE(value);
+
+    // Check that the option with "no data" is rejected.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// 16-bit signed integer value can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, int16Data) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "int16");
+
+    OptionBuffer buf;
+    // Store signed integer value in the input buffer.
+    writeInt<int16_t>(-234, buf);
+
+    // Create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // Initialize value to 0 explicitely to make sure that is
+    // modified by readInteger function to expected -234.
+    int16_t value = 0;
+    ASSERT_NO_THROW(value = option->readInteger<int16_t>(0));
+    EXPECT_EQ(-234, value);
+
+    // Check that the option is not created when a buffer is
+    // too short (1 byte instead of 2 bytes).
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 1)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// 32-bit signed integer value can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, int32Data) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "int32");
+
+    OptionBuffer buf;
+    writeInt<int32_t>(-234, buf);
+    writeInt<int32_t>(100, buf);
+
+    // Create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // Initialize value to 0 explicitely to make sure that is
+    // modified by readInteger function to expected -234.
+    int32_t value = 0;
+    ASSERT_NO_THROW(value = option->readInteger<int32_t>(0));
+    EXPECT_EQ(-234, value);
+
+    // Check that the option is not created when a buffer is
+    // too short (3 bytes instead of 4 bytes).
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 3)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// single IPv4 addres can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, ipv4AddressData) {
+    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address");
+
+    // Create input buffer.
+    OptionBuffer buf;
+    writeAddress(IOAddress("192.168.100.50"), buf);
+
+    // Create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    IOAddress address("127.0.0.1");
+    // Read IPv4 address from using index 0.
+    ASSERT_NO_THROW(address = option->readAddress(0));
+
+    EXPECT_EQ("192.168.100.50", address.toText());
+
+    // Check that option is not created if the provided buffer is
+    // too short (use 3 bytes instead of 4).
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.begin() + 3)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// single IPv6 addres can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, ipv6AddressData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address");
+
+    // Initialize input buffer.
+    OptionBuffer buf;
+    writeAddress(IOAddress("2001:db8:1::100"), buf);
+
+    // Create custom option using input buffer.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // Custom option should comprise exactly one buffer that represents
+    // IPv6 address.
+    IOAddress address("::1");
+    // Read an address from buffer #0.
+    ASSERT_NO_THROW(address = option->readAddress(0));
+
+    EXPECT_EQ("2001:db8:1::100", address.toText());
+
+    // Check that option is not created if the provided buffer is
+    // too short (use 15 bytes instead of 16).
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
+                                      buf.begin() + 15)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// string value can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, stringData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "string");
+
+    // Create an input buffer holding some string value.
+    OptionBuffer buf;
+    writeString("hello world!", buf);
+
+    // Create custom option using input buffer.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have just one data field.
+    ASSERT_EQ(1, option->getDataFieldsNum());
+
+    // Custom option should now comprise single string value that
+    // can be accessed using index 0.
+    std::string value;
+    ASSERT_NO_THROW(value = option->readString(0));
+
+    EXPECT_EQ("hello world!", value);
+
+    // Check that option will not be created if empty buffer is provided.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// an array of boolean values can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, booleanDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "boolean", true);
+
+    // Create a buffer with 5 values that represent array of
+    // booleans.
+    OptionBuffer buf(5);
+    buf[0] = 1; // true
+    buf[1] = 0; // false
+    buf[2] = 0; // false
+    buf[3] = 1; // true
+    buf[4] = 1; // true
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 5 data fields.
+    ASSERT_EQ(5, option->getDataFieldsNum());
+
+    // Read values from custom option using indexes 0..4 and
+    // check that they are valid.
+    bool value0 = false;
+    ASSERT_NO_THROW(value0 = option->readBoolean(0));
+    EXPECT_TRUE(value0);
+
+    bool value1 = true;
+    ASSERT_NO_THROW(value1 = option->readBoolean(1));
+    EXPECT_FALSE(value1);
+
+    bool value2 = true;
+    ASSERT_NO_THROW(value2 = option->readBoolean(2));
+    EXPECT_FALSE(value2);
+
+    bool value3 = false;
+    ASSERT_NO_THROW(value3 = option->readBoolean(3));
+    EXPECT_TRUE(value3);
+
+    bool value4 = false;
+    ASSERT_NO_THROW(value4 = option->readBoolean(4));
+    EXPECT_TRUE(value4);
+
+    // Check that empty buffer can't be used to create option holding
+    // array of boolean values.
+    EXPECT_THROW(
+         option.reset(new OptionCustom(opt_def, Option::V6, OptionBuffer())),
+         isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// an array of 32-bit signed integer values can be used to create an instance
+// of custom option.
+TEST_F(OptionCustomTest, uint32DataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "uint32", true);
+
+    // Create an input buffer that holds 4 uint32 values that
+    // represent an array.
+    std::vector<uint32_t> values;
+    values.push_back(71234);
+    values.push_back(12234);
+    values.push_back(54362);
+    values.push_back(1234);
+
+    // Store these values in a buffer.
+    OptionBuffer buf;
+    for (int i = 0; i < values.size(); ++i) {
+        writeInt<uint32_t>(values[i], buf);
+    }
+    // Create custom option using the input buffer.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        // Note that we just use a part of the whole buffer here: 13 bytes. We want to
+        // check that buffer length which is non-divisible by 4 (size of uint32_t) is
+        // accepted and only 3 (instead of 4) elements will be stored in a custom option.
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 13));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // Expect only 3 values.
+    for (int i = 0; i < 3; ++i) {
+        uint32_t value = 0;
+        ASSERT_NO_THROW(value = option->readInteger<uint32_t>(i));
+        EXPECT_EQ(values[i], value);
+    }
+
+    // Check that too short buffer can't be used to create the option.
+    // Using buffer having length of 3 bytes. The length of 4 bytes is
+    // a minimal length to create the option with single uint32_t value.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                      buf.begin() + 3)),
+        isc::OutOfRange
+    );
+
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// an array of IPv4 addresses can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, ipv4AddressDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address", true);
+
+    // Initialize reference data.
+    std::vector<IOAddress> addresses;
+    addresses.push_back(IOAddress("192.168.0.1"));
+    addresses.push_back(IOAddress("127.0.0.1"));
+    addresses.push_back(IOAddress("10.10.1.2"));
+
+    // Store the collection of IPv4 addresses into the buffer.
+    OptionBuffer buf;
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // We expect 3 IPv4 addresses being stored in the option.
+    for (int i = 0; i < 3; ++i) {
+        IOAddress address("10.10.10.10");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+
+    // Check that it is ok if buffer length is not a multiple of IPv4
+    // address length. Resize it by two bytes.
+    buf.resize(buf.size() + 2);
+    EXPECT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf));
+    );
+
+    // Check that option is not created when the provided buffer
+    // is too short. At least a buffer length of 4 bytes is needed.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(),
+                                      buf.begin() + 2)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// an array of IPv6 addresses can be used to create an instance of custom option.
+TEST_F(OptionCustomTest, ipv6AddressDataArray) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
+
+    // Initialize reference data.
+    std::vector<IOAddress> addresses;
+    addresses.push_back(IOAddress("2001:db8:1::3"));
+    addresses.push_back(IOAddress("::1"));
+    addresses.push_back(IOAddress("fe80::3"));
+
+    // Store the collection of IPv6 addresses into the buffer.
+    OptionBuffer buf;
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // We expect 3 IPv6 addresses being stored in the option.
+    for (int i = 0; i < 3; ++i) {
+        IOAddress address("fe80::4");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+
+    // Check that it is ok if buffer length is not a multiple of IPv6
+    // address length. Resize it by two bytes.
+    buf.resize(buf.size() + 2);
+    EXPECT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+
+    // Check that option is not created when the provided buffer
+    // is too short. At least a buffer length of 16 bytes is needed.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(),
+                                      buf.begin() + 15)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that the option definition comprising
+// a record of various data fields can be used to create an instance of
+// custom option.
+TEST_F(OptionCustomTest, recordData) {
+    // Create the definition of an option which comprises
+    // a record of fields of different types.
+    OptionDefinition opt_def("OPTION_FOO", 1000, "record");
+    ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
+    ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv4-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("string"));
+
+    OptionBuffer buf;
+    // Initialize field 0.
+    writeInt<uint16_t>(8712, buf);
+    // Initialize field 1 to 'true'
+    buf.push_back(static_cast<unsigned short>(1));
+    // Initialize field 2 to IPv4 address.
+    writeAddress(IOAddress("192.168.0.1"), buf);
+    // Initialize field 3 to IPv6 address.
+    writeAddress(IOAddress("2001:db8:1::1"), buf);
+    // Initialize field 4 to string value.
+    writeString("ABCD", buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+         option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 5 data fields.
+    ASSERT_EQ(5, option->getDataFieldsNum());
+
+    // Verify value in the field 0.
+    uint16_t value0 = 0;
+    ASSERT_NO_THROW(value0 = option->readInteger<uint16_t>(0));
+    EXPECT_EQ(8712, value0);
+
+    // Verify value in the field 1.
+    bool value1 = false;
+    ASSERT_NO_THROW(value1 = option->readBoolean(1));
+    EXPECT_TRUE(value1);
+
+    // Verify value in the field 2.
+    IOAddress value2("127.0.0.1");
+    ASSERT_NO_THROW(value2 = option->readAddress(2));
+    EXPECT_EQ("192.168.0.1", value2.toText());
+
+    // Verify value in the field 3.
+    IOAddress value3("::1");
+    ASSERT_NO_THROW(value3 = option->readAddress(3));
+    EXPECT_EQ("2001:db8:1::1", value3.toText());
+
+    // Verify value in the field 4.
+    std::string value4;
+    ASSERT_NO_THROW(value4 = option->readString(4));
+    EXPECT_EQ("ABCD", value4);
+}
+
+// The purpose of this test is to verify that truncated buffer
+// can't be used to create an option being a record of value of
+// different types.
+TEST_F(OptionCustomTest, recordDataTruncated) {
+    // Create the definition of an option which comprises
+    // a record of fields of different types.
+    OptionDefinition opt_def("OPTION_FOO", 1000, "record");
+    ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
+    ASSERT_NO_THROW(opt_def.addRecordField("ipv6-address"));
+    ASSERT_NO_THROW(opt_def.addRecordField("string"));
+
+    OptionBuffer buf;
+    // Initialize field 0.
+    writeInt<uint16_t>(8712, buf);
+    // Initialize field 1 to IPv6 address.
+    writeAddress(IOAddress("2001:db8:1::1"), buf);
+    // Initialize field 2 to string value.
+    writeString("ABCD", buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+
+    // Constructor should not throw exception here because the length of the
+    // buffer meets the minimum length. The first 19 bytes hold data for
+    // all option fields: uint16, IPv4 address and first letter of string.
+    // Note that string will be truncated but this is acceptable because
+    // constructor have no way to determine the length of the original string.
+    EXPECT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 19));
+    );
+
+    // Reduce the buffer length by one byte should cause the constructor
+    // to fail. This is because 18 bytes can only hold first two data fields:
+    // 2 bytes of uint16_t value and IPv6 address. Option definitions specifies
+    // 3 data fields for this option but the length of the data is insufficient
+    // to initialize 3 data field.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 18)),
+        isc::OutOfRange
+    );
+
+    // Try to further reduce the length of the buffer to make it insufficient
+    // to even initialize the second data field.
+    EXPECT_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.begin() + 17)),
+        isc::OutOfRange
+    );
+}
+
+// The purpose of this test is to verify that pack function for
+// DHCPv4 custom option works correctly.
+TEST_F(OptionCustomTest, pack4) {
+    OptionDefinition opt_def("OPTION_FOO", 234, "record");
+    ASSERT_NO_THROW(opt_def.addRecordField("uint8"));
+    ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
+    ASSERT_NO_THROW(opt_def.addRecordField("uint32"));
+
+    OptionBuffer buf;
+    writeInt<uint8_t>(1, buf);
+    writeInt<uint16_t>(1000, buf);
+    writeInt<uint32_t>(100000, buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf));
+    );
+    ASSERT_TRUE(option);
+
+    util::OutputBuffer buf_out(7);
+    ASSERT_NO_THROW(option->pack(buf_out));
+    ASSERT_EQ(9, buf_out.getLength());
+
+    // The original buffer holds the option data but it lacks a header.
+    // We append data length and option code so as it can be directly
+    // compared with the output buffer that holds whole option.
+    buf.insert(buf.begin(), 7);
+    buf.insert(buf.begin(), 234);
+
+    // Validate the buffer.
+    EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
+}
+
+// The purpose of this test is to verify that pack function for
+// DHCPv6 custom option works correctly.
+TEST_F(OptionCustomTest, pack6) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "record");
+    ASSERT_NO_THROW(opt_def.addRecordField("boolean"));
+    ASSERT_NO_THROW(opt_def.addRecordField("uint16"));
+    ASSERT_NO_THROW(opt_def.addRecordField("string"));
+
+    OptionBuffer buf;
+    buf.push_back(1);
+    writeInt<uint16_t>(1000, buf);
+    writeString("hello world", buf);
+
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    util::OutputBuffer buf_out(buf.size() + option->getHeaderLen());
+    ASSERT_NO_THROW(option->pack(buf_out));
+    ASSERT_EQ(buf.size() + option->getHeaderLen(), buf_out.getLength());
+
+    // The original buffer holds the option data but it lacks a header.
+    // We append data length and option code so as it can be directly
+    // compared with the output buffer that holds whole option.
+    OptionBuffer tmp;
+    writeInt<uint16_t>(1000, tmp);
+    writeInt<uint16_t>(buf.size(), tmp);
+    buf.insert(buf.begin(), tmp.begin(), tmp.end());
+
+    // Validate the buffer.
+    EXPECT_EQ(0, memcmp(&buf[0], buf_out.getData(), 7));
+}
+
+// The purpose of this test is to verify that unpack function works
+// correctly for a custom option.
+TEST_F(OptionCustomTest, unpack) {
+    OptionDefinition opt_def("OPTION_FOO", 231, "ipv4-address", true);
+
+    // Initialize reference data.
+    std::vector<IOAddress> addresses;
+    addresses.push_back(IOAddress("192.168.0.1"));
+    addresses.push_back(IOAddress("127.0.0.1"));
+    addresses.push_back(IOAddress("10.10.1.2"));
+
+    // Store the collection of IPv4 addresses into the buffer.
+    OptionBuffer buf;
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V4, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // We expect 3 IPv4 addresses being stored in the option.
+    for (int i = 0; i < 3; ++i) {
+        IOAddress address("10.10.10.10");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+
+    // Remove all addresses we had added. We are going to replace
+    // them with a new set of addresses.
+    addresses.clear();
+
+    // Add new addresses.
+    addresses.push_back(IOAddress("10.1.2.3"));
+    addresses.push_back(IOAddress("85.26.43.234"));
+
+    // Clear the buffer as we need to store new addresses in it.
+    buf.clear();
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Perform 'unpack'.
+    ASSERT_NO_THROW(option->unpack(buf.begin(), buf.end()));
+
+    // Now we should have only 2 data fields.
+    ASSERT_EQ(2, option->getDataFieldsNum());
+
+    // Verify that the addresses have been overwritten.
+    for (int i = 0; i < 2; ++i) {
+        IOAddress address("10.10.10.10");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+}
+
+// The purpose of this test is to verify that new data can be set for
+// a custom option.
+TEST_F(OptionCustomTest, setData) {
+    OptionDefinition opt_def("OPTION_FOO", 1000, "ipv6-address", true);
+
+    // Initialize reference data.
+    std::vector<IOAddress> addresses;
+    addresses.push_back(IOAddress("2001:db8:1::3"));
+    addresses.push_back(IOAddress("::1"));
+    addresses.push_back(IOAddress("fe80::3"));
+
+    // Store the collection of IPv6 addresses into the buffer.
+    OptionBuffer buf;
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf.begin(), buf.end()));
+    );
+    ASSERT_TRUE(option);
+
+    // We should have 3 data fields.
+    ASSERT_EQ(3, option->getDataFieldsNum());
+
+    // We expect 3 IPv6 addresses being stored in the option.
+    for (int i = 0; i < 3; ++i) {
+        IOAddress address("fe80::4");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+
+    // Clear addresses we had previously added.
+    addresses.clear();
+
+    // Store new addresses.
+    addresses.push_back(IOAddress("::1"));
+    addresses.push_back(IOAddress("fe80::10"));
+
+    // Clear the buffer as we need to store new addresses in it.
+    buf.clear();
+    for (int i = 0; i < addresses.size(); ++i) {
+        writeAddress(addresses[i], buf);
+    }
+
+    // Replace the option data.
+    ASSERT_NO_THROW(option->setData(buf.begin(), buf.end()));
+
+    // Now we should have only 2 data fields.
+    ASSERT_EQ(2, option->getDataFieldsNum());
+
+    // Check that it has been replaced.
+    for (int i = 0; i < 2; ++i) {
+        IOAddress address("10.10.10.10");
+        ASSERT_NO_THROW(address = option->readAddress(i));
+        EXPECT_EQ(addresses[i].toText(), address.toText());
+    }
+}
+
+// The purpose of this test is to verify that an invalid index
+// value can't be used to access option data fields.
+TEST_F(OptionCustomTest, invalidIndex) {
+    OptionDefinition opt_def("OPTION_FOO", 999, "uint32", true);
+
+    OptionBuffer buf;
+    for (int i = 0; i < 10; ++i) {
+        writeInt<uint32_t>(i, buf);
+    }
+
+    // Use the input buffer to create custom option.
+    boost::scoped_ptr<OptionCustom> option;
+    ASSERT_NO_THROW(
+        option.reset(new OptionCustom(opt_def, Option::V6, buf));
+    );
+    ASSERT_TRUE(option);
+
+    // We expect that there are 10 uint32_t values stored in
+    // the option. The 10th element is accessed by index eq 9.
+    // Check that 9 is accepted.
+    EXPECT_NO_THROW(option->readInteger<uint32_t>(9));
+
+    // Check that index value beyond 9 is not accepted.
+    EXPECT_THROW(option->readInteger<uint32_t>(10), isc::OutOfRange);
+    EXPECT_THROW(option->readInteger<uint32_t>(11), isc::OutOfRange);
+}
+
+
+
+} // anonymous namespace
diff --git a/src/lib/dhcp/tests/option_definition_unittest.cc b/src/lib/dhcp/tests/option_definition_unittest.cc
index 9fa39c4..20c87d6 100644
--- a/src/lib/dhcp/tests/option_definition_unittest.cc
+++ b/src/lib/dhcp/tests/option_definition_unittest.cc
@@ -458,56 +458,6 @@ TEST_F(OptionDefinitionTest, binary) {
                            buf.begin()));
 }
 
-// The purpose of this test is to verify that definition can be
-// creates for the option that holds binary data and that the
-// binary data can be specified in 'comma separated values'
-// format.
-TEST_F(OptionDefinitionTest, binaryTokenized) {
-    OptionDefinition opt_def("OPTION_FOO", 1000, "binary", true);
-
-    // Prepare some dummy data (serverid): 0, 1, 2 etc.
-    OptionBuffer buf(16);
-    for (int i = 0; i < buf.size(); ++i) {
-        buf[i] = i;
-    }
-    std::vector<std::string> hex_data;
-    hex_data.push_back("00010203");
-    hex_data.push_back("04050607");
-    hex_data.push_back("08090A0B0C0D0E0F");
-
-    // Create option instance with the factory function.
-    // If the OptionDefinition code works properly than
-    // object of the type Option should be returned.
-    OptionPtr option_v6;
-    ASSERT_NO_THROW(
-        option_v6 = opt_def.optionFactory(Option::V6, 1000, hex_data);
-    );
-    // Expect base option type returned.
-    ASSERT_TRUE(typeid(*option_v6) == typeid(Option));
-    // Sanity check on universe, length and size. These are
-    // the basic parameters identifying any option.
-    EXPECT_EQ(Option::V6, option_v6->getUniverse());
-    EXPECT_EQ(4, option_v6->getHeaderLen());
-    ASSERT_EQ(buf.size(), option_v6->getData().size());
-
-    // Get data from the option and compare against reference buffer.
-    // They are expected to match.
-    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
-                           option_v6->getData().end(),
-                           buf.begin()));
-
-    // Repeat the same test scenario for DHCPv4 option.
-    OptionPtr option_v4;
-    ASSERT_NO_THROW(option_v4 = opt_def.optionFactory(Option::V4, 214, hex_data));
-    EXPECT_EQ(Option::V4, option_v4->getUniverse());
-    EXPECT_EQ(2, option_v4->getHeaderLen());
-    ASSERT_EQ(buf.size(), option_v4->getData().size());
-
-    EXPECT_TRUE(std::equal(option_v6->getData().begin(),
-                           option_v6->getData().end(),
-                           buf.begin()));
-}
-
 // The purpose of this test is to verify that definition can be created
 // for option that comprises record of data. In this particular test
 // the IA_NA option is used. This option comprises three uint32 fields.
@@ -652,11 +602,6 @@ TEST_F(OptionDefinitionTest, uint8Tokenized) {
     std::vector<std::string> values;
     values.push_back("123");
     values.push_back("456");
-    try {
-        option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
-    } catch (std::exception& ex) {
-        std::cout << ex.what() << std::endl;
-    }
     ASSERT_NO_THROW(
         option_v6 = opt_def.optionFactory(Option::V6, D6O_PREFERENCE, values);
     );



More information about the bind10-changes mailing list