"Fossies" - the Fresh Open Source Software Archive

Member "kea-1.6.2/src/lib/dhcp/libdhcp++.cc" (21 Feb 2020, 40479 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. See also the latest Fossies "Diffs" side-by-side code changes report for "libdhcp++.cc": 1.6.1_vs_1.6.2.

    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 
    9 #include <dhcp/dhcp4.h>
   10 #include <dhcp/dhcp6.h>
   11 #include <dhcp/libdhcp++.h>
   12 #include <dhcp/option.h>
   13 #include <dhcp/option_vendor.h>
   14 #include <dhcp/option6_ia.h>
   15 #include <dhcp/option6_iaaddr.h>
   16 #include <dhcp/option_definition.h>
   17 #include <dhcp/option_int_array.h>
   18 #include <dhcp/option_space.h>
   19 #include <dhcp/std_option_defs.h>
   20 #include <dhcp/docsis3_option_defs.h>
   21 #include <exceptions/exceptions.h>
   22 #include <util/buffer.h>
   23 #include <dhcp/option_definition.h>
   24 
   25 #include <boost/lexical_cast.hpp>
   26 #include <boost/shared_array.hpp>
   27 #include <boost/shared_ptr.hpp>
   28 
   29 #include <limits>
   30 #include <list>
   31 
   32 using namespace std;
   33 using namespace isc::dhcp;
   34 using namespace isc::util;
   35 
   36 // static array with factories for options
   37 std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
   38 
   39 // static array with factories for options
   40 std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
   41 
   42 // Static container with DHCPv4 option definitions.
   43 OptionDefContainerPtr LibDHCP::v4option_defs_(new OptionDefContainer());
   44 
   45 // Static container with DHCPv6 option definitions.
   46 OptionDefContainerPtr LibDHCP::v6option_defs_(new OptionDefContainer());
   47 
   48 // Static container with option definitions grouped by option space.
   49 OptionDefContainers LibDHCP::option_defs_;
   50 
   51 // Static container with vendor option definitions for DHCPv4.
   52 VendorOptionDefContainers LibDHCP::vendor4_defs_;
   53 
   54 // Static container with vendor option definitions for DHCPv6.
   55 VendorOptionDefContainers LibDHCP::vendor6_defs_;
   56 
   57 // Static container with last resort option definitions for DHCPv4.
   58 OptionDefContainerPtr LibDHCP::lastresort_defs_(new OptionDefContainer());
   59 
   60 // Static container with option definitions created in runtime.
   61 StagedValue<OptionDefSpaceContainer> LibDHCP::runtime_option_defs_;
   62 
   63 // Null container.
   64 const OptionDefContainerPtr null_option_def_container_(new OptionDefContainer());
   65 
   66 // Those two vendor classes are used for cable modems:
   67 
   68 /// DOCSIS3.0 compatible cable modem
   69 const char* isc::dhcp::DOCSIS3_CLASS_MODEM = "docsis3.0";
   70 
   71 /// DOCSIS3.0 cable modem that has router built-in
   72 const char* isc::dhcp::DOCSIS3_CLASS_EROUTER = "eRouter1.0";
   73 
   74 // Let's keep it in .cc file. Moving it to .h would require including optionDefParams
   75 // definitions there
   76 void initOptionSpace(OptionDefContainerPtr& defs,
   77                      const OptionDefParams* params,
   78                      size_t params_size);
   79 
   80 const OptionDefContainerPtr&
   81 LibDHCP::getOptionDefs(const std::string& space) {
   82     // If any of the containers is not initialized, it means that we haven't
   83     // initialized option definitions at all.
   84     if (v4option_defs_->empty()) {
   85         initStdOptionDefs4();
   86         initVendorOptsDocsis4();
   87         initStdOptionDefs6();
   88         initVendorOptsDocsis6();
   89         initLastResortOptionDefs();
   90     }
   91 
   92     if (space == DHCP4_OPTION_SPACE) {
   93         return (v4option_defs_);
   94 
   95     } else if (space == DHCP6_OPTION_SPACE) {
   96         return (v6option_defs_);
   97     }
   98 
   99     OptionDefContainers::const_iterator container = option_defs_.find(space);
  100     if (container != option_defs_.end()) {
  101         return (container->second);
  102     }
  103     return (null_option_def_container_);
  104 }
  105 
  106 const OptionDefContainerPtr&
  107 LibDHCP::getVendorOption4Defs(const uint32_t vendor_id) {
  108 
  109     if (vendor_id == VENDOR_ID_CABLE_LABS &&
  110         vendor4_defs_.find(VENDOR_ID_CABLE_LABS) == vendor4_defs_.end()) {
  111         initVendorOptsDocsis4();
  112     }
  113 
  114     VendorOptionDefContainers::const_iterator def = vendor4_defs_.find(vendor_id);
  115     if (def == vendor4_defs_.end()) {
  116         // No such vendor-id space
  117         return (null_option_def_container_);
  118     }
  119     return (def->second);
  120 }
  121 
  122 const OptionDefContainerPtr&
  123 LibDHCP::getVendorOption6Defs(const uint32_t vendor_id) {
  124 
  125     if (vendor_id == VENDOR_ID_CABLE_LABS &&
  126         vendor6_defs_.find(VENDOR_ID_CABLE_LABS) == vendor6_defs_.end()) {
  127         initVendorOptsDocsis6();
  128     }
  129 
  130     if (vendor_id == ENTERPRISE_ID_ISC &&
  131         vendor6_defs_.find(ENTERPRISE_ID_ISC) == vendor6_defs_.end()) {
  132         initVendorOptsIsc6();
  133     }
  134 
  135     VendorOptionDefContainers::const_iterator def = vendor6_defs_.find(vendor_id);
  136     if (def == vendor6_defs_.end()) {
  137         // No such vendor-id space
  138         return (null_option_def_container_);
  139     }
  140     return (def->second);
  141 }
  142 
  143 OptionDefinitionPtr
  144 LibDHCP::getOptionDef(const std::string& space, const uint16_t code) {
  145     const OptionDefContainerPtr& defs = getOptionDefs(space);
  146     const OptionDefContainerTypeIndex& idx = defs->get<1>();
  147     const OptionDefContainerTypeRange& range = idx.equal_range(code);
  148     if (range.first != range.second) {
  149         return (*range.first);
  150     }
  151     return (OptionDefinitionPtr());
  152 }
  153 
  154 OptionDefinitionPtr
  155 LibDHCP::getOptionDef(const std::string& space, const std::string& name) {
  156     const OptionDefContainerPtr defs = getOptionDefs(space);
  157     const OptionDefContainerNameIndex& idx = defs->get<2>();
  158     const OptionDefContainerNameRange& range = idx.equal_range(name);
  159     if (range.first != range.second) {
  160         return (*range.first);
  161     }
  162     return (OptionDefinitionPtr());
  163 }
  164 
  165 OptionDefinitionPtr
  166 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
  167                             const std::string& name) {
  168     OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
  169                                   getVendorOption6Defs(vendor_id));
  170 
  171     if (!defs) {
  172         return (OptionDefinitionPtr());
  173     }
  174 
  175     const OptionDefContainerNameIndex& idx = defs->get<2>();
  176     const OptionDefContainerNameRange& range = idx.equal_range(name);
  177     if (range.first != range.second) {
  178         return (*range.first);
  179     }
  180     return (OptionDefinitionPtr());
  181 }
  182 
  183 OptionDefinitionPtr
  184 LibDHCP::getVendorOptionDef(const Option::Universe u, const uint32_t vendor_id,
  185                             const uint16_t code) {
  186     OptionDefContainerPtr defs = (u == Option::V4 ? getVendorOption4Defs(vendor_id) :
  187                                   getVendorOption6Defs(vendor_id));
  188 
  189     if (!defs) {
  190         // Weird universe or unknown vendor_id. We don't care. No definitions
  191         // one way or another
  192         // What is it anyway?
  193         return (OptionDefinitionPtr());
  194     }
  195 
  196     const OptionDefContainerTypeIndex& idx = defs->get<1>();
  197     const OptionDefContainerTypeRange& range = idx.equal_range(code);
  198     if (range.first != range.second) {
  199         return (*range.first);
  200     }
  201     return (OptionDefinitionPtr());
  202 }
  203 
  204 OptionDefinitionPtr
  205 LibDHCP::getRuntimeOptionDef(const std::string& space, const uint16_t code) {
  206     OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
  207     const OptionDefContainerTypeIndex& index = container->get<1>();
  208     const OptionDefContainerTypeRange& range = index.equal_range(code);
  209     if (range.first != range.second) {
  210         return (*range.first);
  211     }
  212 
  213     return (OptionDefinitionPtr());
  214 }
  215 
  216 OptionDefinitionPtr
  217 LibDHCP::getRuntimeOptionDef(const std::string& space, const std::string& name) {
  218     OptionDefContainerPtr container = runtime_option_defs_.getValue().getItems(space);
  219     const OptionDefContainerNameIndex& index = container->get<2>();
  220     const OptionDefContainerNameRange& range = index.equal_range(name);
  221     if (range.first != range.second) {
  222         return (*range.first);
  223     }
  224 
  225     return (OptionDefinitionPtr());
  226 }
  227 
  228 OptionDefContainerPtr
  229 LibDHCP::getRuntimeOptionDefs(const std::string& space) {
  230     return (runtime_option_defs_.getValue().getItems(space));
  231 }
  232 
  233 void
  234 LibDHCP::setRuntimeOptionDefs(const OptionDefSpaceContainer& defs) {
  235     OptionDefSpaceContainer defs_copy;
  236     std::list<std::string> option_space_names = defs.getOptionSpaceNames();
  237     for (std::list<std::string>::const_iterator name = option_space_names.begin();
  238          name != option_space_names.end(); ++name) {
  239         OptionDefContainerPtr container = defs.getItems(*name);
  240         for (OptionDefContainer::const_iterator def = container->begin();
  241              def != container->end(); ++def) {
  242             OptionDefinitionPtr def_copy(new OptionDefinition(**def));
  243             defs_copy.addItem(def_copy, *name);
  244         }
  245     }
  246     runtime_option_defs_ = defs_copy;
  247 }
  248 
  249 void
  250 LibDHCP::clearRuntimeOptionDefs() {
  251     runtime_option_defs_.reset();
  252 }
  253 
  254 void
  255 LibDHCP::revertRuntimeOptionDefs() {
  256     runtime_option_defs_.revert();
  257 }
  258 
  259 void
  260 LibDHCP::commitRuntimeOptionDefs() {
  261     runtime_option_defs_.commit();
  262 }
  263 
  264 OptionDefinitionPtr
  265 LibDHCP::getLastResortOptionDef(const std::string& space, const uint16_t code) {
  266     OptionDefContainerPtr container = getLastResortOptionDefs(space);
  267     const OptionDefContainerTypeIndex& index = container->get<1>();
  268     const OptionDefContainerTypeRange& range = index.equal_range(code);
  269     if (range.first != range.second) {
  270         return (*range.first);
  271     }
  272 
  273     return (OptionDefinitionPtr());
  274 }
  275 
  276 OptionDefinitionPtr
  277 LibDHCP::getLastResortOptionDef(const std::string& space, const std::string& name) {
  278     OptionDefContainerPtr container = getLastResortOptionDefs(space);
  279     const OptionDefContainerNameIndex& index = container->get<2>();
  280     const OptionDefContainerNameRange& range = index.equal_range(name);
  281     if (range.first != range.second) {
  282         return (*range.first);
  283     }
  284 
  285     return (OptionDefinitionPtr());
  286 }
  287 
  288 OptionDefContainerPtr
  289 LibDHCP::getLastResortOptionDefs(const std::string& space) {
  290     if (space == DHCP4_OPTION_SPACE) {
  291         return (lastresort_defs_);
  292     }
  293     return (null_option_def_container_);
  294 }
  295 
  296 bool
  297 LibDHCP::shouldDeferOptionUnpack(const std::string& space, const uint16_t code) {
  298     return ((space == DHCP4_OPTION_SPACE) &&
  299             ((code == DHO_VENDOR_ENCAPSULATED_OPTIONS) ||
  300              ((code >= 224) && (code <= 254))));
  301 }
  302 
  303 OptionPtr
  304 LibDHCP::optionFactory(Option::Universe u,
  305                        uint16_t type,
  306                        const OptionBuffer& buf) {
  307     FactoryMap::iterator it;
  308     if (u == Option::V4) {
  309         it = v4factories_.find(type);
  310         if (it == v4factories_.end()) {
  311             isc_throw(BadValue, "factory function not registered "
  312             "for DHCP v4 option type " << type);
  313         }
  314     } else if (u == Option::V6) {
  315         it = v6factories_.find(type);
  316         if (it == v6factories_.end()) {
  317             isc_throw(BadValue, "factory function not registered "
  318                       "for DHCPv6 option type " << type);
  319         }
  320     } else {
  321         isc_throw(BadValue, "invalid universe specified (expected "
  322                   "Option::V4 or Option::V6");
  323     }
  324     return (it->second(u, type, buf));
  325 }
  326 
  327 
  328 size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
  329                                const std::string& option_space,
  330                                isc::dhcp::OptionCollection& options,
  331                                size_t* relay_msg_offset /* = 0 */,
  332                                size_t* relay_msg_len /* = 0 */) {
  333     size_t offset = 0;
  334     size_t length = buf.size();
  335     size_t last_offset = 0;
  336 
  337     // Get the list of standard option definitions.
  338     const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
  339     // Runtime option definitions for non standard option space and if
  340     // the definition doesn't exist within the standard option definitions.
  341     const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
  342 
  343     // @todo Once we implement other option spaces we should add else clause
  344     // here and gather option definitions for them. For now leaving option_defs
  345     // empty will imply creation of generic Option.
  346 
  347     // Get the search indexes #1. It allows to search for option definitions
  348     // using option code.
  349     const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
  350     const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
  351 
  352     // The buffer being read comprises a set of options, each starting with
  353     // a two-byte type code and a two-byte length field.
  354     while (offset < length) {
  355         // Save the current offset for backtracking
  356         last_offset = offset;
  357 
  358         // Check if there is room for another option
  359         if (offset + 4 > length) {
  360             // Still something but smaller than an option
  361             return (last_offset);
  362         }
  363 
  364         // Parse the option header
  365         uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
  366         offset += 2;
  367 
  368         uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
  369         offset += 2;
  370 
  371         if (offset + opt_len > length) {
  372             // We peeked at the option header of the next option, but
  373             // discovered that it would end up beyond buffer end, so
  374             // the option is truncated. Hence we can't parse
  375             // it. Therefore we revert back by those bytes (as if
  376             // we never parsed them).
  377             //
  378             // @note it is the responsibility of the caller to throw
  379             // an exception on partial parsing
  380             return (last_offset);
  381         }
  382 
  383         if (opt_type == D6O_RELAY_MSG && relay_msg_offset && relay_msg_len) {
  384             // remember offset of the beginning of the relay-msg option
  385             *relay_msg_offset = offset;
  386             *relay_msg_len = opt_len;
  387 
  388             // do not create that relay-msg option
  389             offset += opt_len;
  390             continue;
  391         }
  392 
  393         if (opt_type == D6O_VENDOR_OPTS) {
  394             if (offset + 4 > length) {
  395                 // Truncated vendor-option. We expect at least
  396                 // 4 bytes for the enterprise-id field. Let's roll back
  397                 // option code + option length (4 bytes) and return.
  398                 return (last_offset);
  399             }
  400 
  401             // Parse this as vendor option
  402             OptionPtr vendor_opt(new OptionVendor(Option::V6, buf.begin() + offset,
  403                                                   buf.begin() + offset + opt_len));
  404             options.insert(std::make_pair(opt_type, vendor_opt));
  405 
  406             offset += opt_len;
  407             continue;
  408         }
  409 
  410         // Get all definitions with the particular option code. Note
  411         // that option code is non-unique within this container
  412         // however at this point we expect to get one option
  413         // definition with the particular code. If more are returned
  414         // we report an error.
  415         OptionDefContainerTypeRange range;
  416         // Number of option definitions returned.
  417         size_t num_defs = 0;
  418 
  419         // We previously did the lookup only for dhcp6 option space, but with the
  420         // addition of S46 options, we now do it for every space.
  421         range = idx.equal_range(opt_type);
  422         num_defs = std::distance(range.first, range.second);
  423 
  424         // Standard option definitions do not include the definition for
  425         // our option or we're searching for non-standard option. Try to
  426         // find the definition among runtime option definitions.
  427         if (num_defs == 0) {
  428             range = runtime_idx.equal_range(opt_type);
  429             num_defs = std::distance(range.first, range.second);
  430         }
  431 
  432         OptionPtr opt;
  433         if (num_defs > 1) {
  434             // Multiple options of the same code are not supported right now!
  435             isc_throw(isc::Unexpected, "Internal error: multiple option"
  436                       " definitions for option type " << opt_type <<
  437                       " returned. Currently it is not supported to initialize"
  438                       " multiple option definitions for the same option code."
  439                       " This will be supported once support for option spaces"
  440                       " is implemented");
  441         } else if (num_defs == 0) {
  442             // @todo Don't crash if definition does not exist because
  443             // only a few option definitions are initialized right
  444             // now. In the future we will initialize definitions for
  445             // all options and we will remove this elseif. For now,
  446             // return generic option.
  447             opt = OptionPtr(new Option(Option::V6, opt_type,
  448                                        buf.begin() + offset,
  449                                        buf.begin() + offset + opt_len));
  450         } else {
  451             try {
  452                 // The option definition has been found. Use it to create
  453                 // the option instance from the provided buffer chunk.
  454                 const OptionDefinitionPtr& def = *(range.first);
  455                 assert(def);
  456                 opt = def->optionFactory(Option::V6, opt_type,
  457                                          buf.begin() + offset,
  458                                          buf.begin() + offset + opt_len);
  459             } catch (const SkipThisOptionError&)  {
  460                 opt.reset();
  461             }
  462         }
  463 
  464         // add option to options
  465         if (opt) {
  466             options.insert(std::make_pair(opt_type, opt));
  467         }
  468 
  469         offset += opt_len;
  470     }
  471 
  472     last_offset = offset;
  473     return (last_offset);
  474 }
  475 
  476 size_t
  477 LibDHCP::unpackOptions4(const OptionBuffer& buf,
  478                         const std::string& option_space,
  479                         isc::dhcp::OptionCollection& options,
  480                         std::list<uint16_t>& deferred,
  481                         bool flexible_pad_end) {
  482     size_t offset = 0;
  483     size_t last_offset = 0;
  484 
  485     // Special case when option_space is dhcp4.
  486     bool space_is_dhcp4 = (option_space == DHCP4_OPTION_SPACE);
  487 
  488     // Get the list of standard option definitions.
  489     const OptionDefContainerPtr& option_defs = LibDHCP::getOptionDefs(option_space);
  490     // Runtime option definitions for non standard option space and if
  491     // the definition doesn't exist within the standard option definitions.
  492     const OptionDefContainerPtr& runtime_option_defs = LibDHCP::getRuntimeOptionDefs(option_space);
  493 
  494     // Get the search indexes #1. It allows to search for option definitions
  495     // using option code.
  496     const OptionDefContainerTypeIndex& idx = option_defs->get<1>();
  497     const OptionDefContainerTypeIndex& runtime_idx = runtime_option_defs->get<1>();
  498 
  499     // Flexible PAD and END parsing.
  500     bool flex_pad = (flexible_pad_end && (runtime_idx.count(DHO_PAD) == 0));
  501     bool flex_end = (flexible_pad_end && (runtime_idx.count(DHO_END) == 0));
  502 
  503     // The buffer being read comprises a set of options, each starting with
  504     // a one-byte type code and a one-byte length field.
  505     while (offset < buf.size()) {
  506         // Save the current offset for backtracking
  507         last_offset = offset;
  508 
  509         // Get the option type
  510         uint8_t opt_type = buf[offset++];
  511 
  512         // DHO_END is a special, one octet long option
  513         // Valid in dhcp4 space or when flexible_pad_end is true and
  514         // there is a sub-option configured for this code.
  515         if ((opt_type == DHO_END) && (space_is_dhcp4 || flex_end)) {
  516             // just return. Don't need to add DHO_END option
  517             // Don't return offset because it makes this condition
  518             // and partial parsing impossible to recognize.
  519             return (last_offset);
  520         }
  521 
  522         // DHO_PAD is just a padding after DHO_END. Let's continue parsing
  523         // in case we receive a message without DHO_END.
  524         // Valid in dhcp4 space or when flexible_pad_end is true and
  525         // there is a sub-option configured for this code.
  526         if ((opt_type == DHO_PAD) && (space_is_dhcp4 || flex_pad)) {
  527             continue;
  528         }
  529 
  530         if (offset + 1 > buf.size()) {
  531             // We peeked at the option header of the next option, but
  532             // discovered that it would end up beyond buffer end, so
  533             // the option is truncated. Hence we can't parse
  534             // it. Therefore we revert back (as if we never parsed it).
  535             //
  536             // @note it is the responsibility of the caller to throw
  537             // an exception on partial parsing
  538             return (last_offset);
  539         }
  540 
  541         uint8_t opt_len =  buf[offset++];
  542         if (offset + opt_len > buf.size()) {
  543             // We peeked at the option header of the next option, but
  544             // discovered that it would end up beyond buffer end, so
  545             // the option is truncated. Hence we can't parse
  546             // it. Therefore we revert back (as if we never parsed it).
  547             return (last_offset);
  548         }
  549 
  550         // While an empty Host Name option is non-RFC compliant, some clients
  551         // do send it.  In the spirit of being liberal, we'll just drop it,
  552         // rather than the dropping the whole packet.  We do not have a
  553         // way to log this from here but meh...  a PCAP will show it arriving,
  554         // and we know we drop it.
  555         if (space_is_dhcp4 && opt_len == 0 && opt_type == DHO_HOST_NAME) {
  556             continue;
  557         }
  558 
  559         // Get all definitions with the particular option code. Note
  560         // that option code is non-unique within this container
  561         // however at this point we expect to get one option
  562         // definition with the particular code. If more are returned
  563         // we report an error.
  564         OptionDefContainerTypeRange range;
  565         // Number of option definitions returned.
  566         size_t num_defs = 0;
  567 
  568         // Previously we did the lookup only for "dhcp4" option space, but there
  569         // may be standard options in other spaces (e.g. radius). So we now do
  570         // the lookup for every space.
  571         range = idx.equal_range(opt_type);
  572         num_defs = std::distance(range.first, range.second);
  573 
  574         // Standard option definitions do not include the definition for
  575         // our option or we're searching for non-standard option. Try to
  576         // find the definition among runtime option definitions.
  577         if (num_defs == 0) {
  578             range = runtime_idx.equal_range(opt_type);
  579             num_defs = std::distance(range.first, range.second);
  580         }
  581 
  582         // Check if option unpacking must be deferred
  583         if (shouldDeferOptionUnpack(option_space, opt_type)) {
  584             num_defs = 0;
  585             deferred.push_back(opt_type);
  586         }
  587 
  588         OptionPtr opt;
  589         if (num_defs > 1) {
  590             // Multiple options of the same code are not supported right now!
  591             isc_throw(isc::Unexpected, "Internal error: multiple option"
  592                       " definitions for option type " <<
  593                       static_cast<int>(opt_type) <<
  594                       " returned. Currently it is not supported to initialize"
  595                       " multiple option definitions for the same option code."
  596                       " This will be supported once support for option spaces"
  597                       " is implemented");
  598         } else if (num_defs == 0) {
  599             opt = OptionPtr(new Option(Option::V4, opt_type,
  600                                        buf.begin() + offset,
  601                                        buf.begin() + offset + opt_len));
  602             opt->setEncapsulatedSpace(DHCP4_OPTION_SPACE);
  603         } else {
  604             try {
  605                 // The option definition has been found. Use it to create
  606                 // the option instance from the provided buffer chunk.
  607                 const OptionDefinitionPtr& def = *(range.first);
  608                 assert(def);
  609                 opt = def->optionFactory(Option::V4, opt_type,
  610                                          buf.begin() + offset,
  611                                          buf.begin() + offset + opt_len);
  612             } catch (const SkipThisOptionError&)  {
  613                 opt.reset();
  614             }
  615         }
  616 
  617         // If we have the option, insert it
  618         if (opt) {
  619             options.insert(std::make_pair(opt_type, opt));
  620         }
  621 
  622         offset += opt_len;
  623     }
  624     last_offset = offset;
  625     return (last_offset);
  626 }
  627 
  628 size_t LibDHCP::unpackVendorOptions6(const uint32_t vendor_id,
  629                                      const OptionBuffer& buf,
  630                                      isc::dhcp::OptionCollection& options) {
  631     size_t offset = 0;
  632     size_t length = buf.size();
  633 
  634     // Get the list of option definitions for this particular vendor-id
  635     const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption6Defs(vendor_id);
  636 
  637     // Get the search index #1. It allows to search for option definitions
  638     // using option code. If there's no such vendor-id space, we're out of luck
  639     // anyway.
  640     const OptionDefContainerTypeIndex* idx = NULL;
  641     if (option_defs) {
  642         idx = &(option_defs->get<1>());
  643     }
  644 
  645     // The buffer being read comprises a set of options, each starting with
  646     // a two-byte type code and a two-byte length field.
  647     while (offset < length) {
  648         if (offset + 4 > length) {
  649             isc_throw(SkipRemainingOptionsError,
  650                       "Vendor option parse failed: truncated header");
  651         }
  652 
  653         uint16_t opt_type = isc::util::readUint16(&buf[offset], 2);
  654         offset += 2;
  655 
  656         uint16_t opt_len = isc::util::readUint16(&buf[offset], 2);
  657         offset += 2;
  658 
  659         if (offset + opt_len > length) {
  660             isc_throw(SkipRemainingOptionsError,
  661                       "Vendor option parse failed. Tried to parse "
  662                       << offset + opt_len << " bytes from " << length
  663                       << "-byte long buffer.");
  664         }
  665 
  666         OptionPtr opt;
  667         opt.reset();
  668 
  669         // If there is a definition for such a vendor option...
  670         if (idx) {
  671             // Get all definitions with the particular option
  672             // code. Note that option code is non-unique within this
  673             // container however at this point we expect to get one
  674             // option definition with the particular code. If more are
  675             // returned we report an error.
  676             const OptionDefContainerTypeRange& range =
  677                 idx->equal_range(opt_type);
  678             // Get the number of returned option definitions for the
  679             // option code.
  680             size_t num_defs = std::distance(range.first, range.second);
  681 
  682             if (num_defs > 1) {
  683                 // Multiple options of the same code are not supported
  684                 // right now!
  685                 isc_throw(isc::Unexpected, "Internal error: multiple option"
  686                           " definitions for option type " << opt_type <<
  687                           " returned. Currently it is not supported to"
  688                           " initialize multiple option definitions for the"
  689                           " same option code. This will be supported once"
  690                           " support for option spaces is implemented");
  691             } else if (num_defs == 1) {
  692                 // The option definition has been found. Use it to create
  693                 // the option instance from the provided buffer chunk.
  694                 const OptionDefinitionPtr& def = *(range.first);
  695                 assert(def);
  696                 opt = def->optionFactory(Option::V6, opt_type,
  697                                          buf.begin() + offset,
  698                                          buf.begin() + offset + opt_len);
  699             }
  700         }
  701 
  702         // This can happen in one of 2 cases:
  703         // 1. we do not have definitions for that vendor-space
  704         // 2. we do have definitions, but that particular option was
  705         //    not defined
  706 
  707         if (!opt) {
  708             opt = OptionPtr(new Option(Option::V6, opt_type,
  709                                        buf.begin() + offset,
  710                                        buf.begin() + offset + opt_len));
  711         }
  712 
  713         // add option to options
  714         if (opt) {
  715             options.insert(std::make_pair(opt_type, opt));
  716         }
  717         offset += opt_len;
  718     }
  719 
  720     return (offset);
  721 }
  722 
  723 size_t LibDHCP::unpackVendorOptions4(const uint32_t vendor_id, const OptionBuffer& buf,
  724                                      isc::dhcp::OptionCollection& options) {
  725     size_t offset = 0;
  726 
  727     // Get the list of standard option definitions.
  728     const OptionDefContainerPtr& option_defs = LibDHCP::getVendorOption4Defs(vendor_id);
  729     // Get the search index #1. It allows to search for option definitions
  730     // using option code.
  731     const OptionDefContainerTypeIndex* idx = NULL;
  732     if (option_defs) {
  733         idx = &(option_defs->get<1>());
  734     }
  735 
  736     // The buffer being read comprises a set of options, each starting with
  737     // a one-byte type code and a one-byte length field.
  738     while (offset < buf.size()) {
  739         // Note that Vendor-Specific info option (RFC3925) has a
  740         // different option format than Vendor-Spec info for
  741         // DHCPv6. (there's additional layer of data-length)
  742         uint8_t data_len = buf[offset++];
  743 
  744         if (offset + data_len > buf.size()) {
  745             // The option is truncated.
  746             isc_throw(SkipRemainingOptionsError,
  747                       "Attempt to parse truncated vendor option");
  748         }
  749 
  750         uint8_t offset_end = offset + data_len;
  751 
  752         // beginning of data-chunk parser
  753         while (offset < offset_end) {
  754             uint8_t opt_type = buf[offset++];
  755 
  756             // No DHO_END or DHO_PAD in vendor options
  757 
  758             if (offset + 1 > offset_end) {
  759                 // opt_type must be cast to integer so as it is not
  760                 // treated as unsigned char value (a number is
  761                 // presented in error message).
  762                 isc_throw(SkipRemainingOptionsError,
  763                           "Attempt to parse truncated vendor option "
  764                           << static_cast<int>(opt_type));
  765             }
  766 
  767             uint8_t opt_len =  buf[offset++];
  768             if (offset + opt_len > offset_end) {
  769                 isc_throw(SkipRemainingOptionsError,
  770                           "Option parse failed. Tried to parse "
  771                           << offset + opt_len << " bytes from " << buf.size()
  772                           << "-byte long buffer.");
  773             }
  774 
  775             OptionPtr opt;
  776             opt.reset();
  777 
  778             if (idx) {
  779                 // Get all definitions with the particular option
  780                 // code. Note that option code is non-unique within
  781                 // this container however at this point we expect to
  782                 // get one option definition with the particular
  783                 // code. If more are returned we report an error.
  784                 const OptionDefContainerTypeRange& range =
  785                     idx->equal_range(opt_type);
  786                 // Get the number of returned option definitions for
  787                 // the option code.
  788                 size_t num_defs = std::distance(range.first, range.second);
  789 
  790                 if (num_defs > 1) {
  791                     // Multiple options of the same code are not
  792                     // supported right now!
  793                     isc_throw(isc::Unexpected, "Internal error: multiple"
  794                               " option definitions for option type "
  795                               << opt_type << " returned. Currently it is"
  796                               " not supported to initialize multiple option"
  797                               " definitions for the same option code."
  798                               " This will be supported once support for"
  799                               " option spaces is implemented");
  800                 } else if (num_defs == 1) {
  801                     // The option definition has been found. Use it to create
  802                     // the option instance from the provided buffer chunk.
  803                     const OptionDefinitionPtr& def = *(range.first);
  804                     assert(def);
  805                     opt = def->optionFactory(Option::V4, opt_type,
  806                                              buf.begin() + offset,
  807                                              buf.begin() + offset + opt_len);
  808                 }
  809             }
  810 
  811             if (!opt) {
  812                 opt = OptionPtr(new Option(Option::V4, opt_type,
  813                                            buf.begin() + offset,
  814                                            buf.begin() + offset + opt_len));
  815             }
  816 
  817             options.insert(std::make_pair(opt_type, opt));
  818             offset += opt_len;
  819 
  820         } // end of data-chunk
  821 
  822         break; // end of the vendor block.
  823     }
  824     return (offset);
  825 }
  826 
  827 void
  828 LibDHCP::packOptions4(isc::util::OutputBuffer& buf,
  829                      const OptionCollection& options,
  830                      bool top /* = false */) {
  831     OptionPtr agent;
  832     OptionPtr end;
  833 
  834     // We only look for type when we're the top level
  835     // call that starts packing for options for a packet.
  836     // This way we avoid doing type logic in all ensuing
  837     // recursive calls.
  838     if (top) {
  839         auto x = options.find(DHO_DHCP_MESSAGE_TYPE);
  840         if (x != options.end()) {
  841             x->second->pack(buf);
  842         }
  843     }
  844 
  845     for (OptionCollection::const_iterator it = options.begin();
  846          it != options.end(); ++it) {
  847 
  848         // TYPE is already done, RAI and END options must be last.
  849         switch (it->first) {
  850             case DHO_DHCP_MESSAGE_TYPE:
  851                 break;
  852             case DHO_DHCP_AGENT_OPTIONS:
  853                 agent = it->second;
  854                 break;
  855             case DHO_END:
  856                 end = it->second;
  857                 break;
  858             default:
  859                 it->second->pack(buf);
  860                 break;
  861         }
  862     }
  863 
  864     // Add the RAI option if it exists.
  865     if (agent) {
  866        agent->pack(buf);
  867     }
  868 
  869     // And at the end the END option.
  870     if (end)  {
  871        end->pack(buf);
  872     }
  873 }
  874 
  875 void
  876 LibDHCP::packOptions6(isc::util::OutputBuffer& buf,
  877                       const OptionCollection& options) {
  878     for (OptionCollection::const_iterator it = options.begin();
  879          it != options.end(); ++it) {
  880         it->second->pack(buf);
  881     }
  882 }
  883 
  884 void LibDHCP::OptionFactoryRegister(Option::Universe u,
  885                                     uint16_t opt_type,
  886                                     Option::Factory* factory) {
  887     switch (u) {
  888     case Option::V6: {
  889         if (v6factories_.find(opt_type) != v6factories_.end()) {
  890             isc_throw(BadValue, "There is already DHCPv6 factory registered "
  891                      << "for option type "  << opt_type);
  892         }
  893         v6factories_[opt_type]=factory;
  894         return;
  895     }
  896     case Option::V4:
  897     {
  898         // Option 0 is special (a one octet-long, equal 0) PAD option. It is never
  899         // instantiated as an Option object, but rather consumed during packet parsing.
  900         if (opt_type == 0) {
  901             isc_throw(BadValue, "Cannot redefine PAD option (code=0)");
  902         }
  903         // Option 255 is never instantiated as an option object. It is special
  904         // (a one-octet equal 255) option that is added at the end of all options
  905         // during packet assembly. It is also silently consumed during packet parsing.
  906         if (opt_type > 254) {
  907             isc_throw(BadValue, "Too big option type for DHCPv4, only 0-254 allowed.");
  908         }
  909         if (v4factories_.find(opt_type)!=v4factories_.end()) {
  910             isc_throw(BadValue, "There is already DHCPv4 factory registered "
  911                      << "for option type "  << opt_type);
  912         }
  913         v4factories_[opt_type]=factory;
  914         return;
  915     }
  916     default:
  917         isc_throw(BadValue, "Invalid universe type specified.");
  918     }
  919 
  920     return;
  921 }
  922 
  923 void
  924 LibDHCP::initStdOptionDefs4() {
  925     initOptionSpace(v4option_defs_, STANDARD_V4_OPTION_DEFINITIONS,
  926                     STANDARD_V4_OPTION_DEFINITIONS_SIZE);
  927 }
  928 
  929 void
  930 LibDHCP::initStdOptionDefs6() {
  931     initOptionSpace(v6option_defs_, STANDARD_V6_OPTION_DEFINITIONS,
  932                     STANDARD_V6_OPTION_DEFINITIONS_SIZE);
  933     initOptionSpace(option_defs_[MAPE_V6_OPTION_SPACE], MAPE_V6_OPTION_DEFINITIONS,
  934                     MAPE_V6_OPTION_DEFINITIONS_SIZE);
  935     initOptionSpace(option_defs_[MAPT_V6_OPTION_SPACE], MAPT_V6_OPTION_DEFINITIONS,
  936                     MAPT_V6_OPTION_DEFINITIONS_SIZE);
  937     initOptionSpace(option_defs_[LW_V6_OPTION_SPACE], LW_V6_OPTION_DEFINITIONS,
  938                     LW_V6_OPTION_DEFINITIONS_SIZE);
  939     initOptionSpace(option_defs_[V4V6_RULE_OPTION_SPACE], V4V6_RULE_OPTION_DEFINITIONS,
  940                     V4V6_RULE_OPTION_DEFINITIONS_SIZE);
  941     initOptionSpace(option_defs_[V4V6_BIND_OPTION_SPACE], V4V6_BIND_OPTION_DEFINITIONS,
  942                     V4V6_BIND_OPTION_DEFINITIONS_SIZE);
  943 }
  944 
  945 void
  946 LibDHCP::initLastResortOptionDefs() {
  947     initOptionSpace(lastresort_defs_, LAST_RESORT_V4_OPTION_DEFINITIONS,
  948                     LAST_RESORT_V4_OPTION_DEFINITIONS_SIZE);
  949 }
  950 
  951 void
  952 LibDHCP::initVendorOptsDocsis4() {
  953     initOptionSpace(vendor4_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V4_DEFS,
  954                     DOCSIS3_V4_DEFS_SIZE);
  955 }
  956 
  957 void
  958 LibDHCP::initVendorOptsDocsis6() {
  959     initOptionSpace(vendor6_defs_[VENDOR_ID_CABLE_LABS], DOCSIS3_V6_DEFS,
  960                     DOCSIS3_V6_DEFS_SIZE);
  961 }
  962 
  963 void
  964 LibDHCP::initVendorOptsIsc6() {
  965     initOptionSpace(vendor6_defs_[ENTERPRISE_ID_ISC], ISC_V6_OPTION_DEFINITIONS,
  966                     ISC_V6_OPTION_DEFINITIONS_SIZE);
  967 }
  968 
  969 uint32_t
  970 LibDHCP::optionSpaceToVendorId(const std::string& option_space) {
  971     // 8 is a minimal length of "vendor-X" format
  972     if ((option_space.size() < 8) || (option_space.substr(0,7) != "vendor-")) {
  973         return (0);
  974     }
  975 
  976     int64_t check;
  977     try {
  978         // text after "vendor-", supposedly numbers only
  979         std::string x = option_space.substr(7);
  980 
  981         check = boost::lexical_cast<int64_t>(x);
  982 
  983     } catch (const boost::bad_lexical_cast &) {
  984         return (0);
  985     }
  986 
  987     if ((check < 0) || (check > std::numeric_limits<uint32_t>::max())) {
  988         return (0);
  989     }
  990 
  991     // value is small enough to fit
  992     return (static_cast<uint32_t>(check));
  993 }
  994 
  995 void initOptionSpace(OptionDefContainerPtr& defs,
  996                      const OptionDefParams* params,
  997                      size_t params_size) {
  998     // Container holding vendor options is typically not initialized, as it
  999     // is held in map of null pointers. We need to initialize here in this
 1000     // case.
 1001     if (!defs) {
 1002         defs.reset(new OptionDefContainer());
 1003 
 1004     } else {
 1005         defs->clear();
 1006     }
 1007 
 1008     for (size_t i = 0; i < params_size; ++i) {
 1009         std::string encapsulates(params[i].encapsulates);
 1010         if (!encapsulates.empty() && params[i].array) {
 1011             isc_throw(isc::BadValue, "invalid standard option definition: "
 1012                       << "option with code '" << params[i].code
 1013                       << "' may not encapsulate option space '"
 1014                       << encapsulates << "' because the definition"
 1015                       << " indicates that this option comprises an array"
 1016                       << " of values");
 1017         }
 1018 
 1019         // Depending whether an option encapsulates an option space or not
 1020         // we pick different constructor to create an instance of the option
 1021         // definition.
 1022         OptionDefinitionPtr definition;
 1023         if (encapsulates.empty()) {
 1024             // Option does not encapsulate any option space.
 1025             definition.reset(new OptionDefinition(params[i].name,
 1026                                                   params[i].code,
 1027                                                   params[i].type,
 1028                                                   params[i].array));
 1029         } else {
 1030             // Option does encapsulate an option space.
 1031             definition.reset(new OptionDefinition(params[i].name,
 1032                                                   params[i].code,
 1033                                                   params[i].type,
 1034                                                   params[i].encapsulates));
 1035 
 1036         }
 1037 
 1038         for (size_t rec = 0; rec < params[i].records_size; ++rec) {
 1039             definition->addRecordField(params[i].records[rec]);
 1040         }
 1041 
 1042         try {
 1043             definition->validate();
 1044         } catch (const isc::Exception&) {
 1045             // This is unlikely event that validation fails and may
 1046             // be only caused by programming error. To guarantee the
 1047             // data consistency we clear all option definitions that
 1048             // have been added so far and pass the exception forward.
 1049             defs->clear();
 1050             throw;
 1051         }
 1052 
 1053         // option_defs is a multi-index container with no unique indexes
 1054         // so push_back can't fail).
 1055         static_cast<void>(defs->push_back(definition));
 1056     }
 1057 }