Kea 2.5.8
option_definition.cc
Go to the documentation of this file.
1// Copyright (C) 2012-2024 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) const {
191
192 try {
193 // Some of the options are represented by the specialized classes derived
194 // from Option class (e.g. IA_NA, IAADDR). Although, they can be also
195 // represented by the generic classes, we want the object of the specialized
196 // type to be returned. Therefore, we first check that if we are dealing
197 // with such an option. If the instance is returned we just exit at this
198 // point. If not, we will search for a generic option type to return.
199 OptionPtr option = factorySpecialFormatOption(u, begin, end, convenient_notation);
200 if (option) {
201 return (option);
202 }
203
204 switch (type_) {
205 case OPT_EMPTY_TYPE:
206 if (getEncapsulatedSpace().empty()) {
207 return (factoryEmpty(u, type));
208 } else {
209 return (OptionPtr(new OptionCustom(*this, u, begin, end)));
210 }
211
212 case OPT_BINARY_TYPE:
213 // If this is Internal type, and it wasn't handled by factorySpecialFormatOption() before,
214 // let's treat it like normal Binary type.
216 return (factoryGeneric(u, type, begin, end));
217
218 case OPT_UINT8_TYPE:
219 return (array_type_ ?
220 factoryIntegerArray<uint8_t>(u, type, begin, end) :
221 factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
222 begin, end));
223
224 case OPT_INT8_TYPE:
225 return (array_type_ ?
226 factoryIntegerArray<int8_t>(u, type, begin, end) :
227 factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
228 begin, end));
229
230 case OPT_UINT16_TYPE:
231 return (array_type_ ?
232 factoryIntegerArray<uint16_t>(u, type, begin, end) :
233 factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
234 begin, end));
235
236 case OPT_INT16_TYPE:
237 return (array_type_ ?
238 factoryIntegerArray<uint16_t>(u, type, begin, end) :
239 factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
240 begin, end));
241
242 case OPT_UINT32_TYPE:
243 return (array_type_ ?
244 factoryIntegerArray<uint32_t>(u, type, begin, end) :
245 factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
246 begin, end));
247
248 case OPT_INT32_TYPE:
249 return (array_type_ ?
250 factoryIntegerArray<uint32_t>(u, type, begin, end) :
251 factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
252 begin, end));
253
255 // If definition specifies that an option is an array
256 // of IPv4 addresses we return an instance of specialized
257 // class (OptionAddrLst4). For non-array types there is no
258 // specialized class yet implemented so we drop through
259 // to return an instance of OptionCustom.
260 if (array_type_) {
261 return (factoryAddrList4(type, begin, end));
262 }
263 break;
264
266 // Handle array type only here (see comments for
267 // OPT_IPV4_ADDRESS_TYPE case).
268 if (array_type_) {
269 return (factoryAddrList6(type, begin, end));
270 }
271 break;
272
273 case OPT_STRING_TYPE:
274 return (OptionPtr(new OptionString(u, type, begin, end)));
275
276 case OPT_TUPLE_TYPE:
277 // Handle array type only here (see comments for
278 // OPT_IPV4_ADDRESS_TYPE case).
279 if (array_type_) {
280 return (factoryOpaqueDataTuples(u, type, begin, end));
281 }
282 break;
283
284 default:
285 // Do nothing. We will return generic option a few lines down.
286 ;
287 }
288 return (OptionPtr(new OptionCustom(*this, u, begin, end)));
289 } catch (const SkipThisOptionError&) {
290 // We need to throw this one as is.
291 throw;
292 } catch (const SkipRemainingOptionsError&) {
293 // We need to throw this one as is.
294 throw;
295 } catch (const Exception& ex) {
297 }
298}
299
302 const OptionBuffer& buf) const {
303 return (optionFactory(u, type, buf.begin(), buf.end()));
304}
305
308 const std::vector<std::string>& values) const {
309 OptionBuffer buf;
310 if (!array_type_ && type_ != OPT_RECORD_TYPE) {
311 if (values.empty()) {
312 if (type_ != OPT_EMPTY_TYPE) {
313 isc_throw(InvalidOptionValue, "no option value specified");
314 }
315 } else if (type_ == OPT_INTERNAL_TYPE) {
316 // If an Option of type Internal is configured using csv-format=true, it means it is
317 // convenient notation option config that needs special parsing. Let's treat it like
318 // String type. optionFactory() will be called with convenient_notation flag set to
319 // true, so that the factory will have a chance to handle it in a special way.
320
321 // At this stage any escape backslash chars were lost during last call of
322 // isc::util::str::tokens() inside of
323 // OptionDataParser::createOption(ConstElementPtr option_data), so we must
324 // restore them. Some INTERNAL options may use escaped delimiters, e.g. DNR options.
325 // Values are concatenated back to comma separated format and will be written to
326 // the OptionBuffer as one String type option.
327 std::ostringstream stream;
328 bool first = true;
329 for (auto val : values) {
330 boost::algorithm::replace_all(val, ",", "\\,");
331 if (first) {
332 first = false;
333 } else {
334 stream << ",";
335 }
336
337 stream << val;
338 }
339
340 writeToBuffer(u, stream.str(), OPT_STRING_TYPE, buf);
341 } else {
342 writeToBuffer(u, util::str::trim(values[0]), type_, buf);
343 }
344 } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
345 for (size_t i = 0; i < values.size(); ++i) {
346 writeToBuffer(u, util::str::trim(values[i]), type_, buf);
347 }
348 } else if (type_ == OPT_RECORD_TYPE) {
349 const RecordFieldsCollection& records = getRecordFields();
350 if (records.size() > values.size()) {
351 isc_throw(InvalidOptionValue, "number of data fields for the option"
352 << " type '" << getCode() << "' is greater than number"
353 << " of values provided.");
354 }
355 for (size_t i = 0; i < records.size(); ++i) {
356 writeToBuffer(u, util::str::trim(values[i]), records[i], buf);
357 }
358 if (array_type_ && (values.size() > records.size())) {
359 for (size_t i = records.size(); i < values.size(); ++i) {
360 writeToBuffer(u, util::str::trim(values[i]),
361 records.back(), buf);
362 }
363 }
364 }
365 return (optionFactory(u, type, buf.begin(), buf.end(), (type_ == OPT_INTERNAL_TYPE)));
366}
367
368void
370
371 using namespace boost::algorithm;
372
373 std::ostringstream err_str;
374
375 // Allowed characters in the option name are: lower or
376 // upper case letters, digits, underscores and hyphens.
377 // Empty option spaces are not allowed.
378 if (!all(name_, boost::is_from_range('a', 'z') ||
379 boost::is_from_range('A', 'Z') ||
380 boost::is_digit() ||
381 boost::is_any_of(std::string("-_"))) ||
382 name_.empty() ||
383 // Hyphens and underscores are not allowed at the beginning
384 // and at the end of the option name.
385 all(find_head(name_, 1), boost::is_any_of(std::string("-_"))) ||
386 all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
387 err_str << "invalid option name '" << name_ << "'";
388
389 } else if (!OptionSpace::validateName(option_space_name_)) {
390 err_str << "invalid option space name: '"
391 << option_space_name_ << "'";
392
393 } else if (!encapsulated_space_.empty() &&
394 !OptionSpace::validateName(encapsulated_space_)) {
395 err_str << "invalid encapsulated option space name: '"
396 << encapsulated_space_ << "'";
397
398 } else if (type_ >= OPT_UNKNOWN_TYPE) {
399 // Option definition must be of a known type.
400 err_str << "option type " << type_ << " not supported.";
401
402 } else if (type_ == OPT_RECORD_TYPE) {
403 // At least two data fields should be added to the record. Otherwise
404 // non-record option definition could be used.
405 if (getRecordFields().size() < 2) {
406 err_str << "invalid number of data fields: "
407 << getRecordFields().size()
408 << " specified for the option of type 'record'. Expected at"
409 << " least 2 fields.";
410
411 } else {
412 // If the number of fields is valid we have to check if their order
413 // is valid too. We check that string or binary data fields are not
414 // laid before other fields. But we allow that they are laid at the
415 // end of an option.
416 const RecordFieldsCollection& fields = getRecordFields();
417 for (RecordFieldsConstIter it = fields.begin();
418 it != fields.end(); ++it) {
419 if (*it == OPT_STRING_TYPE &&
420 it < fields.end() - 1) {
421 err_str << "string data field can't be laid before data"
422 << " fields of other types.";
423 break;
424 }
425 if (*it == OPT_BINARY_TYPE &&
426 it < fields.end() - 1) {
427 err_str << "binary data field can't be laid before data"
428 << " fields of other types.";
429 break;
430 }
431 // Empty type is not allowed within a record.
432 if (*it == OPT_EMPTY_TYPE) {
433 err_str << "empty data type can't be stored as a field in"
434 << " an option record.";
435 break;
436 }
437 // Internal type is not allowed within a record.
438 if (*it == OPT_INTERNAL_TYPE) {
439 err_str << "internal data type can't be stored as a field in"
440 << " an option record.";
441 break;
442 }
443 }
444 // If the array flag is set the last field is an array.
445 if (err_str.str().empty() && array_type_) {
446 const OptionDataType& last_type = fields.back();
447 if (last_type == OPT_STRING_TYPE) {
448 err_str
449 << "array of strings is not a valid option definition.";
450 } else if (last_type == OPT_BINARY_TYPE) {
451 err_str << "array of binary values is not a valid option "
452 "definition.";
453 }
454 // Empty type was already checked.
455 }
456 }
457
458 } else if (array_type_) {
459 if (type_ == OPT_STRING_TYPE) {
460 // Array of strings is not allowed because there is no way
461 // to determine the size of a particular string and thus there
462 // it no way to tell when other data fields begin.
463 err_str << "array of strings is not a valid option definition.";
464 } else if (type_ == OPT_BINARY_TYPE) {
465 err_str << "array of binary values is not"
466 << " a valid option definition.";
467
468 } else if (type_ == OPT_EMPTY_TYPE) {
469 err_str << "array of empty value is not"
470 << " a valid option definition.";
471
472 } else if (type_ == OPT_INTERNAL_TYPE) {
473 err_str << "array of internal type value is not"
474 << " a valid option definition.";
475
476 }
477 }
478
479 // Non-empty error string means that we have hit the error. We throw
480 // exception and include error string.
481 if (!err_str.str().empty()) {
482 isc_throw(MalformedOptionDefinition, err_str.str());
483 }
484}
485
486bool
487OptionDefinition::haveCompressedFqdnListFormat() const {
488 return (haveType(OPT_FQDN_TYPE) && getArrayType());
489}
490
491bool
492OptionDefinition::convertToBool(const std::string& value_str) const {
493 // Case-insensitive check that the input is one of: "true" or "false".
494 if (boost::iequals(value_str, "true")) {
495 return (true);
496
497 } else if (boost::iequals(value_str, "false")) {
498 return (false);
499
500 }
501
502 // The input string is neither "true" nor "false", so let's check
503 // if it is not an integer wrapped in a string.
504 int result;
505 try {
506 result = boost::lexical_cast<int>(value_str);
507
508 } catch (const boost::bad_lexical_cast&) {
509 isc_throw(BadDataTypeCast, "unable to covert the value '"
510 << value_str << "' to boolean data type");
511 }
512 // The boolean value is encoded in DHCP option as 0 or 1. Therefore,
513 // we only allow a user to specify those values for options which
514 // have boolean fields.
515 if (result != 1 && result != 0) {
516 isc_throw(BadDataTypeCast, "unable to convert '" << value_str
517 << "' to boolean data type");
518 }
519 return (static_cast<bool>(result));
520}
521
522template<typename T>
523T
524OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
525 const {
526 // The lexical cast should be attempted when converting to an integer
527 // value only.
529 isc_throw(BadDataTypeCast,
530 "must not convert '" << value_str
531 << "' to non-integer data type");
532 }
533
534 // We use the 64-bit value here because it has wider range than
535 // any other type we use here and it allows to detect out of
536 // bounds conditions e.g. negative value specified for uintX_t
537 // data type. Obviously if the value exceeds the limits of int64
538 // this function will not handle that properly.
539 int64_t result = 0;
540 try {
541 result = boost::lexical_cast<int64_t>(value_str);
542
543 } catch (const boost::bad_lexical_cast&) {
544 // boost::lexical_cast does not handle hexadecimal
545 // but stringstream does so do it the hard way.
546 std::stringstream ss;
547 ss << std::hex << value_str;
548 ss >> result;
549 if (ss.fail() || !ss.eof()) {
550 isc_throw(BadDataTypeCast, "unable to convert the value '"
551 << value_str << "' to integer data type");
552 }
553 }
554 // Perform range checks.
556 if (result > numeric_limits<T>::max() ||
557 result < numeric_limits<T>::min()) {
558 isc_throw(BadDataTypeCast, "unable to convert '"
559 << value_str << "' to numeric type. This value is "
560 "expected to be in the range of "
561 << +numeric_limits<T>::min() << ".."
562 << +numeric_limits<T>::max());
563 }
564 }
565 return (static_cast<T>(result));
566}
567
568void
569OptionDefinition::writeToBuffer(Option::Universe u,
570 const std::string& value,
571 const OptionDataType type,
572 OptionBuffer& buf) const {
573 // We are going to write value given by value argument to the buffer.
574 // The actual type of the value is given by second argument. Check
575 // this argument to determine how to write this value to the buffer.
576 switch (type) {
577 case OPT_BINARY_TYPE:
579 return;
580 case OPT_BOOLEAN_TYPE:
581 // We encode the true value as 1 and false as 0 on 8 bits.
582 // That way we actually waste 7 bits but it seems to be the
583 // simpler way to encode boolean.
584 // @todo Consider if any other encode methods can be used.
585 OptionDataTypeUtil::writeBool(convertToBool(value), buf);
586 return;
587 case OPT_INT8_TYPE:
588 OptionDataTypeUtil::writeInt<uint8_t>
589 (lexicalCastWithRangeCheck<int8_t>(value),
590 buf);
591 return;
592 case OPT_INT16_TYPE:
593 OptionDataTypeUtil::writeInt<uint16_t>
594 (lexicalCastWithRangeCheck<int16_t>(value),
595 buf);
596 return;
597 case OPT_INT32_TYPE:
598 OptionDataTypeUtil::writeInt<uint32_t>
599 (lexicalCastWithRangeCheck<int32_t>(value),
600 buf);
601 return;
602 case OPT_UINT8_TYPE:
603 OptionDataTypeUtil::writeInt<uint8_t>
604 (lexicalCastWithRangeCheck<uint8_t>(value),
605 buf);
606 return;
607 case OPT_UINT16_TYPE:
608 OptionDataTypeUtil::writeInt<uint16_t>
609 (lexicalCastWithRangeCheck<uint16_t>(value),
610 buf);
611 return;
612 case OPT_UINT32_TYPE:
613 OptionDataTypeUtil::writeInt<uint32_t>
614 (lexicalCastWithRangeCheck<uint32_t>(value),
615 buf);
616 return;
619 {
620 asiolink::IOAddress address(value);
621 if (!address.isV4() && !address.isV6()) {
622 isc_throw(BadDataTypeCast, "provided address "
623 << address
624 << " is not a valid IPv4 or IPv6 address.");
625 }
627 return;
628 }
630 {
631 std::string txt = value;
632
633 // first let's remove any whitespaces
634 boost::erase_all(txt, " "); // space
635 boost::erase_all(txt, "\t"); // tabulation
636
637 // Is this prefix/len notation?
638 size_t pos = txt.find("/");
639
640 if (pos == string::npos) {
641 isc_throw(BadDataTypeCast, "provided address/prefix "
642 << value
643 << " is not valid.");
644 }
645
646 std::string txt_address = txt.substr(0, pos);
648 if (!address.isV6()) {
649 isc_throw(BadDataTypeCast, "provided address "
650 << txt_address
651 << " is not a valid IPv4 or IPv6 address.");
652 }
653
654 std::string txt_prefix = txt.substr(pos + 1);
655 uint8_t len = 0;
656 try {
657 // start with the first character after /
658 len = lexicalCastWithRangeCheck<uint8_t>(txt_prefix);
659 } catch (...) {
660 isc_throw(BadDataTypeCast, "provided prefix "
661 << txt_prefix
662 << " is not valid.");
663 }
664
665 // Write a prefix.
666 OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf);
667
668 return;
669 }
670 case OPT_PSID_TYPE:
671 {
672 std::string txt = value;
673
674 // first let's remove any whitespaces
675 boost::erase_all(txt, " "); // space
676 boost::erase_all(txt, "\t"); // tabulation
677
678 // Is this prefix/len notation?
679 size_t pos = txt.find("/");
680
681 if (pos == string::npos) {
682 isc_throw(BadDataTypeCast, "provided PSID value "
683 << value << " is not valid");
684 }
685
686 const std::string txt_psid = txt.substr(0, pos);
687 const std::string txt_psid_len = txt.substr(pos + 1);
688
689 uint16_t psid = 0;
690 uint8_t psid_len = 0;
691
692 try {
693 psid = lexicalCastWithRangeCheck<uint16_t>(txt_psid);
694 } catch (...) {
695 isc_throw(BadDataTypeCast, "provided PSID "
696 << txt_psid << " is not valid");
697 }
698
699 try {
700 psid_len = lexicalCastWithRangeCheck<uint8_t>(txt_psid_len);
701 } catch (...) {
702 isc_throw(BadDataTypeCast, "provided PSID length "
703 << txt_psid_len << " is not valid");
704 }
705
706 OptionDataTypeUtil::writePsid(PSIDLen(psid_len), PSID(psid), buf);
707 return;
708 }
709 case OPT_STRING_TYPE:
711 return;
712 case OPT_FQDN_TYPE:
714 return;
715 case OPT_TUPLE_TYPE:
716 {
717 // In case of V4_SZTP_REDIRECT option #143, bootstrap-server-list is formatted
718 // as a list of tuples "uri-length;URI" where uri-length is coded on 2 octets,
719 // which is not typical for V4 Universe.
723 OptionDataTypeUtil::writeTuple(value, lft, buf);
724 return;
725 }
726 default:
727 // We hit this point because invalid option data type has been specified
728 // This may be the case because 'empty' or 'record' data type has been
729 // specified. We don't throw exception here because it will be thrown
730 // at the exit point from this function.
731 ;
732 }
733 isc_throw(isc::BadValue, "attempt to write invalid option data field type"
734 " into the option buffer: " << type);
735
736}
737
742 boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin,
743 end));
744 return (option);
745}
746
751 boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin,
752 end));
753 return (option);
754}
755
756
759 OptionPtr option(new Option(u, type));
760 return (option);
761}
762
767 OptionPtr option(new Option(u, type, begin, end));
768 return (option);
769}
770
775 if (std::distance(begin, end) < Option6IA::OPTION6_IA_LEN) {
776 isc_throw(isc::OutOfRange, "input option buffer has invalid size,"
777 << " expected at least " << Option6IA::OPTION6_IA_LEN
778 << " bytes");
779 }
780 boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
781 return (option);
782}
783
788 if (std::distance(begin, end) < Option6IAAddr::OPTION6_IAADDR_LEN) {
790 "input option buffer has invalid size, expected at least "
791 << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
792 }
793 boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin,
794 end));
795 return (option);
796}
797
802 if (std::distance(begin, end) < Option6IAPrefix::OPTION6_IAPREFIX_LEN) {
804 "input option buffer has invalid size, expected at least "
806 }
807 boost::shared_ptr<Option6IAPrefix> option(new Option6IAPrefix(type, begin,
808 end));
809 return (option);
810}
811
814 uint16_t type,
817 boost::shared_ptr<OptionOpaqueDataTuples>
818 option(new OptionOpaqueDataTuples(u, type, begin, end));
819
820 return (option);
821}
822
825 uint16_t type,
828 OpaqueDataTuple::LengthFieldType length_field_type) {
829 boost::shared_ptr<OptionOpaqueDataTuples>
830 option(new OptionOpaqueDataTuples(u, type, begin, end, length_field_type));
831
832 return (option);
833}
834
836OptionDefinition::factoryFqdnList(Option::Universe u,
838 OptionBufferConstIter end) const {
839
840 const std::vector<uint8_t> data(begin, end);
841 if (data.empty()) {
842 isc_throw(InvalidOptionValue, "FQDN list option has invalid length of 0");
843 }
844 InputBuffer in_buf(static_cast<const void*>(&data[0]), data.size());
845 std::vector<uint8_t> out_buf;
846 out_buf.reserve(data.size());
847 while (in_buf.getPosition() < in_buf.getLength()) {
848 // Reuse readFqdn and writeFqdn code but on the whole buffer
849 // so the DNS name code handles compression for us.
850 try {
851 isc::dns::Name name(in_buf);
852 isc::dns::LabelSequence labels(name);
853 if (labels.getDataLength() > 0) {
854 size_t read_len = 0;
855 const uint8_t* label = labels.getData(&read_len);
856 out_buf.insert(out_buf.end(), label, label + read_len);
857 }
858 } catch (const isc::Exception& ex) {
859 isc_throw(InvalidOptionValue, ex.what());
860 }
861 }
862 return OptionPtr(new OptionCustom(*this, u,
863 out_buf.begin(), out_buf.end()));
864}
865
867OptionDefinition::factorySpecialFormatOption(Option::Universe u,
870 bool convenient_notation) const {
871 if ((u == Option::V6) && haveSpace(DHCP6_OPTION_SPACE)) {
872 switch (getCode()) {
873 case D6O_IA_NA:
874 case D6O_IA_PD:
875 // Record of 3 uint32, no array.
876 return (factoryIA6(getCode(), begin, end));
877
878 case D6O_IAADDR:
879 // Record of an IPv6 address followed by 2 uint32, no array.
880 return (factoryIAAddr6(getCode(), begin, end));
881
882 case D6O_IAPREFIX:
883 // Record of 2 uint32, one uint8 and an IPv6 address, no array.
884 return (factoryIAPrefix6(getCode(), begin, end));
885
886 case D6O_CLIENT_FQDN:
887 // Record of one uint8 and one FQDN, no array.
888 return (OptionPtr(new Option6ClientFqdn(begin, end)));
889
890 case D6O_VENDOR_OPTS:
891 // Type uint32.
892 // Vendor-Specific Information (option code 17).
893 return (OptionPtr(new OptionVendor(Option::V6, begin, end)));
894
895 case D6O_VENDOR_CLASS:
896 // Record of one uint32 and one string.
897 // Vendor Class (option code 16).
898 return (OptionPtr(new OptionVendorClass(Option::V6, begin, end)));
899
900 case D6O_STATUS_CODE:
901 // Record of one uint16 and one string.
902 // Status Code (option code 13).
903 return (OptionPtr(new Option6StatusCode(begin, end)));
904
906 // Array of tuples.
907 // Bootfile params (option code 60).
908 return (factoryOpaqueDataTuples(Option::V6, getCode(), begin, end));
909
910 case D6O_PD_EXCLUDE:
911 // Type IPv6 prefix.
912 // Prefix Exclude (option code 67),
913 return (OptionPtr(new Option6PDExclude(begin, end)));
914
915 case D6O_V6_DNR:
916 return (OptionPtr(new Option6Dnr(begin, end, convenient_notation)));
917
918 default:
919 break;
920 }
921 } else if ((u == Option::V4) && haveSpace(DHCP4_OPTION_SPACE)) {
922 switch (getCode()) {
924 // Record of a boolean and a string.
925 return (OptionPtr(new Option4SlpServiceScope(begin, end)));
926
927 case DHO_FQDN:
928 // Record of 3 uint8 and a FQDN, no array.
929 return (OptionPtr(new Option4ClientFqdn(begin, end)));
930
932 return (OptionPtr(new OptionClasslessStaticRoute(begin, end, convenient_notation)));
933
935 // Record of uint32 followed by binary.
936 // V-I Vendor Class (option code 124).
937 return (OptionPtr(new OptionVendorClass(Option::V4, begin, end)));
938
940 // Type uint32.
941 // Vendor-Specific Information (option code 125).
942 return (OptionPtr(new OptionVendor(Option::V4, begin, end)));
943
945 // Array of tuples.
946 // DHCPv4 SZTP Redirect Option (option code 143).
948
949 case DHO_V4_DNR:
950 return (OptionPtr(new Option4Dnr(begin, end, convenient_notation)));
951
952 default:
953 break;
954 }
955 }
956 if ((u == Option::V4) && haveCompressedFqdnListFormat()) {
957 return (factoryFqdnList(Option::V4, begin, end));
958 }
959 return (OptionPtr());
960}
961
962} // end of isc::dhcp namespace
963} // 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.
Definition: option_custom.h:31
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 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.
Base class representing a DHCP option definition.
uint16_t getCode() const
Return option code.
static OptionPtr factoryEmpty(Option::Universe u, uint16_t type)
Empty option factory.
static OptionPtr factoryIAPrefix6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory for IAPREFIX-type of option.
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 factoryAddrList6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory to create option with address list.
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.
OptionPtr optionFactory(Option::Universe u, uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end, bool convenient_notation=false) const
Option factory.
static OptionPtr factoryIAAddr6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
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.
static OptionPtr factoryIA6(uint16_t type, OptionBufferConstIter begin, OptionBufferConstIter end)
Factory for IA-type of option.
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.
Definition: option_space.cc:26
Class which represents an option carrying a single string value.
Definition: option_string.h:28
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:83
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.
Definition: labelsequence.h:35
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:82
@ 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:159
@ 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
const Name & name_
Definition: dns/message.cc:696
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
OptionDataType
Data types of DHCP option fields.
@ DHO_V4_SZTP_REDIRECT
Definition: dhcp4.h:200
@ DHO_SERVICE_SCOPE
Definition: dhcp4.h:148
@ DHO_CLASSLESS_STATIC_ROUTE
Definition: dhcp4.h:186
@ DHO_VIVCO_SUBOPTIONS
Definition: dhcp4.h:189
@ DHO_V4_DNR
Definition: dhcp4.h:218
@ DHO_FQDN
Definition: dhcp4.h:150
@ DHO_VIVSO_SUBOPTIONS
Definition: dhcp4.h:190
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
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.
uint16_t code_
#define DHCP4_OPTION_SPACE
global std option spaces
#define DHCP6_OPTION_SPACE