Kea  2.3.3-git
option_definition.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2022 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/option4_addrlst.h>
13 #include <dhcp/option6_addrlst.h>
15 #include <dhcp/option6_ia.h>
16 #include <dhcp/option6_iaaddr.h>
17 #include <dhcp/option6_iaprefix.h>
18 #include <dhcp/option6_pdexclude.h>
20 #include <dhcp/option_custom.h>
21 #include <dhcp/option_definition.h>
22 #include <dhcp/option_int.h>
23 #include <dhcp/option_int_array.h>
25 #include <dhcp/option_string.h>
26 #include <dhcp/option_vendor.h>
28 #include <util/encode/hex.h>
29 #include <dns/labelsequence.h>
30 #include <dns/name.h>
31 #include <util/strutil.h>
32 #include <boost/algorithm/string/classification.hpp>
33 #include <boost/algorithm/string/predicate.hpp>
34 #include <boost/dynamic_bitset.hpp>
35 #include <boost/make_shared.hpp>
36 #include <sstream>
37 
38 using namespace std;
39 using namespace isc::util;
40 
41 namespace isc {
42 namespace dhcp {
43 
44 OptionDefinition::OptionDefinition(const std::string& name,
45  const uint16_t code,
46  const std::string& space,
47  const std::string& type,
48  const bool array_type /* = false */)
49  : name_(name),
50  code_(code),
51  type_(OPT_UNKNOWN_TYPE),
52  array_type_(array_type),
53  encapsulated_space_(""),
54  record_fields_(),
55  user_context_(),
56  option_space_name_(space) {
57  // Data type is held as enum value by this class.
58  // Use the provided option type string to get the
59  // corresponding enum value.
60  type_ = OptionDataTypeUtil::getDataType(type);
61 }
62 
63 OptionDefinition::OptionDefinition(const std::string& name,
64  const uint16_t code,
65  const std::string& space,
66  const OptionDataType type,
67  const bool array_type /* = false */)
68  : name_(name),
69  code_(code),
70  type_(type),
71  array_type_(array_type),
72  encapsulated_space_(""),
73  option_space_name_(space){
74 }
75 
76 OptionDefinition::OptionDefinition(const std::string& name,
77  const uint16_t code,
78  const std::string& space,
79  const std::string& type,
80  const char* encapsulated_space)
81  : name_(name),
82  code_(code),
83  // Data type is held as enum value by this class.
84  // Use the provided option type string to get the
85  // corresponding enum value.
86  type_(OptionDataTypeUtil::getDataType(type)),
87  array_type_(false),
88  encapsulated_space_(encapsulated_space),
89  record_fields_(),
90  user_context_(),
91  option_space_name_(space) {
92 }
93 
94 OptionDefinition::OptionDefinition(const std::string& name,
95  const uint16_t code,
96  const std::string& space,
97  const OptionDataType type,
98  const char* encapsulated_space)
99  : name_(name),
100  code_(code),
101  type_(type),
102  array_type_(false),
103  encapsulated_space_(encapsulated_space),
104  record_fields_(),
105  user_context_(),
106  option_space_name_(space) {
107 }
108 
110 OptionDefinition::create(const std::string& name,
111  const uint16_t code,
112  const std::string& space,
113  const std::string& type,
114  const bool array_type) {
115  return (boost::make_shared<OptionDefinition>(name, code, space, type, array_type));
116 }
117 
119 OptionDefinition::create(const std::string& name,
120  const uint16_t code,
121  const std::string& space,
122  const OptionDataType type,
123  const bool array_type) {
124  return (boost::make_shared<OptionDefinition>(name, code, space, type, array_type));
125 }
126 
128 OptionDefinition::create(const std::string& name,
129  const uint16_t code,
130  const std::string& space,
131  const std::string& type,
132  const char* encapsulated_space) {
133  return (boost::make_shared<OptionDefinition>(name, code, space, type, encapsulated_space));
134 }
135 
137 OptionDefinition::create(const std::string& name,
138  const uint16_t code,
139  const std::string& space,
140  const OptionDataType type,
141  const char* encapsulated_space) {
142  return (boost::make_shared<OptionDefinition>(name, code, space, type, encapsulated_space));
143 }
144 
145 bool
147  return (name_ == other.name_ &&
148  code_ == other.code_ &&
149  type_ == other.type_ &&
150  array_type_ == other.array_type_ &&
151  encapsulated_space_ == other.encapsulated_space_ &&
152  record_fields_ == other.record_fields_ &&
153  option_space_name_ == other.option_space_name_);
154 }
155 
156 void
157 OptionDefinition::addRecordField(const std::string& data_type_name) {
158  OptionDataType data_type = OptionDataTypeUtil::getDataType(data_type_name);
159  addRecordField(data_type);
160 }
161 
162 void
164  if (type_ != OPT_RECORD_TYPE) {
166  "'record' option type must be used instead of '"
168  << "' to add data fields to the record");
169  }
170  if (data_type >= OPT_RECORD_TYPE ||
171  data_type == OPT_ANY_ADDRESS_TYPE ||
172  data_type == OPT_EMPTY_TYPE) {
174  "attempted to add invalid data type '"
176  << "' to the record.");
177  }
178  record_fields_.push_back(data_type);
179 }
180 
181 OptionPtr
183  OptionBufferConstIter begin,
184  OptionBufferConstIter end) const {
185 
186  try {
187  // Some of the options are represented by the specialized classes derived
188  // from Option class (e.g. IA_NA, IAADDR). Although, they can be also
189  // represented by the generic classes, we want the object of the specialized
190  // type to be returned. Therefore, we first check that if we are dealing
191  // with such an option. If the instance is returned we just exit at this
192  // point. If not, we will search for a generic option type to return.
193  OptionPtr option = factorySpecialFormatOption(u, begin, end);
194  if (option) {
195  return (option);
196  }
197 
198  switch (type_) {
199  case OPT_EMPTY_TYPE:
200  if (getEncapsulatedSpace().empty()) {
201  return (factoryEmpty(u, type));
202  } else {
203  return (OptionPtr(new OptionCustom(*this, u, begin, end)));
204  }
205 
206  case OPT_BINARY_TYPE:
207  return (factoryGeneric(u, type, begin, end));
208 
209  case OPT_UINT8_TYPE:
210  return (array_type_ ?
211  factoryIntegerArray<uint8_t>(u, type, begin, end) :
212  factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
213  begin, end));
214 
215  case OPT_INT8_TYPE:
216  return (array_type_ ?
217  factoryIntegerArray<int8_t>(u, type, begin, end) :
218  factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
219  begin, end));
220 
221  case OPT_UINT16_TYPE:
222  return (array_type_ ?
223  factoryIntegerArray<uint16_t>(u, type, begin, end) :
224  factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
225  begin, end));
226 
227  case OPT_INT16_TYPE:
228  return (array_type_ ?
229  factoryIntegerArray<uint16_t>(u, type, begin, end) :
230  factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
231  begin, end));
232 
233  case OPT_UINT32_TYPE:
234  return (array_type_ ?
235  factoryIntegerArray<uint32_t>(u, type, begin, end) :
236  factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
237  begin, end));
238 
239  case OPT_INT32_TYPE:
240  return (array_type_ ?
241  factoryIntegerArray<uint32_t>(u, type, begin, end) :
242  factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
243  begin, end));
244 
246  // If definition specifies that an option is an array
247  // of IPv4 addresses we return an instance of specialized
248  // class (OptionAddrLst4). For non-array types there is no
249  // specialized class yet implemented so we drop through
250  // to return an instance of OptionCustom.
251  if (array_type_) {
252  return (factoryAddrList4(type, begin, end));
253  }
254  break;
255 
257  // Handle array type only here (see comments for
258  // OPT_IPV4_ADDRESS_TYPE case).
259  if (array_type_) {
260  return (factoryAddrList6(type, begin, end));
261  }
262  break;
263 
264  case OPT_STRING_TYPE:
265  return (OptionPtr(new OptionString(u, type, begin, end)));
266 
267  case OPT_TUPLE_TYPE:
268  // Handle array type only here (see comments for
269  // OPT_IPV4_ADDRESS_TYPE case).
270  if (array_type_) {
271  return (factoryOpaqueDataTuples(u, type, begin, end));
272  }
273  break;
274 
275  default:
276  // Do nothing. We will return generic option a few lines down.
277  ;
278  }
279  return (OptionPtr(new OptionCustom(*this, u, begin, end)));
280  } catch (const SkipThisOptionError&) {
281  // We need to throw this one as is.
282  throw;
283  } catch (const SkipRemainingOptionsError&) {
284  // We need to throw this one as is.
285  throw;
286  } catch (const Exception& ex) {
288  }
289 }
290 
291 OptionPtr
293  const OptionBuffer& buf) const {
294  return (optionFactory(u, type, buf.begin(), buf.end()));
295 }
296 
297 OptionPtr
299  const std::vector<std::string>& values) const {
300  OptionBuffer buf;
301  if (!array_type_ && type_ != OPT_RECORD_TYPE) {
302  if (values.empty()) {
303  if (type_ != OPT_EMPTY_TYPE) {
304  isc_throw(InvalidOptionValue, "no option value specified");
305  }
306  } else {
307  writeToBuffer(u, util::str::trim(values[0]), type_, buf);
308  }
309  } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
310  for (size_t i = 0; i < values.size(); ++i) {
311  writeToBuffer(u, util::str::trim(values[i]), type_, buf);
312  }
313  } else if (type_ == OPT_RECORD_TYPE) {
314  const RecordFieldsCollection& records = getRecordFields();
315  if (records.size() > values.size()) {
316  isc_throw(InvalidOptionValue, "number of data fields for the option"
317  << " type '" << getCode() << "' is greater than number"
318  << " of values provided.");
319  }
320  for (size_t i = 0; i < records.size(); ++i) {
321  writeToBuffer(u, util::str::trim(values[i]), records[i], buf);
322  }
323  if (array_type_ && (values.size() > records.size())) {
324  for (size_t i = records.size(); i < values.size(); ++i) {
325  writeToBuffer(u, util::str::trim(values[i]),
326  records.back(), buf);
327  }
328  }
329  }
330  return (optionFactory(u, type, buf.begin(), buf.end()));
331 }
332 
333 void
335 
336  using namespace boost::algorithm;
337 
338  std::ostringstream err_str;
339 
340  // Allowed characters in the option name are: lower or
341  // upper case letters, digits, underscores and hyphens.
342  // Empty option spaces are not allowed.
343  if (!all(name_, boost::is_from_range('a', 'z') ||
344  boost::is_from_range('A', 'Z') ||
345  boost::is_digit() ||
346  boost::is_any_of(std::string("-_"))) ||
347  name_.empty() ||
348  // Hyphens and underscores are not allowed at the beginning
349  // and at the end of the option name.
350  all(find_head(name_, 1), boost::is_any_of(std::string("-_"))) ||
351  all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
352  err_str << "invalid option name '" << name_ << "'";
353 
354  } else if (!OptionSpace::validateName(option_space_name_)) {
355  err_str << "invalid option space name: '"
356  << option_space_name_ << "'";
357 
358  } else if (!encapsulated_space_.empty() &&
359  !OptionSpace::validateName(encapsulated_space_)) {
360  err_str << "invalid encapsulated option space name: '"
361  << encapsulated_space_ << "'";
362 
363  } else if (type_ >= OPT_UNKNOWN_TYPE) {
364  // Option definition must be of a known type.
365  err_str << "option type " << type_ << " not supported.";
366 
367  } else if (type_ == OPT_RECORD_TYPE) {
368  // At least two data fields should be added to the record. Otherwise
369  // non-record option definition could be used.
370  if (getRecordFields().size() < 2) {
371  err_str << "invalid number of data fields: "
372  << getRecordFields().size()
373  << " specified for the option of type 'record'. Expected at"
374  << " least 2 fields.";
375 
376  } else {
377  // If the number of fields is valid we have to check if their order
378  // is valid too. We check that string or binary data fields are not
379  // laid before other fields. But we allow that they are laid at the
380  // end of an option.
381  const RecordFieldsCollection& fields = getRecordFields();
382  for (RecordFieldsConstIter it = fields.begin();
383  it != fields.end(); ++it) {
384  if (*it == OPT_STRING_TYPE &&
385  it < fields.end() - 1) {
386  err_str << "string data field can't be laid before data"
387  << " fields of other types.";
388  break;
389  }
390  if (*it == OPT_BINARY_TYPE &&
391  it < fields.end() - 1) {
392  err_str << "binary data field can't be laid before data"
393  << " fields of other types.";
394  break;
395  }
396  // Empty type is not allowed within a record.
397  if (*it == OPT_EMPTY_TYPE) {
398  err_str << "empty data type can't be stored as a field in"
399  << " an option record.";
400  break;
401  }
402  }
403  // If the array flag is set the last field is an array.
404  if (err_str.str().empty() && array_type_) {
405  const OptionDataType& last_type = fields.back();
406  if (last_type == OPT_STRING_TYPE) {
407  err_str
408  << "array of strings is not a valid option definition.";
409  } else if (last_type == OPT_BINARY_TYPE) {
410  err_str << "array of binary values is not a valid option "
411  "definition.";
412  }
413  // Empty type was already checked.
414  }
415  }
416 
417  } else if (array_type_) {
418  if (type_ == OPT_STRING_TYPE) {
419  // Array of strings is not allowed because there is no way
420  // to determine the size of a particular string and thus there
421  // it no way to tell when other data fields begin.
422  err_str << "array of strings is not a valid option definition.";
423  } else if (type_ == OPT_BINARY_TYPE) {
424  err_str << "array of binary values is not"
425  << " a valid option definition.";
426 
427  } else if (type_ == OPT_EMPTY_TYPE) {
428  err_str << "array of empty value is not"
429  << " a valid option definition.";
430 
431  }
432  }
433 
434  // Non-empty error string means that we have hit the error. We throw
435  // exception and include error string.
436  if (!err_str.str().empty()) {
437  isc_throw(MalformedOptionDefinition, err_str.str());
438  }
439 }
440 
441 bool
442 OptionDefinition::haveCompressedFqdnListFormat() const {
443  return (haveType(OPT_FQDN_TYPE) && getArrayType());
444 }
445 
446 bool
447 OptionDefinition::convertToBool(const std::string& value_str) const {
448  // Case-insensitive check that the input is one of: "true" or "false".
449  if (boost::iequals(value_str, "true")) {
450  return (true);
451 
452  } else if (boost::iequals(value_str, "false")) {
453  return (false);
454 
455  }
456 
457  // The input string is neither "true" nor "false", so let's check
458  // if it is not an integer wrapped in a string.
459  int result;
460  try {
461  result = boost::lexical_cast<int>(value_str);
462 
463  } catch (const boost::bad_lexical_cast&) {
464  isc_throw(BadDataTypeCast, "unable to covert the value '"
465  << value_str << "' to boolean data type");
466  }
467  // The boolean value is encoded in DHCP option as 0 or 1. Therefore,
468  // we only allow a user to specify those values for options which
469  // have boolean fields.
470  if (result != 1 && result != 0) {
471  isc_throw(BadDataTypeCast, "unable to convert '" << value_str
472  << "' to boolean data type");
473  }
474  return (static_cast<bool>(result));
475 }
476 
477 template<typename T>
478 T
479 OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
480  const {
481  // The lexical cast should be attempted when converting to an integer
482  // value only.
485  "must not convert '" << value_str
486  << "' to non-integer data type");
487  }
488 
489  // We use the 64-bit value here because it has wider range than
490  // any other type we use here and it allows to detect out of
491  // bounds conditions e.g. negative value specified for uintX_t
492  // data type. Obviously if the value exceeds the limits of int64
493  // this function will not handle that properly.
494  int64_t result = 0;
495  try {
496  result = boost::lexical_cast<int64_t>(value_str);
497 
498  } catch (const boost::bad_lexical_cast&) {
499  // boost::lexical_cast does not handle hexadecimal
500  // but stringstream does so do it the hard way.
501  std::stringstream ss;
502  ss << std::hex << value_str;
503  ss >> result;
504  if (ss.fail() || !ss.eof()) {
505  isc_throw(BadDataTypeCast, "unable to convert the value '"
506  << value_str << "' to integer data type");
507  }
508  }
509  // Perform range checks.
511  if (result > numeric_limits<T>::max() ||
512  result < numeric_limits<T>::min()) {
513  isc_throw(BadDataTypeCast, "unable to convert '"
514  << value_str << "' to numeric type. This value is "
515  "expected to be in the range of "
516  << +numeric_limits<T>::min() << ".."
517  << +numeric_limits<T>::max());
518  }
519  }
520  return (static_cast<T>(result));
521 }
522 
523 void
524 OptionDefinition::writeToBuffer(Option::Universe u,
525  const std::string& value,
526  const OptionDataType type,
527  OptionBuffer& buf) const {
528  // We are going to write value given by value argument to the buffer.
529  // The actual type of the value is given by second argument. Check
530  // this argument to determine how to write this value to the buffer.
531  switch (type) {
532  case OPT_BINARY_TYPE:
534  return;
535  case OPT_BOOLEAN_TYPE:
536  // We encode the true value as 1 and false as 0 on 8 bits.
537  // That way we actually waste 7 bits but it seems to be the
538  // simpler way to encode boolean.
539  // @todo Consider if any other encode methods can be used.
540  OptionDataTypeUtil::writeBool(convertToBool(value), buf);
541  return;
542  case OPT_INT8_TYPE:
543  OptionDataTypeUtil::writeInt<uint8_t>
544  (lexicalCastWithRangeCheck<int8_t>(value),
545  buf);
546  return;
547  case OPT_INT16_TYPE:
548  OptionDataTypeUtil::writeInt<uint16_t>
549  (lexicalCastWithRangeCheck<int16_t>(value),
550  buf);
551  return;
552  case OPT_INT32_TYPE:
553  OptionDataTypeUtil::writeInt<uint32_t>
554  (lexicalCastWithRangeCheck<int32_t>(value),
555  buf);
556  return;
557  case OPT_UINT8_TYPE:
558  OptionDataTypeUtil::writeInt<uint8_t>
559  (lexicalCastWithRangeCheck<uint8_t>(value),
560  buf);
561  return;
562  case OPT_UINT16_TYPE:
563  OptionDataTypeUtil::writeInt<uint16_t>
564  (lexicalCastWithRangeCheck<uint16_t>(value),
565  buf);
566  return;
567  case OPT_UINT32_TYPE:
568  OptionDataTypeUtil::writeInt<uint32_t>
569  (lexicalCastWithRangeCheck<uint32_t>(value),
570  buf);
571  return;
574  {
575  asiolink::IOAddress address(value);
576  if (!address.isV4() && !address.isV6()) {
577  isc_throw(BadDataTypeCast, "provided address "
578  << address
579  << " is not a valid IPv4 or IPv6 address.");
580  }
581  OptionDataTypeUtil::writeAddress(address, buf);
582  return;
583  }
585  {
586  std::string txt = value;
587 
588  // first let's remove any whitespaces
589  boost::erase_all(txt, " "); // space
590  boost::erase_all(txt, "\t"); // tabulation
591 
592  // Is this prefix/len notation?
593  size_t pos = txt.find("/");
594 
595  if (pos == string::npos) {
596  isc_throw(BadDataTypeCast, "provided address/prefix "
597  << value
598  << " is not valid.");
599  }
600 
601  std::string txt_address = txt.substr(0, pos);
602  isc::asiolink::IOAddress address = isc::asiolink::IOAddress(txt_address);
603  if (!address.isV6()) {
604  isc_throw(BadDataTypeCast, "provided address "
605  << txt_address
606  << " is not a valid IPv4 or IPv6 address.");
607  }
608 
609  std::string txt_prefix = txt.substr(pos + 1);
610  uint8_t len = 0;
611  try {
612  // start with the first character after /
613  len = lexicalCastWithRangeCheck<uint8_t>(txt_prefix);
614  } catch (...) {
615  isc_throw(BadDataTypeCast, "provided prefix "
616  << txt_prefix
617  << " is not valid.");
618  }
619 
620  // Write a prefix.
621  OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf);
622 
623  return;
624  }
625  case OPT_PSID_TYPE:
626  {
627  std::string txt = value;
628 
629  // first let's remove any whitespaces
630  boost::erase_all(txt, " "); // space
631  boost::erase_all(txt, "\t"); // tabulation
632 
633  // Is this prefix/len notation?
634  size_t pos = txt.find("/");
635 
636  if (pos == string::npos) {
637  isc_throw(BadDataTypeCast, "provided PSID value "
638  << value << " is not valid");
639  }
640 
641  const std::string txt_psid = txt.substr(0, pos);
642  const std::string txt_psid_len = txt.substr(pos + 1);
643 
644  uint16_t psid = 0;
645  uint8_t psid_len = 0;
646 
647  try {
648  psid = lexicalCastWithRangeCheck<uint16_t>(txt_psid);
649  } catch (...) {
650  isc_throw(BadDataTypeCast, "provided PSID "
651  << txt_psid << " is not valid");
652  }
653 
654  try {
655  psid_len = lexicalCastWithRangeCheck<uint8_t>(txt_psid_len);
656  } catch (...) {
657  isc_throw(BadDataTypeCast, "provided PSID length "
658  << txt_psid_len << " is not valid");
659  }
660 
661  OptionDataTypeUtil::writePsid(PSIDLen(psid_len), PSID(psid), buf);
662  return;
663  }
664  case OPT_STRING_TYPE:
666  return;
667  case OPT_FQDN_TYPE:
668  OptionDataTypeUtil::writeFqdn(value, buf);
669  return;
670  case OPT_TUPLE_TYPE:
671  {
674  OptionDataTypeUtil::writeTuple(value, lft, buf);
675  return;
676  }
677  default:
678  // We hit this point because invalid option data type has been specified
679  // This may be the case because 'empty' or 'record' data type has been
680  // specified. We don't throw exception here because it will be thrown
681  // at the exit point from this function.
682  ;
683  }
684  isc_throw(isc::BadValue, "attempt to write invalid option data field type"
685  " into the option buffer: " << type);
686 
687 }
688 
689 OptionPtr
691  OptionBufferConstIter begin,
692  OptionBufferConstIter end) {
693  boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin,
694  end));
695  return (option);
696 }
697 
698 OptionPtr
700  OptionBufferConstIter begin,
701  OptionBufferConstIter end) {
702  boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin,
703  end));
704  return (option);
705 }
706 
707 
708 OptionPtr
710  OptionPtr option(new Option(u, type));
711  return (option);
712 }
713 
714 OptionPtr
716  OptionBufferConstIter begin,
717  OptionBufferConstIter end) {
718  OptionPtr option(new Option(u, type, begin, end));
719  return (option);
720 }
721 
722 OptionPtr
724  OptionBufferConstIter begin,
725  OptionBufferConstIter end) {
726  if (std::distance(begin, end) < Option6IA::OPTION6_IA_LEN) {
727  isc_throw(isc::OutOfRange, "input option buffer has invalid size,"
728  << " expected at least " << Option6IA::OPTION6_IA_LEN
729  << " bytes");
730  }
731  boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
732  return (option);
733 }
734 
735 OptionPtr
737  OptionBufferConstIter begin,
738  OptionBufferConstIter end) {
739  if (std::distance(begin, end) < Option6IAAddr::OPTION6_IAADDR_LEN) {
741  "input option buffer has invalid size, expected at least "
742  << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
743  }
744  boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin,
745  end));
746  return (option);
747 }
748 
749 OptionPtr
751  OptionBufferConstIter begin,
752  OptionBufferConstIter end) {
753  if (std::distance(begin, end) < Option6IAPrefix::OPTION6_IAPREFIX_LEN) {
755  "input option buffer has invalid size, expected at least "
757  }
758  boost::shared_ptr<Option6IAPrefix> option(new Option6IAPrefix(type, begin,
759  end));
760  return (option);
761 }
762 
763 OptionPtr
765  uint16_t type,
766  OptionBufferConstIter begin,
767  OptionBufferConstIter end) {
768  boost::shared_ptr<OptionOpaqueDataTuples>
769  option(new OptionOpaqueDataTuples(u, type, begin, end));
770 
771  return (option);
772 }
773 
774 OptionPtr
775 OptionDefinition::factoryFqdnList(Option::Universe u,
776  OptionBufferConstIter begin,
777  OptionBufferConstIter end) const {
778 
779  const std::vector<uint8_t> data(begin, end);
780  if (data.empty()) {
781  isc_throw(InvalidOptionValue, "FQDN list option has invalid length of 0");
782  }
783  InputBuffer in_buf(static_cast<const void*>(&data[0]), data.size());
784  std::vector<uint8_t> out_buf;
785  out_buf.reserve(data.size());
786  while (in_buf.getPosition() < in_buf.getLength()) {
787  // Reuse readFqdn and writeFqdn code but on the whole buffer
788  // so the DNS name code handles compression for us.
789  try {
790  isc::dns::Name name(in_buf);
791  isc::dns::LabelSequence labels(name);
792  if (labels.getDataLength() > 0) {
793  size_t read_len = 0;
794  const uint8_t* label = labels.getData(&read_len);
795  out_buf.insert(out_buf.end(), label, label + read_len);
796  }
797  } catch (const isc::Exception& ex) {
799  }
800  }
801  return OptionPtr(new OptionCustom(*this, u,
802  out_buf.begin(), out_buf.end()));
803 }
804 
805 OptionPtr
806 OptionDefinition::factorySpecialFormatOption(Option::Universe u,
807  OptionBufferConstIter begin,
808  OptionBufferConstIter end) const {
809  if ((u == Option::V6) && haveSpace(DHCP6_OPTION_SPACE)) {
810  switch (getCode()) {
811  case D6O_IA_NA:
812  case D6O_IA_PD:
813  // Record of 3 uint32, no array.
814  return (factoryIA6(getCode(), begin, end));
815 
816  case D6O_IAADDR:
817  // Record of an IPv6 address followed by 2 uint32, no array.
818  return (factoryIAAddr6(getCode(), begin, end));
819 
820  case D6O_IAPREFIX:
821  // Record of 2 uint32, one uint8 and an IPv6 address, no array.
822  return (factoryIAPrefix6(getCode(), begin, end));
823 
824  case D6O_CLIENT_FQDN:
825  // Record of one uint8 and one FQDN, no array.
826  return (OptionPtr(new Option6ClientFqdn(begin, end)));
827 
828  case D6O_VENDOR_OPTS:
829  // Type uint32.
830  // Vendor-Specific Information (option code 17).
831  return (OptionPtr(new OptionVendor(Option::V6, begin, end)));
832 
833  case D6O_VENDOR_CLASS:
834  // Record of one uint32 and one string.
835  // Vendor Class (option code 16).
836  return (OptionPtr(new OptionVendorClass(Option::V6, begin, end)));
837 
838  case D6O_STATUS_CODE:
839  // Record of one uint16 and one string.
840  // Status Code (option code 13).
841  return (OptionPtr(new Option6StatusCode(begin, end)));
842 
843  case D6O_BOOTFILE_PARAM:
844  // Array of tuples.
845  // Bootfile params (option code 60).
846  return (factoryOpaqueDataTuples(Option::V6, getCode(), begin, end));
847 
848  case D6O_PD_EXCLUDE:
849  // Type IPv6 prefix.
850  // Prefix Exclude (option code 67),
851  return (OptionPtr(new Option6PDExclude(begin, end)));
852 
853  default:
854  break;
855  }
856  } else if ((u == Option::V4) && haveSpace(DHCP4_OPTION_SPACE)) {
857  switch (getCode()) {
858  case DHO_SERVICE_SCOPE:
859  // Record of a boolean and a string.
860  return (OptionPtr(new Option4SlpServiceScope(begin, end)));
861 
862  case DHO_FQDN:
863  // Record of 3 uint8 and a FQDN, no array.
864  return (OptionPtr(new Option4ClientFqdn(begin, end)));
865 
867  // Record of uint32 followed by binary.
868  // V-I Vendor Class (option code 124).
869  return (OptionPtr(new OptionVendorClass(Option::V4, begin, end)));
870 
872  // Type uint32.
873  // Vendor-Specific Information (option code 125).
874  return (OptionPtr(new OptionVendor(Option::V4, begin, end)));
875 
876  default:
877  break;
878  }
879  }
880  if ((u == Option::V4) && haveCompressedFqdnListFormat()) {
881  return (factoryFqdnList(Option::V4, begin, end));
882  }
883  return (OptionPtr());
884 }
885 
886 } // end of isc::dhcp namespace
887 } // end of isc namespace
The Name class encapsulates DNS names.
Definition: name.h:223
static const size_t OPTION6_IAADDR_LEN
length of the fixed part of the IAADDR option
bool equals(const OptionDefinition &other) const
Check if option definition is equal to other.
Encapsulates PSID length.
static OptionDefinitionPtr create(const std::string &name, const uint16_t code, const std::string &space, const std::string &type, const bool array_type=false)
Factory function creating an instance of the OptionDefinition.
Exception to be thrown when invalid option value has been specified for a particular option definitio...
Represents DHCPv6 Client FQDN Option (code 39).
Base class representing a DHCP option definition.
std::vector< OptionDataType > RecordFieldsCollection
List of fields within the record.
static OptionPtr factoryAddrList4(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory to create option with address list.
static void writePsid(const PSIDLen &psid_len, const PSID &psid, std::vector< uint8_t > &buf)
Append PSID length/value into a buffer.
static const std::string & getDataTypeName(const OptionDataType data_type)
Return option data type name from the data type enumerator.
Exception thrown during option unpacking This exception is thrown when an error has occurred...
Definition: option.h:52
DHCPv4 Option class for handling list of IPv4 addresses.
This class represents SLP Service Scope option (79) from RFC2610.
Utility class for option data types.
static bool validateName(const std::string &name)
Checks that the provided option space name is valid.
Definition: option_space.cc:26
static void writeBool(const bool value, std::vector< uint8_t > &buf)
Append boolean value into a buffer.
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
static void writeFqdn(const std::string &fqdn, std::vector< uint8_t > &buf, const bool downcase=false)
Append FQDN into a buffer.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
static void writePrefix(const PrefixLen &prefix_len, const asiolink::IOAddress &prefix, std::vector< uint8_t > &buf)
Append prefix into a buffer.
DHCPv6 option class representing Prefix Exclude Option (RFC 6603).
STL namespace.
DHCPv6 Option class for handling list of IPv6 addresses.
static OptionPtr factoryEmpty(Option::Universe u, uint16_t type)
Empty option factory.
OptionDefinition(const std::string &name, const uint16_t code, const std::string &space, const std::string &type, const bool array_type=false)
Constructor.
Represents DHCPv4 Client FQDN Option (code 81).
size_t getDataLength() const
Return the length of the wire-format data of this LabelSequence.
static OptionPtr factoryIA6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory for IA-type of option.
OptionPtr optionFactory(Option::Universe u, uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end) const
Option factory.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static OptionPtr factoryOpaqueDataTuples(Option::Universe u, uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory to create option with tuple list.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
static OptionPtr factoryIAPrefix6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory for IAPREFIX-type of option.
This class represents Status Code option (13) from RFC 8415.
static void writeTuple(const std::string &value, OpaqueDataTuple::LengthFieldType lengthfieldtype, std::vector< uint8_t > &buf)
Append length and string tuple to a buffer.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
static OptionDataType getDataType(const std::string &data_type)
Return option data type from its name.
OptionDataType
Data types of DHCP option fields.
Definition: edns.h:19
uint16_t getCode() const
Return option code.
static OptionPtr factoryAddrList6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory to create option with address list.
Encapsulates PSID value.
const uint8_t * getData(size_t *len) const
Return the wire-format data for this LabelSequence.
OptionBuffer::const_iterator OptionBufferConstIter
const_iterator for walking over OptionBuffer
Definition: option.h:30
LengthFieldType
Size of the length field in the tuple.
This is a base class for exceptions thrown from the DNS library module.
Defines the logger used by the top-level component of kea-lfc.
uint16_t code_
static OptionPtr factoryGeneric(Option::Universe u, uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory to create generic option.
static const size_t OPTION6_IAPREFIX_LEN
length of the fixed part of the IAPREFIX option
const RecordFieldsCollection & getRecordFields() const
Return list of record fields.
const Name & name_
Definition: dns/message.cc:693
#define DHCP6_OPTION_SPACE
void addRecordField(const std::string &data_type_name)
Adds data field to the record.
A generic exception that is thrown if a function is called in a prohibited way.
static void writeAddress(const asiolink::IOAddress &address, std::vector< uint8_t > &buf)
Append IPv4 or IPv6 address to a buffer.
#define DHCP4_OPTION_SPACE
global std option spaces
void validate() const
Check if the option definition is valid.
static void writeBinary(const std::string &hex_str, std::vector< uint8_t > &buf)
Append hex-encoded binary values to a buffer.
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
boost::shared_ptr< OptionDefinition > OptionDefinitionPtr
Pointer to option definition object.
Exception thrown during option unpacking This exception is thrown when an error has occurred unpackin...
Definition: option.h:67
std::vector< OptionDataType >::const_iterator RecordFieldsConstIter
Const iterator for record data fields.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
static const size_t OPTION6_IA_LEN
Length of IA_NA and IA_PD content.
Definition: option6_ia.h:26
Exception to be thrown when cast to the data type was unsuccessful.
Trait class for data types supported in DHCP option definitions.
static OptionPtr factoryIAAddr6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory for IAADDR-type of option.
This class encapsulates a collection of data tuples and could be used by multiple options...
bool getArrayType() const
Return array type indicator.
Class that represents IAPREFIX option in DHCPv6.
Option with defined data fields represented as buffers that can be accessed using data field index...
Definition: option_custom.h:32
static void writeString(const std::string &value, std::vector< uint8_t > &buf)
Write UTF8-encoded string into a buffer.
std::string getEncapsulatedSpace() const
Return name of the encapsulated option space.
Encapsulates prefix length.
Class which represents an option carrying a single string value.
Definition: option_string.h:28
This class represents vendor-specific information option.
Definition: option_vendor.h:30
Light-weight Accessor to Name data.
Definition: labelsequence.h:35
This class encapsulates DHCPv6 Vendor Class and DHCPv4 V-I Vendor Class options.
Exception to be thrown when option definition is invalid.