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