From: Marcin Siodelski Date: Tue, 27 Nov 2012 20:53:54 +0000 (+0100) Subject: [2312] Implemented OptionCustom::toText() function. X-Git-Tag: bind10-1.0.0-beta-release~46^2~18^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ff2167388ff8a86dfdae23fbd147602b102b0319;p=thirdparty%2Fkea.git [2312] Implemented OptionCustom::toText() function. --- diff --git a/src/lib/dhcp/option_custom.cc b/src/lib/dhcp/option_custom.cc index 66975871a5..9f9e282968 100644 --- a/src/lib/dhcp/option_custom.cc +++ b/src/lib/dhcp/option_custom.cc @@ -242,20 +242,30 @@ OptionCustom::len() { return (length); } -std::string OptionCustom::toText(int /* =0 */ ) { +std::string OptionCustom::toText(int indent /* = 0 */ ) { std::stringstream tmp; - /* for (int i = 0; i < indent; i++) + for (int i = 0; i < indent; ++i) tmp << " "; - tmp << "type=" << type_ << ", len=" << len()-getHeaderLen() << ": "; + tmp << "type=" << type_ << ", len=" << len()-getHeaderLen() + << ", data fields:" << std::endl; - for (unsigned int i = 0; i < data_.size(); i++) { - if (i) { - tmp << ":"; + OptionDataType data_type = definition_.getType(); + for (unsigned int i = 0; i < getDataFieldsNum(); ++i) { + if (data_type == OPT_RECORD_TYPE) { + const OptionDefinition::RecordFieldsCollection& fields = + definition_.getRecordFields(); + + for (OptionDefinition::RecordFieldsConstIter field = fields.begin(); + field != fields.end(); ++field) { + for (int j = 0; j < indent + 2; ++j) { + tmp << " "; + } + tmp << OptionDataTypeUtil::getDataTypeName(*field) + << ", value = " << "here is a value" << std::endl; + } } - tmp << setfill('0') << setw(2) << hex - << static_cast(data_[i]); } // print suboptions @@ -263,7 +273,7 @@ std::string OptionCustom::toText(int /* =0 */ ) { opt != options_.end(); ++opt) { tmp << (*opt).second->toText(indent+2); - } */ + } return tmp.str(); } diff --git a/src/lib/dhcp/option_data_types.cc b/src/lib/dhcp/option_data_types.cc index 146b1097a9..7f67d6b5bc 100644 --- a/src/lib/dhcp/option_data_types.cc +++ b/src/lib/dhcp/option_data_types.cc @@ -18,6 +18,54 @@ 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] = "ipv4-address"; + data_type_names_[OPT_STRING_TYPE] = "string"; + data_type_names_[OPT_FQDN_TYPE] = "fqdn"; + data_type_names_[OPT_RECORD_TYPE] = "record"; + 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::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) { @@ -41,6 +89,27 @@ OptionDataTypeUtil::getDataTypeLen(const OptionDataType data_type) { 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::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); +} + void OptionDataTypeUtil::readAddress(const std::vector& buf, const short family, diff --git a/src/lib/dhcp/option_data_types.h b/src/lib/dhcp/option_data_types.h index d227749a41..f5efa0f301 100644 --- a/src/lib/dhcp/option_data_types.h +++ b/src/lib/dhcp/option_data_types.h @@ -174,10 +174,22 @@ struct OptionDataTypeTraits { static const OptionDataType type = OPT_STRING_TYPE; }; -/// @brief Utility class to write/read data to/from a buffer. +/// @brief Utility class for option data types. 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 functionm returs the size of a particular data type. @@ -312,7 +324,43 @@ public: /// @param [out] buf output buffer. static void writeString(const std::string& value, std::vector& buf); +private: + + /// The container holding mapping of data type names to + /// data types enumerator. + std::map data_types_; + + /// The container holding mapping of data types to data + /// type names. + std::map 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; }; diff --git a/src/lib/dhcp/option_definition.cc b/src/lib/dhcp/option_definition.cc index 2b4fddb2d8..b61a4ea7f5 100644 --- a/src/lib/dhcp/option_definition.cc +++ b/src/lib/dhcp/option_definition.cc @@ -28,150 +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::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 -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::integer_type && - OptionDataTypeTraits::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(value_str); - } catch (const boost::bad_lexical_cast& ex) { - // Prepare error message here. - std::string data_type_str = "boolean"; - if (OptionDataTypeTraits::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::integer_type) { - if (result > numeric_limits::max() || - result < numeric_limits::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::min() << ".." << numeric_limits::max()); - } - } - return (static_cast(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: - 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(value), buf); - return; - case OPT_INT8_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_INT16_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_INT32_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_UINT8_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_UINT16_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_UINT32_TYPE: - OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), - buf); - return; - case OPT_IPV4_ADDRESS_TYPE: - case OPT_IPV6_ADDRESS_TYPE: - { - asiolink::IOAddress address(value); - if (!address.getAddress().is_v4() && - !address.getAddress().is_v6()) { - 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); - -} OptionDefinition::OptionDefinition(const std::string& name, const uint16_t code, @@ -184,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, @@ -199,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); } @@ -291,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(); @@ -304,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())); @@ -410,6 +266,124 @@ OptionDefinition::haveIAAddr6Format() const { return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE)); } +template +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::integer_type && + OptionDataTypeTraits::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(value_str); + } catch (const boost::bad_lexical_cast& ex) { + // Prepare error message here. + std::string data_type_str = "boolean"; + if (OptionDataTypeTraits::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::integer_type) { + if (result > numeric_limits::max() || + result < numeric_limits::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::min() << ".." << numeric_limits::max()); + } + } + return (static_cast(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(value), buf); + return; + case OPT_INT8_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_INT16_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_INT32_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_UINT8_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_UINT16_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_UINT32_TYPE: + OptionDataTypeUtil::writeInt(lexicalCastWithRangeCheck(value), + buf); + return; + case OPT_IPV4_ADDRESS_TYPE: + case OPT_IPV6_ADDRESS_TYPE: + { + asiolink::IOAddress address(value); + if (!address.getAddress().is_v4() && + !address.getAddress().is_v6()) { + 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 18f79d27ae..3d48ef2bb0 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::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 - 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 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 + 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.