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