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