// 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 #include #include #include #include #include #include #include using namespace std; using namespace isc::util; namespace isc { namespace dhcp { OptionDefinition::DataTypeUtil::DataTypeUtil() { data_types_["empty"] = EMPTY_TYPE; data_types_["binary"] = BINARY_TYPE; data_types_["boolean"] = BOOLEAN_TYPE; data_types_["int8"] = INT8_TYPE; data_types_["int16"] = INT16_TYPE; data_types_["int32"] = INT32_TYPE; data_types_["uint8"] = UINT8_TYPE; data_types_["uint16"] = UINT16_TYPE; data_types_["uint32"] = UINT32_TYPE; data_types_["ipv4-address"] = IPV4_ADDRESS_TYPE; data_types_["ipv6-address"] = IPV6_ADDRESS_TYPE; data_types_["string"] = STRING_TYPE; data_types_["fqdn"] = FQDN_TYPE; data_types_["record"] = RECORD_TYPE; } OptionDefinition::DataType OptionDefinition::DataTypeUtil::getDataType(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 UNKNOWN_TYPE; } OptionDefinition::OptionDefinition(const std::string& name, const uint16_t code, const std::string& type, const bool array_type /* = false */) : name_(name), code_(code), type_(UNKNOWN_TYPE), array_type_(array_type) { // 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().getDataType(type); } OptionDefinition::OptionDefinition(const std::string& name, const uint16_t code, const DataType type, const bool array_type /* = false */) : name_(name), code_(code), type_(type), array_type_(array_type) { } void OptionDefinition::addRecordField(const std::string& data_type_name) { DataType data_type = DataTypeUtil::instance().getDataType(data_type_name); addRecordField(data_type); } void OptionDefinition::addRecordField(const DataType data_type) { if (type_ != RECORD_TYPE) { isc_throw(isc::InvalidOperation, "'record' option type must be used" " to add data fields to the record"); } if (data_type >= UNKNOWN_TYPE) { isc_throw(isc::BadValue, "attempted to add invalid data type to the record"); } record_fields_.push_back(data_type); } Option::Factory* OptionDefinition::getFactory() const { validate(); // @todo This function must be extended to return more factory // functions that create instances of more specialized options. // This requires us to first implement those more specialized // options that will be derived from Option class. if (type_ == BINARY_TYPE) { return (factoryGeneric); } else if (type_ == IPV6_ADDRESS_TYPE && array_type_) { return (factoryAddrList6); } else if (type_ == IPV4_ADDRESS_TYPE && array_type_) { return (factoryAddrList4); } else if (type_ == EMPTY_TYPE) { return (factoryEmpty); } else if (code_ == D6O_IA_NA && haveIA6Format()) { return (factoryIA6); } else if (code_ == D6O_IAADDR && haveIAAddr6Format()) { return (factoryIAAddr6); } else if (type_ == UINT8_TYPE) { if (array_type_) { return (factoryGeneric); } else { return (factoryInteger); } } else if (type_ == UINT16_TYPE) { if (array_type_) { return (factoryIntegerArray); } else { return (factoryInteger); } } else if (type_ == UINT32_TYPE) { if (array_type_) { return (factoryIntegerArray); } else { return (factoryInteger); } } // Factory generic returns instance of Option class. However, once we // implement CustomOption class we may want to return factory function // that will create instance of CustomOption rather than Option. // CustomOption will allow to access particular data fields within the // option rather than raw data buffer. return (factoryGeneric); } void OptionDefinition::sanityCheckUniverse(const Option::Universe expected_universe, const Option::Universe actual_universe) { if (expected_universe != actual_universe) { isc_throw(isc::BadValue, "invalid universe specified for the option"); } } void OptionDefinition::validate() const { // Option name must not be empty. if (name_.empty()) { isc_throw(isc::BadValue, "option name must not be empty"); } // Option name must not contain spaces. if (name_.find(" ") != string::npos) { isc_throw(isc::BadValue, "option name must not contain spaces"); } // Unsupported option types are not allowed. if (type_ >= UNKNOWN_TYPE) { isc_throw(isc::OutOfRange, "option type value " << type_ << " is out of range"); } } bool OptionDefinition::haveIAx6Format(OptionDefinition::DataType first_type) const { return (haveType(RECORD_TYPE) && record_fields_.size() == 3 && record_fields_[0] == first_type && record_fields_[1] == UINT32_TYPE && record_fields_[2] == UINT32_TYPE); } bool OptionDefinition::haveIA6Format() const { // Expect that IA_NA option format is defined as record. // Although it consists of 3 elements of the same (uint32) // type it can't be defined as array of uint32 elements because // arrays do not impose limitations on number of elements in // the array while this limitation is needed for IA_NA - need // exactly 3 elements. return (haveIAx6Format(UINT32_TYPE)); } bool OptionDefinition::haveIAAddr6Format() const { return (haveIAx6Format(IPV6_ADDRESS_TYPE)); } OptionPtr OptionDefinition::factoryAddrList4(Option::Universe u, uint16_t type, const OptionBuffer& buf) { sanityCheckUniverse(u, Option::V4); boost::shared_ptr option(new Option4AddrLst(type, buf.begin(), buf.begin() + buf.size())); return (option); } OptionPtr OptionDefinition::factoryAddrList6(Option::Universe u, uint16_t type, const OptionBuffer& buf) { sanityCheckUniverse(u, Option::V6); boost::shared_ptr option(new Option6AddrLst(type, buf.begin(), buf.begin() + buf.size())); return (option); } OptionPtr OptionDefinition::factoryEmpty(Option::Universe u, uint16_t type, const OptionBuffer& buf) { if (buf.size() > 0) { isc_throw(isc::BadValue, "input option buffer must be empty" " when creating empty option instance"); } OptionPtr option(new Option(u, type)); return (option); } OptionPtr OptionDefinition::factoryGeneric(Option::Universe u, uint16_t type, const OptionBuffer& buf) { OptionPtr option(new Option(u, type, buf)); return (option); } OptionPtr OptionDefinition::factoryIA6(Option::Universe u, uint16_t type, const OptionBuffer& buf) { sanityCheckUniverse(u, Option::V6); if (buf.size() != Option6IA::OPTION6_IA_LEN) { isc_throw(isc::OutOfRange, "input option buffer has invalid size, expeted " << Option6IA::OPTION6_IA_LEN << " bytes"); } boost::shared_ptr option(new Option6IA(type, buf.begin(), buf.begin() + buf.size())); return (option); } OptionPtr OptionDefinition::factoryIAAddr6(Option::Universe u, uint16_t type, const OptionBuffer& buf) { sanityCheckUniverse(u, Option::V6); if (buf.size() != Option6IAAddr::OPTION6_IAADDR_LEN) { isc_throw(isc::OutOfRange, "input option buffer has invalid size, expeted " << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes"); } boost::shared_ptr option(new Option6IAAddr(type, buf.begin(), buf.begin() + buf.size())); return (option); } } // end of isc::dhcp namespace } // end of isc namespace