diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
commit | f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch) | |
tree | 49e44c6f87febed37efb953ab5485aa49f6481a7 /src/lib/dhcp/option.cc | |
parent | Initial commit. (diff) | |
download | isc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.tar.xz isc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.zip |
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/dhcp/option.cc')
-rw-r--r-- | src/lib/dhcp/option.cc | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/src/lib/dhcp/option.cc b/src/lib/dhcp/option.cc new file mode 100644 index 0000000..95353ef --- /dev/null +++ b/src/lib/dhcp/option.cc @@ -0,0 +1,389 @@ +// Copyright (C) 2011-2023 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 +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <dhcp/dhcp4.h> +#include <dhcp/libdhcp++.h> +#include <dhcp/option.h> +#include <dhcp/option_space.h> +#include <exceptions/exceptions.h> +#include <util/encode/hex.h> +#include <util/io_utilities.h> + +#include <boost/make_shared.hpp> + +#include <iomanip> +#include <list> +#include <sstream> + +#include <arpa/inet.h> +#include <stdint.h> +#include <string.h> + +using namespace std; +using namespace isc::util; + +namespace isc { +namespace dhcp { + +OptionPtr +Option::factory(Option::Universe u, + uint16_t type, + const OptionBuffer& buf) { + return (LibDHCP::optionFactory(u, type, buf)); +} + +Option::Option(Universe u, uint16_t type) + : universe_(u), type_(type) { + check(); +} + +Option::Option(Universe u, uint16_t type, const OptionBuffer& data) + : universe_(u), type_(type), data_(data) { + check(); +} + +Option::Option(Universe u, uint16_t type, OptionBufferConstIter first, + OptionBufferConstIter last) + : universe_(u), type_(type), data_(first, last) { + check(); +} + +Option::Option(const Option& option) + : universe_(option.universe_), type_(option.type_), + data_(option.data_), options_(), + encapsulated_space_(option.encapsulated_space_) { + option.getOptionsCopy(options_); +} + +OptionPtr +Option::create(Universe u, uint16_t type) { + return (boost::make_shared<Option>(u, type)); +} + +OptionPtr +Option::create(Universe u, uint16_t type, const OptionBuffer& data) { + return (boost::make_shared<Option>(u, type, data)); +} + +Option& +Option::operator=(const Option& rhs) { + if (&rhs != this) { + universe_ = rhs.universe_; + type_ = rhs.type_; + data_ = rhs.data_; + rhs.getOptionsCopy(options_); + encapsulated_space_ = rhs.encapsulated_space_; + } + return (*this); +} + +OptionPtr +Option::clone() const { + return (cloneInternal<Option>()); +} + +void +Option::check() const { + if ((universe_ != V4) && (universe_ != V6)) { + isc_throw(BadValue, "Invalid universe type specified. " + << "Only V4 and V6 are allowed."); + } + + if (universe_ == V4) { + if (type_ > 255) { + isc_throw(OutOfRange, "DHCPv4 Option type " << type_ << " is too big. " + << "For DHCPv4 allowed type range is 0..255"); + } + } + + // no need to check anything for DHCPv6. It allows full range (0-64k) of + // both types and data size. +} + +void Option::pack(isc::util::OutputBuffer& buf, bool check) const { + // Write a header. + packHeader(buf, check); + // Write data. + if (!data_.empty()) { + buf.writeData(&data_[0], data_.size()); + } + // Write sub-options. + packOptions(buf, check); +} + +void +Option::packHeader(isc::util::OutputBuffer& buf, bool check) const { + if (universe_ == V4) { + if (check && len() > 255) { + isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big. " + << "At most 255 bytes are supported."); + } + + buf.writeUint8(type_); + buf.writeUint8(len() - getHeaderLen()); + + } else { + buf.writeUint16(type_); + buf.writeUint16(len() - getHeaderLen()); + } +} + +void +Option::packOptions(isc::util::OutputBuffer& buf, bool check) const { + switch (universe_) { + case V4: + LibDHCP::packOptions4(buf, options_, false, check); + return; + case V6: + LibDHCP::packOptions6(buf, options_); + return; + default: + isc_throw(isc::BadValue, "Invalid universe type " << universe_); + } +} + +void Option::unpack(OptionBufferConstIter begin, + OptionBufferConstIter end) { + setData(begin, end); +} + +void +Option::unpackOptions(const OptionBuffer& buf) { + list<uint16_t> deferred; + switch (universe_) { + case V4: + LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(), + options_, deferred, + getType() == DHO_VENDOR_ENCAPSULATED_OPTIONS); + return; + case V6: + LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_); + return; + default: + isc_throw(isc::BadValue, "Invalid universe type " << universe_); + } +} + +uint16_t Option::len() const { + // Returns length of the complete option (data length + DHCPv4/DHCPv6 + // option header) + + // length of the whole option is header and data stored in this option... + size_t length = getHeaderLen() + data_.size(); + + // ... and sum of lengths of all suboptions + for (auto const& option : options_) { + length += option.second->len(); + } + + // note that this is not equal to length field. This value denotes + // number of bytes required to store this option. length option should + // contain (len()-getHeaderLen()) value. + return (static_cast<uint16_t>(length)); +} + +bool +Option::valid() const { + if (universe_ != V4 && + universe_ != V6) { + return (false); + } + + return (true); +} + +OptionPtr Option::getOption(uint16_t opt_type) const { + auto const& x = options_.find(opt_type); + if (x != options_.end()) { + return (x->second); + } + return OptionPtr(); // NULL +} + +void +Option::getOptionsCopy(OptionCollection& options_copy) const { + OptionCollection local_options; + for (auto const& option : options_) { + OptionPtr copy = option.second->clone(); + local_options.insert(std::make_pair(option.second->getType(), copy)); + } + // All options copied successfully, so assign them to the output + // parameter. + options_copy.swap(local_options); +} + +bool Option::delOption(uint16_t opt_type) { + auto const& x = options_.find(opt_type); + if (x != options_.end()) { + options_.erase(x); + return (true); // delete successful + } + return (false); // option not found, can't delete +} + +std::string Option::toText(int indent) const { + std::stringstream output; + output << headerToText(indent) << ": "; + + for (unsigned int i = 0; i < data_.size(); i++) { + if (i) { + output << ":"; + } + output << setfill('0') << setw(2) << hex + << static_cast<unsigned short>(data_[i]); + } + + // Append suboptions. + output << suboptionsToText(indent + 2); + + return (output.str()); +} + +std::string +Option::toString() const { + /// @todo: Implement actual conversion in derived classes. + return (toText(0)); +} + +std::vector<uint8_t> +Option::toBinary(const bool include_header) const { + OutputBuffer buf(len()); + try { + // The RFC3396 adds support for long options split over multiple options + // using the same code. + pack(buf, false); + + } catch (const std::exception &ex) { + isc_throw(OutOfRange, "unable to obtain hexadecimal representation" + " of option " << getType() << ": " << ex.what()); + } + const uint8_t* option_data = static_cast<const uint8_t*>(buf.getData()); + + // Assign option data to a vector, with or without option header depending + // on the value of "include_header" flag. + std::vector<uint8_t> option_vec(option_data + (include_header ? 0 : getHeaderLen()), + option_data + buf.getLength()); + return (option_vec); +} + +std::string +Option::toHexString(const bool include_header) const { + // Prepare binary version of the option. + std::vector<uint8_t> option_vec = toBinary(include_header); + + // Return hexadecimal representation prepended with 0x or empty string + // if option has no payload and the header fields are excluded. + std::ostringstream s; + if (!option_vec.empty()) { + s << "0x" << encode::encodeHex(option_vec); + } + return (s.str()); +} + +std::string +Option::headerToText(const int indent, const std::string& type_name) const { + std::stringstream output; + for (int i = 0; i < indent; i++) + output << " "; + + int field_len = (getUniverse() == V4 ? 3 : 5); + output << "type=" << std::setw(field_len) << std::setfill('0') + << type_; + + if (!type_name.empty()) { + output << "(" << type_name << ")"; + } + + output << ", len=" << std::setw(field_len) << std::setfill('0') + << len() - getHeaderLen(); + return (output.str()); +} + +std::string +Option::suboptionsToText(const int indent) const { + std::stringstream output; + + if (!options_.empty()) { + output << "," << std::endl << "options:"; + for (auto const& opt : options_) { + output << std::endl << opt.second->toText(indent); + } + } + + return (output.str()); +} + +uint16_t +Option::getHeaderLen() const { + switch (universe_) { + case V4: + return OPTION4_HDR_LEN; // header length for v4 + case V6: + return OPTION6_HDR_LEN; // header length for v6 + } + return 0; // should not happen +} + +void Option::addOption(OptionPtr opt) { + if (this == opt.get()) { + // Do not allow options to be added to themselves as this + // can lead to infinite recursion. + isc_throw(InvalidOperation, "option cannot be added to itself: " << toText()); + } + + options_.insert(make_pair(opt->getType(), opt)); +} + +uint8_t Option::getUint8() const { + if (data_.size() < sizeof(uint8_t) ) { + isc_throw(OutOfRange, "Attempt to read uint8 from option " << type_ + << " that has size " << data_.size()); + } + return (data_[0]); +} + +uint16_t Option::getUint16() const { + // readUint16() checks and throws OutOfRange if data_ is too small. + return (readUint16(&data_[0], data_.size())); +} + +uint32_t Option::getUint32() const { + // readUint32() checks and throws OutOfRange if data_ is too small. + return (readUint32(&data_[0], data_.size())); +} + +void Option::setUint8(uint8_t value) { + data_.resize(sizeof(value)); + data_[0] = value; +} + +void Option::setUint16(uint16_t value) { + data_.resize(sizeof(value)); + writeUint16(value, &data_[0], data_.size()); +} + +void Option::setUint32(uint32_t value) { + data_.resize(sizeof(value)); + writeUint32(value, &data_[0], data_.size()); +} + +bool Option::equals(const OptionPtr& other) const { + return (equals(*other)); +} + +bool Option::equals(const Option& other) const { + return ((getType() == other.getType()) && + (getData() == other.getData())); +} + +Option::~Option() { +} + +bool Option::lenient_parsing_; + +} // end of isc::dhcp namespace +} // end of isc namespace |