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