Browse Source

[3036] Return instance of Option6ClientFqdn for option 39.

Also folded long lines in OptionDefinition implementation.
Marcin Siodelski 11 years ago
parent
commit
d803eeaf3f

+ 76 - 37
src/lib/dhcp/option_definition.cc

@@ -17,6 +17,7 @@
 #include <dhcp/option6_addrlst.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
+#include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option_custom.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_int.h>
@@ -101,7 +102,8 @@ OptionDefinition::addRecordField(const OptionDataType data_type) {
     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.");
+        isc_throw(isc::BadValue,
+                  "attempted to add invalid data type to the record.");
     }
     record_fields_.push_back(data_type);
 }
@@ -127,19 +129,23 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                     factoryInteger<int8_t>(u, type, begin, end));
 
         case OPT_UINT16_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
+            return (array_type_ ?
+                    factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<uint16_t>(u, type, begin, end));
 
         case OPT_INT16_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint16_t>(u, type, begin, end) :
+            return (array_type_ ?
+                    factoryIntegerArray<uint16_t>(u, type, begin, end) :
                     factoryInteger<int16_t>(u, type, begin, end));
 
         case OPT_UINT32_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
+            return (array_type_ ?
+                    factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<uint32_t>(u, type, begin, end));
 
         case OPT_INT32_TYPE:
-            return (array_type_ ? factoryIntegerArray<uint32_t>(u, type, begin, end) :
+            return (array_type_ ?
+                    factoryIntegerArray<uint32_t>(u, type, begin, end) :
                     factoryInteger<int32_t>(u, type, begin, end));
 
         case OPT_IPV4_ADDRESS_TYPE:
@@ -183,6 +189,10 @@ OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                     // option only for the same reasons as described in
                     // for IA_NA and IA_PD above.
                     return (factoryIAAddr6(type, begin, end));
+                } else if (code_ == D6O_CLIENT_FQDN && haveClientFqdnFormat()) {
+                    // FQDN option requires special processing. Thus, there is
+                    // a specialized class to handle it.
+                    return (OptionPtr(new Option6ClientFqdn(begin, end)));
                 }
             }
         }
@@ -264,10 +274,12 @@ OptionDefinition::validate() const {
             // 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.";
+            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.";
+            err_str << "array of empty value is not"
+                    << " a valid option definition.";
 
         }
 
@@ -275,33 +287,34 @@ OptionDefinition::validate() const {
         // At least two data fields should be added to the record. Otherwise
         // non-record option definition could be used.
         if (getRecordFields().size() < 2) {
-            err_str << "invalid number of data fields: " << getRecordFields().size()
+            err_str << "invalid number of data fields: "
+                    << getRecordFields().size()
                     << " specified for the option of type 'record'. Expected at"
                     << " 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 or binary data fields are not
-            // laid before other fields. But we allow that they are laid at the end of
-            // an option.
+            // 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();
                  it != fields.end(); ++it) {
                 if (*it == OPT_STRING_TYPE &&
                     it < fields.end() - 1) {
-                    err_str << "string data field can't be laid before data fields"
-                            << " of other types.";
+                    err_str << "string data field can't be laid before data"
+                            << " fields 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.";
+                    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.";
+                    err_str << "empty data type can't be stored as a field in"
+                            << " an option record.";
                     break;
                 }
             }
@@ -341,13 +354,24 @@ OptionDefinition::haveIAAddr6Format() const {
     return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE));
 }
 
+bool
+OptionDefinition::haveClientFqdnFormat() const {
+    return (haveType(OPT_RECORD_TYPE) &&
+            (record_fields_.size() == 2) &&
+            (record_fields_[0] == OPT_UINT8_TYPE) &&
+            (record_fields_[1] == OPT_FQDN_TYPE));
+}
+
 template<typename T>
-T OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) const {
+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"
+        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
@@ -364,16 +388,18 @@ T OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str) cons
         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());
+        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());
+                      << 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));
@@ -395,30 +421,37 @@ OptionDefinition::writeToBuffer(const std::string& value,
         // 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);
+        OptionDataTypeUtil::writeBool(lexicalCastWithRangeCheck<bool>(value),
+                                      buf);
         return;
     case OPT_INT8_TYPE:
-        OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<int8_t>(value),
+        OptionDataTypeUtil::writeInt<uint8_t>
+            (lexicalCastWithRangeCheck<int8_t>(value),
                                               buf);
         return;
     case OPT_INT16_TYPE:
-        OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<int16_t>(value),
+        OptionDataTypeUtil::writeInt<uint16_t>
+            (lexicalCastWithRangeCheck<int16_t>(value),
                                                buf);
         return;
     case OPT_INT32_TYPE:
-        OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<int32_t>(value),
+        OptionDataTypeUtil::writeInt<uint32_t>
+            (lexicalCastWithRangeCheck<int32_t>(value),
                                                buf);
         return;
     case OPT_UINT8_TYPE:
-        OptionDataTypeUtil::writeInt<uint8_t>(lexicalCastWithRangeCheck<uint8_t>(value),
+        OptionDataTypeUtil::writeInt<uint8_t>
+            (lexicalCastWithRangeCheck<uint8_t>(value),
                                               buf);
         return;
     case OPT_UINT16_TYPE:
-        OptionDataTypeUtil::writeInt<uint16_t>(lexicalCastWithRangeCheck<uint16_t>(value),
+        OptionDataTypeUtil::writeInt<uint16_t>
+            (lexicalCastWithRangeCheck<uint16_t>(value),
                                                buf);
         return;
     case OPT_UINT32_TYPE:
-        OptionDataTypeUtil::writeInt<uint32_t>(lexicalCastWithRangeCheck<uint32_t>(value),
+        OptionDataTypeUtil::writeInt<uint32_t>
+            (lexicalCastWithRangeCheck<uint32_t>(value),
                                                buf);
         return;
     case OPT_IPV4_ADDRESS_TYPE:
@@ -426,7 +459,8 @@ OptionDefinition::writeToBuffer(const std::string& value,
         {
             asiolink::IOAddress address(value);
             if (!address.isV4() && !address.isV6()) {
-                isc_throw(BadDataTypeCast, "provided address " << address.toText()
+                isc_throw(BadDataTypeCast, "provided address "
+                          << address.toText()
                           << " is not a valid IPv4 or IPv6 address.");
             }
             OptionDataTypeUtil::writeAddress(address, buf);
@@ -454,7 +488,8 @@ OptionPtr
 OptionDefinition::factoryAddrList4(uint16_t type,
                                   OptionBufferConstIter begin,
                                   OptionBufferConstIter end) {
-    boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin, end));
+    boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin,
+                                                                end));
     return (option);
 }
 
@@ -462,7 +497,8 @@ OptionPtr
 OptionDefinition::factoryAddrList6(uint16_t type,
                                    OptionBufferConstIter begin,
                                    OptionBufferConstIter end) {
-    boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin, end));
+    boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin,
+                                                                end));
     return (option);
 }
 
@@ -486,8 +522,9 @@ OptionDefinition::factoryIA6(uint16_t type,
                              OptionBufferConstIter begin,
                              OptionBufferConstIter end) {
     if (std::distance(begin, end) < Option6IA::OPTION6_IA_LEN) {
-        isc_throw(isc::OutOfRange, "input option buffer has invalid size, expected "
-                  "at least " << Option6IA::OPTION6_IA_LEN << " bytes");
+        isc_throw(isc::OutOfRange, "input option buffer has invalid size,"
+                  << " expected at least " << Option6IA::OPTION6_IA_LEN
+                  << " bytes");
     }
     boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
     return (option);
@@ -498,10 +535,12 @@ OptionDefinition::factoryIAAddr6(uint16_t type,
                                  OptionBufferConstIter begin,
                                  OptionBufferConstIter end) {
     if (std::distance(begin, end) < Option6IAAddr::OPTION6_IAADDR_LEN) {
-        isc_throw(isc::OutOfRange, "input option buffer has invalid size, expected "
-                  " at least " << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
+        isc_throw(isc::OutOfRange,
+                  "input option buffer has invalid size, expected at least "
+                  << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
     }
-    boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin, end));
+    boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin,
+                                                              end));
     return (option);
 }
 

+ 6 - 0
src/lib/dhcp/option_definition.h

@@ -275,6 +275,12 @@ public:
     /// @return true if specified format is IAADDR option format.
     bool haveIAAddr6Format() const;
 
+    /// @brief Check if specified format is OPTION_CLIENT_FQDN option format.
+    ///
+    /// @return true of specified format is OPTION_CLIENT_FQDN option format,
+    /// false otherwise.
+    bool haveClientFqdnFormat() const;
+
     /// @brief Option factory.
     ///
     /// This function creates an instance of DHCP option using

+ 3 - 1
src/lib/dhcp/tests/libdhcp++_unittest.cc

@@ -19,6 +19,7 @@
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option4_addrlst.h>
 #include <dhcp/option6_addrlst.h>
+#include <dhcp/option6_client_fqdn.h>
 #include <dhcp/option6_ia.h>
 #include <dhcp/option6_iaaddr.h>
 #include <dhcp/option_custom.h>
@@ -905,7 +906,8 @@ TEST_F(LibDhcpTest, stdOptionDefs6) {
                                     typeid(Option));
 
     LibDhcpTest::testStdOptionDefs6(D6O_CLIENT_FQDN, client_fqdn_buf.begin(),
-                                    client_fqdn_buf.end(), typeid(OptionCustom));
+                                    client_fqdn_buf.end(),
+                                    typeid(Option6ClientFqdn));
 
     LibDhcpTest::testStdOptionDefs6(D6O_PANA_AGENT, begin, end,
                                     typeid(Option6AddrLst));

+ 17 - 2
src/lib/dhcp/tests/option_definition_unittest.cc

@@ -928,7 +928,7 @@ TEST_F(OptionDefinitionTest, utf8StringTokenized) {
     // Let's create some dummy option.
     const uint16_t opt_code = 80;
     OptionDefinition opt_def("OPTION_WITH_STRING", opt_code, "string");
-    
+
     std::vector<std::string> values;
     values.push_back("Hello World");
     values.push_back("this string should not be included in the option");
@@ -960,7 +960,7 @@ TEST_F(OptionDefinitionTest, integerInvalidType) {
 // The purpose of this test is to verify that helper methods
 // haveIA6Format and haveIAAddr6Format can be used to determine
 // IA_NA  and IAADDR option formats.
-TEST_F(OptionDefinitionTest, recognizeFormat) {
+TEST_F(OptionDefinitionTest, haveIAFormat) {
     // IA_NA option format.
     OptionDefinition opt_def1("OPTION_IA_NA", D6O_IA_NA, "record");
     for (int i = 0; i < 3; ++i) {
@@ -984,4 +984,19 @@ TEST_F(OptionDefinitionTest, recognizeFormat) {
     EXPECT_FALSE(opt_def4.haveIAAddr6Format());
 }
 
+// This test verifies that haveClientFqdnFormat function recognizes that option
+// definition describes the format of DHCPv6 Client Fqdn Option Format.
+TEST_F(OptionDefinitionTest, haveClientFqdnFormat) {
+    OptionDefinition opt_def("OPTION_CLIENT_FQDN", D6O_CLIENT_FQDN, "record");
+    opt_def.addRecordField("uint8");
+    opt_def.addRecordField("fqdn");
+    EXPECT_TRUE(opt_def.haveClientFqdnFormat());
+
+    // Create option format which is not matching the Client FQDN option format
+    // to verify that tested function does dont always return true.
+    OptionDefinition opt_def_invalid("OPTION_CLIENT_FQDN", D6O_CLIENT_FQDN,
+                                     "uint8");
+    EXPECT_FALSE(opt_def_invalid.haveClientFqdnFormat());
+}
+
 } // anonymous namespace