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