Kea 3.2.1-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.
Option4AddrLst(uint8_t type)
Constructor, creates an option with empty list of addresses.
Option4ClientFqdn(const uint8_t flags, const Rcode &rcode, const std::string &domain_name, const DomainNameType domain_name_type=FULL)
Constructor, creates option instance using flags and domain name.
Option4Dnr(OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false)
Constructor of the Option from on-wire data.
Option4SlpServiceScope(const bool mandatory_flag, const std::string &scope_list)
Constructor, used for options constructed (during transmission).
Option6AddrLst(uint16_t type, const AddressContainer &addrs)
Constructor used during option generation.
Option6ClientFqdn(const uint8_t flags, const std::string &domain_name, const DomainNameType domain_name_type=FULL)
Constructor, creates option instance using flags and domain name.
Option6Dnr(OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false)
Constructor of the Option from on-wire data.
static const size_t OPTION6_IAADDR_LEN
length of the fixed part of the IAADDR option
Option6IAAddr(uint16_t type, const isc::asiolink::IOAddress &addr, uint32_t preferred, uint32_t valid)
Constructor, used for options constructed (during transmission).
static const size_t OPTION6_IAPREFIX_LEN
length of the fixed part of the IAPREFIX option
Option6IAPrefix(uint16_t type, const isc::asiolink::IOAddress &addr, uint8_t prefix_length, uint32_t preferred, uint32_t valid)
Constructor, used for options constructed (during transmission).
static const size_t OPTION6_IA_LEN
Length of IA_NA and IA_PD content.
Definition option6_ia.h:26
Option6IA(uint16_t type, uint32_t iaid)
Ctor, used for constructed options, usually during transmission.
Definition option6_ia.cc:25
Option6PDExclude(const isc::asiolink::IOAddress &delegated_prefix, const uint8_t delegated_prefix_length, const isc::asiolink::IOAddress &excluded_prefix, const uint8_t excluded_prefix_length)
Constructor.
Option6StatusCode(const uint16_t status_code, const std::string &status_message)
Constructor, used for options constructed (during transmission).
OptionClasslessStaticRoute(OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false)
Constructor of the Option from data in the buffer.
OptionCustom(const OptionDefinition &def, Universe u)
Constructor, used for options to be sent.
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.
OptionOpaqueDataTuples(Option::Universe u, const uint16_t type, OpaqueDataTuple::LengthFieldType length_field_type=OpaqueDataTuple::LENGTH_EMPTY)
Constructor.
static bool validateName(const std::string &name)
Checks that the provided option space name is valid.
OptionString(const Option::Universe u, const uint16_t type, const std::string &value)
Constructor, used to create options to be sent.
OptionVendorClass(Option::Universe u, const uint32_t vendor_id)
Constructor.
OptionVendor(Option::Universe u, const uint32_t vendor_id)
Constructor.
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
Option(Universe u, uint16_t type)
ctor, used for options constructed, usually during transmission
Definition option.cc:39
PSIDLen()
Default constructor.
PSID()
Default constructor.
PrefixLen()
Default constructor.
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