"Fossies" - the Fresh Open Source Software Archive

Member "kea-1.6.2/src/lib/dhcp/option.cc" (21 Feb 2020, 11426 Bytes) of package /linux/misc/kea-1.6.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "option.cc" see the Fossies "Dox" file reference documentation.

    1 // Copyright (C) 2011-2020 Internet Systems Consortium, Inc. ("ISC")
    2 //
    3 // This Source Code Form is subject to the terms of the Mozilla Public
    4 // License, v. 2.0. If a copy of the MPL was not distributed with this
    5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
    6 
    7 #include <config.h>
    8 #include <dhcp/dhcp4.h>
    9 #include <dhcp/libdhcp++.h>
   10 #include <dhcp/option.h>
   11 #include <dhcp/option_space.h>
   12 #include <exceptions/exceptions.h>
   13 #include <util/encode/hex.h>
   14 #include <util/io_utilities.h>
   15 
   16 #include <boost/make_shared.hpp>
   17 
   18 #include <iomanip>
   19 #include <list>
   20 #include <sstream>
   21 
   22 #include <arpa/inet.h>
   23 #include <stdint.h>
   24 #include <string.h>
   25 
   26 using namespace std;
   27 using namespace isc::util;
   28 
   29 namespace isc {
   30 namespace dhcp {
   31 
   32 OptionPtr
   33 Option::factory(Option::Universe u,
   34         uint16_t type,
   35         const OptionBuffer& buf) {
   36     return(LibDHCP::optionFactory(u, type, buf));
   37 }
   38 
   39 
   40 Option::Option(Universe u, uint16_t type)
   41     :universe_(u), type_(type) {
   42     check();
   43 }
   44 
   45 Option::Option(Universe u, uint16_t type, const OptionBuffer& data)
   46     :universe_(u), type_(type), data_(data) {
   47     check();
   48 }
   49 
   50 Option::Option(Universe u, uint16_t type, OptionBufferConstIter first,
   51                OptionBufferConstIter last)
   52     :universe_(u), type_(type), data_(first, last) {
   53     check();
   54 }
   55 
   56 Option::Option(const Option& option)
   57     : universe_(option.universe_), type_(option.type_),
   58       data_(option.data_), options_(),
   59       encapsulated_space_(option.encapsulated_space_) {
   60     option.getOptionsCopy(options_);
   61 }
   62 
   63 OptionPtr
   64 Option::create(Universe u, uint16_t type) {
   65     return (boost::make_shared<Option>(u, type));
   66 }
   67 
   68 OptionPtr
   69 Option::create(Universe u, uint16_t type, const OptionBuffer& data) {
   70     return (boost::make_shared<Option>(u, type, data));
   71 }
   72 
   73 Option&
   74 Option::operator=(const Option& rhs) {
   75     if (&rhs != this) {
   76         universe_ = rhs.universe_;
   77         type_ = rhs.type_;
   78         data_ = rhs.data_;
   79         rhs.getOptionsCopy(options_);
   80         encapsulated_space_ = rhs.encapsulated_space_;
   81     }
   82     return (*this);
   83 }
   84 
   85 OptionPtr
   86 Option::clone() const {
   87     return (cloneInternal<Option>());
   88 }
   89 
   90 void
   91 Option::check() const {
   92     if ( (universe_ != V4) && (universe_ != V6) ) {
   93         isc_throw(BadValue, "Invalid universe type specified. "
   94                   << "Only V4 and V6 are allowed.");
   95     }
   96 
   97     if (universe_ == V4) {
   98 
   99         if (type_ > 255) {
  100             isc_throw(OutOfRange, "DHCPv4 Option type " << type_ << " is too big. "
  101                       << "For DHCPv4 allowed type range is 0..255");
  102         } else if (data_.size() > 255) {
  103             isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big.");
  104             /// TODO Larger options can be stored as separate instances
  105             /// of DHCPv4 options. Clients MUST concatenate them.
  106             /// Fortunately, there are no such large options used today.
  107         }
  108     }
  109 
  110     // no need to check anything for DHCPv6. It allows full range (0-64k) of
  111     // both types and data size.
  112 }
  113 
  114 void Option::pack(isc::util::OutputBuffer& buf) const {
  115     // Write a header.
  116     packHeader(buf);
  117     // Write data.
  118     if (!data_.empty()) {
  119         buf.writeData(&data_[0], data_.size());
  120     }
  121     // Write sub-options.
  122     packOptions(buf);
  123 }
  124 
  125 void
  126 Option::packHeader(isc::util::OutputBuffer& buf) const {
  127     if (universe_ == V4) {
  128         if (len() > 255) {
  129             isc_throw(OutOfRange, "DHCPv4 Option " << type_ << " is too big. "
  130                       << "At most 255 bytes are supported.");
  131             /// TODO Larger options can be stored as separate instances
  132             /// of DHCPv4 options. Clients MUST concatenate them.
  133             /// Fortunately, there are no such large options used today.
  134         }
  135 
  136         buf.writeUint8(type_);
  137         buf.writeUint8(len() - getHeaderLen());
  138 
  139     } else {
  140         buf.writeUint16(type_);
  141         buf.writeUint16(len() - getHeaderLen());
  142     }
  143 }
  144 
  145 void
  146 Option::packOptions(isc::util::OutputBuffer& buf) const {
  147     switch (universe_) {
  148     case V4:
  149         LibDHCP::packOptions4(buf, options_);
  150         return;
  151     case V6:
  152         LibDHCP::packOptions6(buf, options_);
  153         return;
  154     default:
  155         isc_throw(isc::BadValue, "Invalid universe type " << universe_);
  156     }
  157 }
  158 
  159 void Option::unpack(OptionBufferConstIter begin,
  160                     OptionBufferConstIter end) {
  161     setData(begin, end);
  162 }
  163 
  164 void
  165 Option::unpackOptions(const OptionBuffer& buf) {
  166     list<uint16_t> deferred;
  167     switch (universe_) {
  168     case V4:
  169         LibDHCP::unpackOptions4(buf, getEncapsulatedSpace(),
  170                                 options_, deferred,
  171                                 getType() == DHO_VENDOR_ENCAPSULATED_OPTIONS);
  172         return;
  173     case V6:
  174         LibDHCP::unpackOptions6(buf, getEncapsulatedSpace(), options_);
  175         return;
  176     default:
  177         isc_throw(isc::BadValue, "Invalid universe type " << universe_);
  178     }
  179 }
  180 
  181 uint16_t Option::len() const {
  182     // Returns length of the complete option (data length + DHCPv4/DHCPv6
  183     // option header)
  184 
  185     // length of the whole option is header and data stored in this option...
  186     size_t length = getHeaderLen() + data_.size();
  187 
  188     // ... and sum of lengths of all suboptions
  189     for (OptionCollection::const_iterator it = options_.begin();
  190          it != options_.end();
  191          ++it) {
  192         length += (*it).second->len();
  193     }
  194 
  195     // note that this is not equal to length field. This value denotes
  196     // number of bytes required to store this option. length option should
  197     // contain (len()-getHeaderLen()) value.
  198     return (static_cast<uint16_t>(length));
  199 }
  200 
  201 bool
  202 Option::valid() const {
  203     if (universe_ != V4 &&
  204         universe_ != V6) {
  205         return (false);
  206     }
  207 
  208     return (true);
  209 }
  210 
  211 OptionPtr Option::getOption(uint16_t opt_type) const {
  212     isc::dhcp::OptionCollection::const_iterator x =
  213         options_.find(opt_type);
  214     if ( x != options_.end() ) {
  215         return (*x).second;
  216     }
  217     return OptionPtr(); // NULL
  218 }
  219 
  220 void
  221 Option::getOptionsCopy(OptionCollection& options_copy) const {
  222     OptionCollection local_options;
  223     for (OptionCollection::const_iterator it = options_.begin();
  224          it != options_.end(); ++it) {
  225         OptionPtr copy = it->second->clone();
  226         local_options.insert(std::make_pair(it->second->getType(),
  227                                             copy));
  228     }
  229     // All options copied successfully, so assign them to the output
  230     // parameter.
  231     options_copy.swap(local_options);
  232 }
  233 
  234 bool Option::delOption(uint16_t opt_type) {
  235     isc::dhcp::OptionCollection::iterator x = options_.find(opt_type);
  236     if ( x != options_.end() ) {
  237         options_.erase(x);
  238         return true; // delete successful
  239     }
  240     return (false); // option not found, can't delete
  241 }
  242 
  243 
  244 std::string Option::toText(int indent) const {
  245     std::stringstream output;
  246     output << headerToText(indent) << ": ";
  247 
  248     for (unsigned int i = 0; i < data_.size(); i++) {
  249         if (i) {
  250             output << ":";
  251         }
  252         output << setfill('0') << setw(2) << hex
  253             << static_cast<unsigned short>(data_[i]);
  254     }
  255 
  256     // Append suboptions.
  257     output << suboptionsToText(indent + 2);
  258 
  259     return (output.str());
  260 }
  261 
  262 std::string
  263 Option::toString() const {
  264     /// @todo: Implement actual conversion in derived classes.
  265     return (toText(0));
  266 }
  267 
  268 std::vector<uint8_t>
  269 Option::toBinary(const bool include_header) const {
  270     OutputBuffer buf(len());
  271     try {
  272         // If the option is too long, exception will be thrown. We allow
  273         // for this exception to propagate to not mask this error.
  274         pack(buf);
  275 
  276     } catch (const std::exception &ex) {
  277         isc_throw(OutOfRange, "unable to obtain hexadecimal representation"
  278                   " of option " << getType() << ": " << ex.what());
  279     }
  280     const uint8_t* option_data = static_cast<const uint8_t*>(buf.getData());
  281 
  282     // Assign option data to a vector, with or without option header depending
  283     // on the value of "include_header" flag.
  284     std::vector<uint8_t> option_vec(option_data + (include_header ? 0 : getHeaderLen()),
  285                                     option_data + buf.getLength());
  286     return (option_vec);
  287 }
  288 
  289 std::string
  290 Option::toHexString(const bool include_header) const {
  291     // Prepare binary version of the option.
  292     std::vector<uint8_t> option_vec = toBinary(include_header);
  293 
  294     // Return hexadecimal representation prepended with 0x or empty string
  295     // if option has no payload and the header fields are excluded.
  296     std::ostringstream s;
  297     if (!option_vec.empty()) {
  298         s << "0x" << encode::encodeHex(option_vec);
  299     }
  300     return (s.str());
  301 }
  302 
  303 std::string
  304 Option::headerToText(const int indent, const std::string& type_name) const {
  305     std::stringstream output;
  306     for (int i = 0; i < indent; i++)
  307         output << " ";
  308 
  309     int field_len = (getUniverse() == V4 ? 3 : 5);
  310     output << "type=" << std::setw(field_len) << std::setfill('0')
  311            << type_;
  312 
  313     if (!type_name.empty()) {
  314         output << "(" << type_name << ")";
  315     }
  316 
  317     output << ", len=" << std::setw(field_len) << std::setfill('0')
  318            << len()-getHeaderLen();
  319     return (output.str());
  320 }
  321 
  322 std::string
  323 Option::suboptionsToText(const int indent) const {
  324     std::stringstream output;
  325 
  326     if (!options_.empty()) {
  327         output << "," << std::endl << "options:";
  328         for (OptionCollection::const_iterator opt = options_.begin();
  329              opt != options_.end(); ++opt) {
  330             output << std::endl << (*opt).second->toText(indent);
  331         }
  332     }
  333 
  334     return (output.str());
  335 }
  336 
  337 uint16_t
  338 Option::getHeaderLen() const {
  339     switch (universe_) {
  340     case V4:
  341         return OPTION4_HDR_LEN; // header length for v4
  342     case V6:
  343         return OPTION6_HDR_LEN; // header length for v6
  344     }
  345     return 0; // should not happen
  346 }
  347 
  348 void Option::addOption(OptionPtr opt) {
  349     if (universe_ == V4) {
  350         // check for uniqueness (DHCPv4 options must be unique)
  351         if (getOption(opt->getType())) {
  352             isc_throw(BadValue, "Option " << opt->getType()
  353                       << " already present in this message.");
  354         }
  355     }
  356     options_.insert(make_pair(opt->getType(), opt));
  357 }
  358 
  359 uint8_t Option::getUint8() const {
  360     if (data_.size() < sizeof(uint8_t) ) {
  361         isc_throw(OutOfRange, "Attempt to read uint8 from option " << type_
  362                   << " that has size " << data_.size());
  363     }
  364     return (data_[0]);
  365 }
  366 
  367 uint16_t Option::getUint16() const {
  368     // readUint16() checks and throws OutOfRange if data_ is too small.
  369     return (readUint16(&data_[0], data_.size()));
  370 }
  371 
  372 uint32_t Option::getUint32() const {
  373     // readUint32() checks and throws OutOfRange if data_ is too small.
  374     return (readUint32(&data_[0], data_.size()));
  375 }
  376 
  377 void Option::setUint8(uint8_t value) {
  378     data_.resize(sizeof(value));
  379     data_[0] = value;
  380 }
  381 
  382 void Option::setUint16(uint16_t value) {
  383     data_.resize(sizeof(value));
  384     writeUint16(value, &data_[0], data_.size());
  385 }
  386 
  387 void Option::setUint32(uint32_t value) {
  388     data_.resize(sizeof(value));
  389     writeUint32(value, &data_[0], data_.size());
  390 }
  391 
  392 bool Option::equals(const OptionPtr& other) const {
  393     return (equals(*other));
  394 }
  395 
  396 bool Option::equals(const Option& other) const {
  397     return ( (getType() == other.getType()) &&
  398              (getData() == other.getData()) );
  399 }
  400 
  401 Option::~Option() {
  402 
  403 }
  404 
  405 } // end of isc::dhcp namespace
  406 } // end of isc namespace