Browse Source

[4070] Began tuple code

Francis Dupont 8 years ago
parent
commit
60feddc3b5

File diff suppressed because it is too large
+ 1 - 0
doc/guide/dhcp4-srv.xml


+ 1 - 1
doc/guide/dhcp6-srv.xml

@@ -1173,7 +1173,7 @@ temporarily override a list of interface names and listen on all interfaces.
 <row><entry>lq-relay-data</entry><entry>47</entry><entry>record (ipv6-address, binary)</entry><entry>false</entry></row>
 <row><entry>lq-client-link</entry><entry>48</entry><entry>ipv6-address</entry><entry>true</entry></row>
 <row><entry>bootfile-url</entry><entry>59</entry><entry>string</entry><entry>false</entry></row>
-<row><entry>bootfile-param</entry><entry>60</entry><entry>binary</entry><entry>false</entry></row>
+<row><entry>bootfile-param</entry><entry>60</entry><entry>tuple</entry><entry>true</entry></row>
 <row><entry>client-arch-type</entry><entry>61</entry><entry>uint16</entry><entry>true</entry></row>
 <row><entry>nii</entry><entry>62</entry><entry>record (uint8, uint8, uint8)</entry><entry>false</entry></row>
 <row><entry>aftr-name</entry><entry>64</entry><entry>fqdn</entry><entry>false</entry></row>

+ 72 - 2
src/lib/dhcp/option_custom.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -65,6 +65,17 @@ OptionCustom::addArrayDataField(const IOAddress& address) {
 }
 
 void
+OptionCustom::addArrayDataField(const std::string& value) {
+    checkArrayType();
+
+    OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
+        OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
+    OptionBuffer buf;
+    OptionDataTypeUtil::writeTuple(value, lft, buf);
+    buffers_.push_back(buf);
+}
+
+void
 OptionCustom::addArrayDataField(const bool value) {
     checkArrayType();
 
@@ -244,7 +255,7 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
                     // that the validate() function in OptionDefinition object
                     // should have checked wheter it is a case for this option.
                     data_size = std::distance(data, data_buf.end());
-                } else if (*field == OPT_IPV6_PREFIX_TYPE ) {
+                } else if (*field == OPT_IPV6_PREFIX_TYPE) {
                     // The size of the IPV6 prefix type is determined as
                     // one byte (which is the size of the prefix in bits)
                     // followed by the prefix bits (right-padded with
@@ -252,6 +263,18 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
                     if (std::distance(data, data_buf.end()) > 0) {
                         data_size = static_cast<size_t>(sizeof(uint8_t) + (*data + 7) / 8);
                     }
+                } else if (*field == OPT_TUPLE_TYPE) {
+                    OpaqueDataTuple::LengthFieldType lft =
+                        getUniverse() == Option::V4 ?
+                        OpaqueDataTuple::LENGTH_1_BYTE :
+                        OpaqueDataTuple::LENGTH_2_BYTES;
+                    std::string value =
+                        OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
+                                                      lft);
+                    data_size = value.size();
+                    // The size of the buffer holding a tuple is always
+                    // 1 or 2 byte larger than the size of the string
+                    data_size += getUniverse() == Option::V4 ? 1 : 2;
                 } else {
                     // If we reached the end of buffer we assume that this option is
                     // truncated because there is no remaining data to initialize
@@ -314,6 +337,19 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
                     // Data size comprises 1 byte holding a prefix length and the
                     // prefix length (in bytes) rounded to the nearest byte boundary.
                     data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
+                } else if (data_type == OPT_TUPLE_TYPE) {
+                    OpaqueDataTuple::LengthFieldType lft =
+                        getUniverse() == Option::V4 ?
+                        OpaqueDataTuple::LENGTH_1_BYTE :
+                        OpaqueDataTuple::LENGTH_2_BYTES;
+                    std::string value =
+                        OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
+                                                      lft);
+                    data_size = value.size();
+                    // The size of the buffer holding a tuple is always
+                    // 1 or 2 byte larger than the size of the string
+                    data_size += getUniverse() == Option::V4 ? 1 : 2;
+
                 }
                 // We don't perform other checks for data types that can't be
                 // used together with array indicator such as strings, empty field
@@ -351,6 +387,19 @@ OptionCustom::createBuffers(const OptionBuffer& data_buf) {
                         data_size = static_cast<size_t>
                             (sizeof(uint8_t) + (data_buf[0] + 7) / 8);
                     }
+                } else if (data_type == OPT_TUPLE_TYPE) {
+                    OpaqueDataTuple::LengthFieldType lft =
+                        getUniverse() == Option::V4 ?
+                        OpaqueDataTuple::LENGTH_1_BYTE :
+                        OpaqueDataTuple::LENGTH_2_BYTES;
+                    std::string value =
+                        OptionDataTypeUtil::readTuple(OptionBuffer(data, data_buf.end()),
+                                                      lft);
+                    data_size = value.size();
+                    // The size of the buffer holding a tuple is always
+                    // 1 or 2 byte larger than the size of the string
+                    data_size += getUniverse() == Option::V4 ? 1 : 2;
+
                 } else {
                     data_size = std::distance(data, data_buf.end());
                 }
@@ -415,6 +464,9 @@ OptionCustom::dataFieldToText(const OptionDataType data_type,
     case OPT_FQDN_TYPE:
         text << "\"" << readFqdn(index) << "\"";
         break;
+    case OPT_TUPLE_TYPE:
+        text << "\"" << readTuple(index) << "\"";
+        break;
     case OPT_STRING_TYPE:
         text << "\"" << readString(index) << "\"";
         break;
@@ -499,6 +551,24 @@ OptionCustom::writeBinary(const OptionBuffer& buf,
     buffers_[index] = buf;
 }
 
+std::string
+OptionCustom::readTuple(const uint32_t index) const {
+    checkIndex(index);
+    OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
+        OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
+    return (OptionDataTypeUtil::readTuple(buffers_[index], lft));
+}
+
+void
+OptionCustom::writeTuple(const std::string& value, const uint32_t index) {
+    checkIndex(index);
+
+    buffers_[index].clear();
+    OpaqueDataTuple::LengthFieldType lft = getUniverse() == Option::V4 ?
+        OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
+    OptionDataTypeUtil::writeTuple(value, lft, buffers_[index]);
+}
+
 bool
 OptionCustom::readBoolean(const uint32_t index) const {
     checkIndex(index);

+ 20 - 1
src/lib/dhcp/option_custom.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -113,6 +113,11 @@ public:
         buffers_.push_back(buf);
     }
 
+    /// @brief Create new buffer and store tuple value in it
+    ///
+    /// @param value value to be stored as a tuple in the created buffer.
+    void addArrayDataField(const std::string& value);
+
     /// @brief Create new buffer and store variable length prefix in it.
     ///
     /// @param prefix_len Prefix length.
@@ -163,6 +168,20 @@ public:
     /// @param index buffer index.
     void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);
 
+    /// @brief Read a buffer as length and string tuple.
+    ///
+    /// @param index buffer index.
+    ///
+    /// @throw isc::OutOfRange if index is out of range.
+    /// @return string read from a buffer.
+    std::string readTuple(const uint32_t index = 0) const;
+
+    /// @brief Write a length and string tuple into a buffer.
+    ///
+    /// @param value value to be written.
+    /// @param index buffer index.
+    void writeTuple(const std::string& value, const uint32_t index = 0);
+
     /// @brief Read a buffer as boolean value.
     ///
     /// @param index buffer index.

+ 75 - 1
src/lib/dhcp/option_data_types.cc

@@ -9,6 +9,7 @@
 #include <dns/name.h>
 #include <util/encode/hex.h>
 #include <algorithm>
+#include <limits>
 
 using namespace isc::asiolink;
 
@@ -30,6 +31,7 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
     data_types_["ipv6-prefix"] = OPT_IPV6_PREFIX_TYPE;
     data_types_["psid"] = OPT_PSID_TYPE;
     data_types_["string"] = OPT_STRING_TYPE;
+    data_types_["tuple"] = OPT_TUPLE_TYPE;
     data_types_["fqdn"] = OPT_FQDN_TYPE;
     data_types_["record"] = OPT_RECORD_TYPE;
 
@@ -47,6 +49,7 @@ OptionDataTypeUtil::OptionDataTypeUtil() {
     data_type_names_[OPT_IPV6_PREFIX_TYPE] = "ipv6-prefix";
     data_type_names_[OPT_PSID_TYPE] = "psid";
     data_type_names_[OPT_STRING_TYPE] = "string";
+    data_type_names_[OPT_TUPLE_TYPE] = "tuple";
     data_type_names_[OPT_FQDN_TYPE] = "fqdn";
     data_type_names_[OPT_RECORD_TYPE] = "record";
     // The "unknown" data type is declared here so as
@@ -141,7 +144,7 @@ OptionDataTypeUtil::readAddress(const std::vector<uint8_t>& buf,
         return (IOAddress::fromBytes(AF_INET6, &buf[0]));
     } else {
         isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
-                  "IP address. Invalid family: " << family);
+                  << " IP address. Invalid family: " << family);
     }
 }
 
@@ -170,6 +173,77 @@ OptionDataTypeUtil::writeBinary(const std::string& hex_str,
     buf.insert(buf.end(), binary.begin(), binary.end());
 }
 
+std::string
+OptionDataTypeUtil::readTuple(const std::vector<uint8_t>& buf,
+                              OpaqueDataTuple::LengthFieldType lengthfieldtype) {
+    if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
+        if (buf.size() < 1) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " tuple (length). Invalid buffer size: "
+                      << buf.size());
+        }
+        uint8_t len = buf[0];
+        if (buf.size() < 1 + len) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " tuple (length " << static_cast<unsigned>(len)
+                      << "). Invalid buffer size: " << buf.size());
+        }
+        std::string value;
+        value.resize(len);
+        std::memcpy(&value[0], &buf[1], len);
+        return (value);
+    } else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
+        if (buf.size() < 2) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " tuple (length). Invalid buffer size: "
+                      << buf.size());
+        }
+        uint16_t len = isc::util::readUint16(&buf[0], 2);
+        if (buf.size() < 2 + len) {
+            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                      << " tuple (length " << len
+                      << "). Invalid buffer size: " << buf.size());
+        }
+        std::string value;
+        value.resize(len);
+        std::memcpy(&value[0], &buf[2], len);
+        return (value);
+    } else {
+        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
+                  << " tuple. Invalid length type field: "
+                  << static_cast<unsigned>(lengthfieldtype));
+    }
+}
+
+void
+OptionDataTypeUtil:: writeTuple(const std::string& value,
+                                OpaqueDataTuple::LengthFieldType lengthfieldtype,
+                                std::vector<uint8_t>& buf) {
+    if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
+        if (value.size() > std::numeric_limits<uint8_t>::max()) {
+            isc_throw(BadDataTypeCast, "invalid tuple value (size "
+                      << value.size() << " larger than "
+                      << std::numeric_limits<uint8_t>::max() << ")");
+        }
+        buf.push_back(static_cast<uint8_t>(value.size()));
+
+    } else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
+        if (value.size() > std::numeric_limits<uint16_t>::max()) {
+            isc_throw(BadDataTypeCast, "invalid tuple value (size "
+                      << value.size() << " larger than "
+                      << std::numeric_limits<uint16_t>::max() << ")");
+        }
+        buf.resize(buf.size() + 2);
+        isc::util::writeUint16(static_cast<uint16_t>(value.size()),
+                               &buf[buf.size() - 2], 2);
+    } else {
+        isc_throw(BadDataTypeCast, "unable to write data to the buffer as"
+                  << " tuple. Invalid length type field: "
+                  << static_cast<unsigned>(lengthfieldtype));
+    }
+    buf.insert(buf.end(), value.begin(), value.end());
+}
+
 bool
 OptionDataTypeUtil::readBool(const std::vector<uint8_t>& buf) {
     if (buf.empty()) {

+ 22 - 1
src/lib/dhcp/option_data_types.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,7 @@
 #define OPTION_DATA_TYPES_H
 
 #include <asiolink/io_address.h>
+#include <dhcp/opaque_data_tuple.h>
 #include <dhcp/option.h>
 #include <exceptions/exceptions.h>
 #include <util/io_utilities.h>
@@ -57,6 +58,7 @@ enum OptionDataType {
     OPT_IPV6_PREFIX_TYPE,
     OPT_PSID_TYPE,
     OPT_STRING_TYPE,
+    OPT_TUPLE_TYPE,
     OPT_FQDN_TYPE,
     OPT_RECORD_TYPE,
     OPT_UNKNOWN_TYPE
@@ -382,6 +384,25 @@ public:
     static void writeBinary(const std::string& hex_str,
                             std::vector<uint8_t>& buf);
 
+    /// @brief Read length and string tuple from a buffer.
+    ///
+    /// @param buf input buffer.
+    /// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
+    /// @throw isc::dhcp::BadDataTypeCast when the data being read
+    /// is truncated.
+    /// @return string being read.
+    static std::string readTuple(const std::vector<uint8_t>& buf,
+                                 OpaqueDataTuple::LengthFieldType lengthfieldtype);
+
+    /// @brief Append length and string tuple to a buffer
+    ///
+    /// @param value length and string tuple
+    /// @param lengthfieldtype LENGTH_1_BYTE (DHCPv4) or LENGTH_2_BYTES (DHCPv6)
+    /// @param [out] buf output buffer.
+    static void writeTuple(const std::string& value,
+                           OpaqueDataTuple::LengthFieldType lengthfieldtype,
+                           std::vector<uint8_t>& buf);
+
     /// @brief Read boolean value from a buffer.
     ///
     /// @param buf input buffer.

+ 64 - 1
src/lib/dhcp/tests/option_data_types_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -192,6 +192,69 @@ TEST_F(OptionDataTypesTest, writeBinary) {
     EXPECT_TRUE(std::equal(buf_ref.begin(), buf_ref.end(), buf.begin()));
 }
 
+// The purpose of this test is to verify that the tuple value stored
+TEST_F(OptionDataTypesTest, readTuple) {
+    // The string
+    std::string value = "hello world";
+    // Create an input buffer.
+    std::vector<uint8_t> buf;
+    // DHCPv4 tuples use 1 byte length
+    writeInt<uint8_t>(static_cast<uint8_t>(value.size()), buf);
+    writeString(value, buf);
+
+    // Read the string from the buffer.
+    std::string result;
+    ASSERT_NO_THROW(
+        result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_1_BYTE);
+    );
+    // Check that it is valid.
+    EXPECT_EQ(value, result);
+
+    buf.clear();
+
+    // DHCPv6 tuples use 2 byte length
+    writeInt<uint16_t>(static_cast<uint16_t>(value.size()), buf);
+    writeString(value, buf);
+
+    // Read the string from the buffer.
+    ASSERT_NO_THROW(
+        result = OptionDataTypeUtil::readTuple(buf, OpaqueDataTuple::LENGTH_2_BYTES);
+    );
+    // Check that it is valid.
+    EXPECT_EQ(value, result);
+}
+
+// The purpose of this test is to verify that a tuple value
+// are correctly encoded in a buffer
+TEST_F(OptionDataTypesTest, writeTuple) {
+    // The string
+    std::string value = "hello world";
+    // Create an output buffer.
+    std::vector<uint8_t> buf;
+
+    // Encode it in DHCPv4
+    OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_1_BYTE, buf);
+
+    // Check that it is valid.
+    ASSERT_EQ(value.size() + 1, buf.size());
+    std::vector<uint8_t> expected;
+    writeInt<uint8_t>(static_cast<uint8_t>(value.size()), expected);
+    writeString(value, expected);
+    EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
+
+    buf.clear();
+
+    // Encode it in DHCPv6
+    OptionDataTypeUtil::writeTuple(value, OpaqueDataTuple::LENGTH_2_BYTES, buf);
+
+    // Check that it is valid.
+    ASSERT_EQ(value.size() + 2, buf.size());
+    expected.clear();
+    writeInt<uint16_t>(static_cast<uint16_t>(value.size()), expected);
+    writeString(value, expected);
+    EXPECT_EQ(0, std::memcmp(&buf[0], &expected[0], buf.size()));
+}
+
 // The purpose of this test is to verify that the boolean value stored
 // in a buffer is correctly read from this buffer.
 TEST_F(OptionDataTypesTest, readBool) {