option_definition.cc 32.9 KB
Newer Older
1
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
2
//
3
4
5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7
#include <config.h>
8
#include <dhcp/dhcp4.h>
Marcin Siodelski's avatar
Marcin Siodelski committed
9
10
#include <dhcp/dhcp6.h>
#include <dhcp/option4_addrlst.h>
11
#include <dhcp/option4_client_fqdn.h>
Marcin Siodelski's avatar
Marcin Siodelski committed
12
#include <dhcp/option6_addrlst.h>
13
#include <dhcp/option6_client_fqdn.h>
Marcin Siodelski's avatar
Marcin Siodelski committed
14
15
#include <dhcp/option6_ia.h>
#include <dhcp/option6_iaaddr.h>
16
#include <dhcp/option6_iaprefix.h>
17
#include <dhcp/option6_pdexclude.h>
18
#include <dhcp/option6_status_code.h>
19
#include <dhcp/option_custom.h>
20
#include <dhcp/option_definition.h>
21
22
#include <dhcp/option_int.h>
#include <dhcp/option_int_array.h>
Shawn Routhier's avatar
Shawn Routhier committed
23
#include <dhcp/option_opaque_data_tuples.h>
24
#include <dhcp/option_string.h>
25
#include <dhcp/option_vendor.h>
26
#include <dhcp/option_vendor_class.h>
27
#include <util/encode/hex.h>
28
29
#include <dns/labelsequence.h>
#include <dns/name.h>
30
#include <util/strutil.h>
31
32
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
33
#include <boost/dynamic_bitset.hpp>
34
#include <sstream>
35
36
37
38
39
40
41

using namespace std;
using namespace isc::util;

namespace isc {
namespace dhcp {

42

43
44
45
46
47
48
OptionDefinition::OptionDefinition(const std::string& name,
                                 const uint16_t code,
                                 const std::string& type,
                                 const bool array_type /* = false */)
    : name_(name),
      code_(code),
49
      type_(OPT_UNKNOWN_TYPE),
50
51
      array_type_(array_type),
      encapsulated_space_("") {
52
53
54
    // Data type is held as enum value by this class.
    // Use the provided option type string to get the
    // corresponding enum value.
55
    type_ = OptionDataTypeUtil::getDataType(type);
56
57
58
59
}

OptionDefinition::OptionDefinition(const std::string& name,
                                   const uint16_t code,
60
                                   const OptionDataType type,
61
62
63
64
                                   const bool array_type /* = false */)
    : name_(name),
      code_(code),
      type_(type),
65
66
67
68
69
70
71
72
73
74
      array_type_(array_type),
      encapsulated_space_("") {
}

OptionDefinition::OptionDefinition(const std::string& name,
                                   const uint16_t code,
                                   const std::string& type,
                                   const char* encapsulated_space)
    : name_(name),
      code_(code),
75
76
77
78
      // Data type is held as enum value by this class.
      // Use the provided option type string to get the
      // corresponding enum value.
      type_(OptionDataTypeUtil::getDataType(type)),
79
80
81
82
83
84
85
86
87
88
89
90
91
      array_type_(false),
      encapsulated_space_(encapsulated_space) {
}

OptionDefinition::OptionDefinition(const std::string& name,
                                   const uint16_t code,
                                   const OptionDataType type,
                                   const char* encapsulated_space)
    : name_(name),
      code_(code),
      type_(type),
      array_type_(false),
      encapsulated_space_(encapsulated_space) {
92
93
}

94
95
96
97
98
99
100
101
102
103
bool
OptionDefinition::equals(const OptionDefinition& other) const {
    return (name_ == other.name_ &&
            code_ == other.code_ &&
            type_ == other.type_ &&
            array_type_ == other.array_type_ &&
            encapsulated_space_ == other.encapsulated_space_ &&
            record_fields_ == other.record_fields_);
}

104
105
void
OptionDefinition::addRecordField(const std::string& data_type_name) {
106
    OptionDataType data_type = OptionDataTypeUtil::getDataType(data_type_name);
107
108
109
110
    addRecordField(data_type);
}

void
111
112
OptionDefinition::addRecordField(const OptionDataType data_type) {
    if (type_ != OPT_RECORD_TYPE) {
113
114
115
        isc_throw(isc::InvalidOperation, "'record' option type must be used"
                  " to add data fields to the record");
    }
116
117
118
    if (data_type >= OPT_RECORD_TYPE ||
        data_type == OPT_ANY_ADDRESS_TYPE ||
        data_type == OPT_EMPTY_TYPE) {
119
120
        isc_throw(isc::BadValue,
                  "attempted to add invalid data type to the record.");
121
122
123
124
    }
    record_fields_.push_back(data_type);
}

125
126
127
OptionPtr
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                OptionBufferConstIter begin,
128
                                OptionBufferConstIter end) const {
129

130
    try {
131
132
133
134
135
136
        // Some of the options are represented by the specialized classes derived
        // from Option class (e.g. IA_NA, IAADDR). Although, they can be also
        // represented by the generic classes, we want the object of the specialized
        // type to be returned. Therefore, we first check that if we are dealing
        // with such an option. If the instance is returned we just exit at this
        // point. If not, we will search for a generic option type to return.
137
        OptionPtr option = factorySpecialFormatOption(u, begin, end);
138
139
140
141
        if (option) {
            return (option);
        }

142
143
        switch(type_) {
        case OPT_EMPTY_TYPE:
144
145
146
147
148
            if (getEncapsulatedSpace().empty()) {
                    return (factoryEmpty(u, type));
            } else {
                return (OptionPtr(new OptionCustom(*this, u, begin, end)));
            }
149
150

        case OPT_BINARY_TYPE:
151
            return (factoryGeneric(u, type, begin, end));
152

153
        case OPT_UINT8_TYPE:
154
155
            return (array_type_ ?
                    factoryIntegerArray<uint8_t>(u, type, begin, end) :
156
                    factoryInteger<uint8_t>(u, type, getEncapsulatedSpace(),
157
                                            begin, end));
158

159
        case OPT_INT8_TYPE:
160
161
            return (array_type_ ?
                    factoryIntegerArray<int8_t>(u, type, begin, end) :
162
                    factoryInteger<int8_t>(u, type, getEncapsulatedSpace(),
163
                                           begin, end));
164

165
        case OPT_UINT16_TYPE:
166
167
            return (array_type_ ?
                    factoryIntegerArray<uint16_t>(u, type, begin, end) :
168
                    factoryInteger<uint16_t>(u, type, getEncapsulatedSpace(),
169
                                             begin, end));
170

171
        case OPT_INT16_TYPE:
172
173
            return (array_type_ ?
                    factoryIntegerArray<uint16_t>(u, type, begin, end) :
174
                    factoryInteger<int16_t>(u, type, getEncapsulatedSpace(),
175
                                            begin, end));
176

177
        case OPT_UINT32_TYPE:
178
179
            return (array_type_ ?
                    factoryIntegerArray<uint32_t>(u, type, begin, end) :
180
                    factoryInteger<uint32_t>(u, type, getEncapsulatedSpace(),
181
                                             begin, end));
182

183
        case OPT_INT32_TYPE:
184
185
            return (array_type_ ?
                    factoryIntegerArray<uint32_t>(u, type, begin, end) :
186
                    factoryInteger<int32_t>(u, type, getEncapsulatedSpace(),
187
                                            begin, end));
188

189
        case OPT_IPV4_ADDRESS_TYPE:
190
191
192
193
194
            // If definition specifies that an option is an array
            // of IPv4 addresses we return an instance of specialized
            // class (OptionAddrLst4). For non-array types there is no
            // specialized class yet implemented so we drop through
            // to return an instance of OptionCustom.
195
            if (array_type_) {
196
                return (factoryAddrList4(type, begin, end));
197
            }
198
            break;
199

200
        case OPT_IPV6_ADDRESS_TYPE:
201
202
            // Handle array type only here (see comments for
            // OPT_IPV4_ADDRESS_TYPE case).
203
            if (array_type_) {
204
205
206
207
                return (factoryAddrList6(type, begin, end));
            }
            break;

208
209
210
        case OPT_STRING_TYPE:
            return (OptionPtr(new OptionString(u, type, begin, end)));

211
212
213
214
215
216
217
218
        case OPT_TUPLE_TYPE:
            // Handle array type only here (see comments for
            // OPT_IPV4_ADDRESS_TYPE case).
            if (array_type_) {
                return (factoryOpaqueDataTuples(u, type, begin, end));
            }
            break;

219
        default:
220
221
            // Do nothing. We will return generic option a few lines down.
            ;
222
        }
223
        return (OptionPtr(new OptionCustom(*this, u, begin, end)));
224
225
    } catch (const Exception& ex) {
        isc_throw(InvalidOptionValue, ex.what());
226
    }
227
228
229
230
}

OptionPtr
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
231
232
                                const OptionBuffer& buf) const {
    return (optionFactory(u, type, buf.begin(), buf.end()));
233
234
}

235
OptionPtr
236
237
238
239
OptionDefinition::optionFactory(Option::Universe u, uint16_t type,
                                const std::vector<std::string>& values) const {
    OptionBuffer buf;
    if (!array_type_ && type_ != OPT_RECORD_TYPE) {
240
        if (values.empty()) {
Francis Dupont's avatar
Francis Dupont committed
241
242
243
244
            if (type_ != OPT_EMPTY_TYPE) {
                isc_throw(InvalidOptionValue, "no option value specified");
            }
        } else {
245
            writeToBuffer(u, util::str::trim(values[0]), type_, buf);
246
247
248
        }
    } else if (array_type_ && type_ != OPT_RECORD_TYPE) {
        for (size_t i = 0; i < values.size(); ++i) {
249
            writeToBuffer(u, util::str::trim(values[i]), type_, buf);
250
251
252
253
254
        }
    } else if (type_ == OPT_RECORD_TYPE) {
        const RecordFieldsCollection& records = getRecordFields();
        if (records.size() > values.size()) {
            isc_throw(InvalidOptionValue, "number of data fields for the option"
255
256
                      << " type '" <<  getCode() << "' is greater than number"
                      << " of values provided.");
257
258
        }
        for (size_t i = 0; i < records.size(); ++i) {
259
            writeToBuffer(u, util::str::trim(values[i]), records[i], buf);
260
261
262
        }
    }
    return (optionFactory(u, type, buf.begin(), buf.end()));
263
264
}

265
266
void
OptionDefinition::validate() const {
267
268
269

    using namespace boost::algorithm;

270
    std::ostringstream err_str;
271
272
273
274
275
276
277

    // Allowed characters in the option name are: lower or
    // upper case letters, digits, underscores and hyphens.
    // Empty option spaces are not allowed.
    if (!all(name_, boost::is_from_range('a', 'z') ||
             boost::is_from_range('A', 'Z') ||
             boost::is_digit() ||
278
             boost::is_any_of(std::string("-_"))) ||
279
280
281
        name_.empty() ||
        // Hyphens and underscores are not allowed at the beginning
        // and at the end of the option name.
282
283
        all(find_head(name_, 1), boost::is_any_of(std::string("-_"))) ||
        all(find_tail(name_, 1), boost::is_any_of(std::string("-_")))) {
284
285
        err_str << "invalid option name '" << name_ << "'";

286
287
288
289
290
    } else if (!encapsulated_space_.empty() &&
               !OptionSpace::validateName(encapsulated_space_)) {
        err_str << "invalid encapsulated option space name: '"
                << encapsulated_space_ << "'";

291
292
    } else if (type_ >= OPT_UNKNOWN_TYPE) {
        // Option definition must be of a known type.
293
        err_str << "option type " << type_ << " not supported.";
294

295
296
297
298
299
300
301
    } else if (array_type_) {
        if (type_ == OPT_STRING_TYPE) {
            // Array of strings is not allowed because there is no way
            // to determine the size of a particular string and thus there
            // it no way to tell when other data fields begin.
            err_str << "array of strings is not a valid option definition.";
        } else if (type_ == OPT_BINARY_TYPE) {
302
303
            err_str << "array of binary values is not"
                    << " a valid option definition.";
304

305
        } else if (type_ == OPT_EMPTY_TYPE) {
306
307
            err_str << "array of empty value is not"
                    << " a valid option definition.";
308

309
        }
310

311
312
313
314
    } else if (type_ == OPT_RECORD_TYPE) {
        // At least two data fields should be added to the record. Otherwise
        // non-record option definition could be used.
        if (getRecordFields().size() < 2) {
315
316
            err_str << "invalid number of data fields: "
                    << getRecordFields().size()
317
318
                    << " specified for the option of type 'record'. Expected at"
                    << " least 2 fields.";
319

320
321
        } else {
            // If the number of fields is valid we have to check if their order
322
            // is valid too. We check that string or binary data fields are not
323
324
            // laid before other fields. But we allow that they are laid at the
            // end of an option.
325
326
327
328
329
            const RecordFieldsCollection& fields = getRecordFields();
            for (RecordFieldsConstIter it = fields.begin();
                 it != fields.end(); ++it) {
                if (*it == OPT_STRING_TYPE &&
                    it < fields.end() - 1) {
330
331
                    err_str << "string data field can't be laid before data"
                            << " fields of other types.";
332
333
                    break;
                }
334
335
                if (*it == OPT_BINARY_TYPE &&
                    it < fields.end() - 1) {
336
337
                    err_str << "binary data field can't be laid before data"
                            << " fields of other types.";
338
                }
339
                /// Empty type is not allowed within a record.
340
                if (*it == OPT_EMPTY_TYPE) {
341
342
                    err_str << "empty data type can't be stored as a field in"
                            << " an option record.";
343
344
                    break;
                }
345
346
347
            }
        }

Marcin Siodelski's avatar
Marcin Siodelski committed
348
    }
349
350
351
352
353

    // Non-empty error string means that we have hit the error. We throw
    // exception and include error string.
    if (!err_str.str().empty()) {
        isc_throw(MalformedOptionDefinition, err_str.str());
354
355
356
    }
}

357
bool
358
359
OptionDefinition::haveIAx6Format(OptionDataType first_type) const {
   return (haveType(OPT_RECORD_TYPE) &&
360
361
           record_fields_.size() == 3 &&
           record_fields_[0] == first_type &&
362
363
           record_fields_[1] == OPT_UINT32_TYPE &&
           record_fields_[2] == OPT_UINT32_TYPE);
364
365
366
367
}

bool
OptionDefinition::haveIA6Format() const {
368
369
370
371
372
373
    // Expect that IA_NA option format is defined as record.
    // Although it consists of 3 elements of the same (uint32)
    // type it can't be defined as array of uint32 elements because
    // arrays do not impose limitations on number of elements in
    // the array while this limitation is needed for IA_NA - need
    // exactly 3 elements.
374
    return (haveIAx6Format(OPT_UINT32_TYPE));
375
376
377
378
}

bool
OptionDefinition::haveIAAddr6Format() const {
379
    return (haveIAx6Format(OPT_IPV6_ADDRESS_TYPE));
380
381
}

382
383
384
385
386
387
388
389
390
391
bool
OptionDefinition::haveIAPrefix6Format() const {
    return (haveType(OPT_RECORD_TYPE) &&
            record_fields_.size() == 4 &&
            record_fields_[0] == OPT_UINT32_TYPE &&
            record_fields_[1] == OPT_UINT32_TYPE &&
            record_fields_[2] == OPT_UINT8_TYPE &&
            record_fields_[3] == OPT_IPV6_ADDRESS_TYPE);
}

392
393
394
395
396
397
398
399
400
401
bool
OptionDefinition::haveFqdn4Format() const {
    return (haveType(OPT_RECORD_TYPE) &&
            record_fields_.size() == 4 &&
            record_fields_[0] == OPT_UINT8_TYPE &&
            record_fields_[1] == OPT_UINT8_TYPE &&
            record_fields_[2] == OPT_UINT8_TYPE &&
            record_fields_[3] == OPT_FQDN_TYPE);
}

402
403
404
405
406
407
408
409
bool
OptionDefinition::haveClientFqdnFormat() const {
    return (haveType(OPT_RECORD_TYPE) &&
            (record_fields_.size() == 2) &&
            (record_fields_[0] == OPT_UINT8_TYPE) &&
            (record_fields_[1] == OPT_FQDN_TYPE));
}

410
411
412
413
414
415
416
417
418
419
bool
OptionDefinition::haveVendor4Format() const {
    return (true);
}

bool
OptionDefinition::haveVendor6Format() const {
    return  (getType() == OPT_UINT32_TYPE && !getEncapsulatedSpace().empty());
}

420
421
422
423
424
425
426
427
bool
OptionDefinition::haveVendorClass4Format() const {
    return (haveType(OPT_RECORD_TYPE) &&
            (record_fields_.size() == 2) &&
            (record_fields_[0] == OPT_UINT32_TYPE) &&
            (record_fields_[1] == OPT_BINARY_TYPE));
}

428
429
430
431
432
433
434
435
bool
OptionDefinition::haveVendorClass6Format() const {
    return (haveType(OPT_RECORD_TYPE) &&
            (record_fields_.size() == 2) &&
            (record_fields_[0] == OPT_UINT32_TYPE) &&
            (record_fields_[1] == OPT_BINARY_TYPE));
}

436
437
438
439
440
441
442
443
bool
OptionDefinition::haveStatusCodeFormat() const {
    return (haveType(OPT_RECORD_TYPE) &&
            (record_fields_.size() == 2) &&
            (record_fields_[0] == OPT_UINT16_TYPE) &&
            (record_fields_[1] == OPT_STRING_TYPE));
}

Shawn Routhier's avatar
Shawn Routhier committed
444
445
bool
OptionDefinition::haveOpaqueDataTuplesFormat() const {
446
    return (haveType(OPT_TUPLE_TYPE) && getArrayType());
Shawn Routhier's avatar
Shawn Routhier committed
447
448
}

449
450
451
452
453
bool
OptionDefinition::haveCompressedFqdnListFormat() const {
    return (haveType(OPT_FQDN_TYPE) && getArrayType());
}

454
455
bool
OptionDefinition::convertToBool(const std::string& value_str) const {
Andrei Pavel's avatar
Andrei Pavel committed
456
    // Case-insensitive check that the input is one of: "true" or "false".
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
    if (boost::iequals(value_str, "true")) {
        return (true);

    } else if (boost::iequals(value_str, "false")) {
        return (false);

    }

    // The input string is neither "true" nor "false", so let's check
    // if it is not an integer wrapped in a string.
    int result;
    try {
       result = boost::lexical_cast<int>(value_str);

    } catch (const boost::bad_lexical_cast&) {
        isc_throw(BadDataTypeCast, "unable to covert the value '"
                  << value_str << "' to boolean data type");
    }
    // The boolean value is encoded in DHCP option as 0 or 1. Therefore,
    // we only allow a user to specify those values for options which
    // have boolean fields.
    if (result != 1 && result != 0) {
        isc_throw(BadDataTypeCast, "unable to convert '" << value_str
                  << "' to boolean data type");
    }
    return (static_cast<bool>(result));
}

485
template<typename T>
486
487
488
T
OptionDefinition::lexicalCastWithRangeCheck(const std::string& value_str)
    const {
489
490
491
    // The lexical cast should be attempted when converting to an integer
    // value only.
    if (!OptionDataTypeTraits<T>::integer_type) {
492
        isc_throw(BadDataTypeCast,
493
494
                  "must not convert '" << value_str
                  << "' to non-integer data type");
495
    }
496

497
498
499
500
501
502
503
504
    // We use the 64-bit value here because it has wider range than
    // any other type we use here and it allows to detect out of
    // bounds conditions e.g. negative value specified for uintX_t
    // data type. Obviously if the value exceeds the limits of int64
    // this function will not handle that properly.
    int64_t result = 0;
    try {
        result = boost::lexical_cast<int64_t>(value_str);
505

506
    } catch (const boost::bad_lexical_cast&) {
507
508
509
510
511
512
513
514
515
        // boost::lexical_cast does not handle hexadecimal
        // but stringstream does so do it the hard way.
        std::stringstream ss;
        ss << std::hex << value_str;
        ss >> result;
        if (ss.fail() || !ss.eof()) {
            isc_throw(BadDataTypeCast, "unable to convert the value '"
                      << value_str << "' to integer data type");
        }
516
    }
517
    // Perform range checks.
518
519
520
    if (OptionDataTypeTraits<T>::integer_type) {
        if (result > numeric_limits<T>::max() ||
            result < numeric_limits<T>::min()) {
521
522
523
524
            isc_throw(BadDataTypeCast, "unable to convert '"
                      << value_str << "' to numeric type. This value is "
                      " expected to be in the range of "
                      << numeric_limits<T>::min()
525
                      << ".." << numeric_limits<T>::max());
526
527
528
529
530
531
        }
    }
    return (static_cast<T>(result));
}

void
532
533
OptionDefinition::writeToBuffer(Option::Universe u,
                                const std::string& value,
534
535
536
537
538
539
540
541
542
543
544
545
546
547
                                const OptionDataType type,
                                OptionBuffer& buf) const {
    // We are going to write value given by value argument to the buffer.
    // The actual type of the value is given by second argument. Check
    // this argument to determine how to write this value to the buffer.
    switch (type) {
    case OPT_BINARY_TYPE:
        OptionDataTypeUtil::writeBinary(value, buf);
        return;
    case OPT_BOOLEAN_TYPE:
        // We encode the true value as 1 and false as 0 on 8 bits.
        // That way we actually waste 7 bits but it seems to be the
        // simpler way to encode boolean.
        // @todo Consider if any other encode methods can be used.
548
        OptionDataTypeUtil::writeBool(convertToBool(value), buf);
549
550
        return;
    case OPT_INT8_TYPE:
551
552
        OptionDataTypeUtil::writeInt<uint8_t>
            (lexicalCastWithRangeCheck<int8_t>(value),
553
554
555
                                              buf);
        return;
    case OPT_INT16_TYPE:
556
557
        OptionDataTypeUtil::writeInt<uint16_t>
            (lexicalCastWithRangeCheck<int16_t>(value),
558
559
560
                                               buf);
        return;
    case OPT_INT32_TYPE:
561
562
        OptionDataTypeUtil::writeInt<uint32_t>
            (lexicalCastWithRangeCheck<int32_t>(value),
563
564
565
                                               buf);
        return;
    case OPT_UINT8_TYPE:
566
567
        OptionDataTypeUtil::writeInt<uint8_t>
            (lexicalCastWithRangeCheck<uint8_t>(value),
568
569
570
                                              buf);
        return;
    case OPT_UINT16_TYPE:
571
572
        OptionDataTypeUtil::writeInt<uint16_t>
            (lexicalCastWithRangeCheck<uint16_t>(value),
573
574
575
                                               buf);
        return;
    case OPT_UINT32_TYPE:
576
577
        OptionDataTypeUtil::writeInt<uint32_t>
            (lexicalCastWithRangeCheck<uint32_t>(value),
578
579
580
581
582
583
                                               buf);
        return;
    case OPT_IPV4_ADDRESS_TYPE:
    case OPT_IPV6_ADDRESS_TYPE:
        {
            asiolink::IOAddress address(value);
584
            if (!address.isV4() && !address.isV6()) {
585
                isc_throw(BadDataTypeCast, "provided address "
586
                          << address
587
                          << " is not a valid IPv4 or IPv6 address.");
588
589
590
591
            }
            OptionDataTypeUtil::writeAddress(address, buf);
            return;
        }
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
    case OPT_IPV6_PREFIX_TYPE:
        {
            std::string txt = value;

            // first let's remove any whitespaces
            boost::erase_all(txt, " "); // space
            boost::erase_all(txt, "\t"); // tabulation

            // Is this prefix/len notation?
            size_t pos = txt.find("/");

            if (pos == string::npos) {
                isc_throw(BadDataTypeCast, "provided address/prefix "
                          << value
                          << " is not valid.");
            }

            std::string txt_address = txt.substr(0, pos);
            isc::asiolink::IOAddress address = isc::asiolink::IOAddress(txt_address);
            if (!address.isV6()) {
                isc_throw(BadDataTypeCast, "provided address "
                          << txt_address
                          << " is not a valid IPv4 or IPv6 address.");
            }

            std::string txt_prefix = txt.substr(pos + 1);
            uint8_t len = 0;
            try {
                // start with the first character after /
                len = lexicalCastWithRangeCheck<uint8_t>(txt_prefix);
            } catch (...)  {
                isc_throw(BadDataTypeCast, "provided prefix "
                          << txt_prefix
                          << " is not valid.");
            }


            // Write a prefix.
            OptionDataTypeUtil::writePrefix(PrefixLen(len), address, buf);

            return;
    }
    case OPT_PSID_TYPE:
    {
        std::string txt = value;

        // first let's remove any whitespaces
        boost::erase_all(txt, " "); // space
        boost::erase_all(txt, "\t"); // tabulation

        // Is this prefix/len notation?
        size_t pos = txt.find("/");

        if (pos == string::npos) {
            isc_throw(BadDataTypeCast, "provided PSID value "
                      << value << " is not valid");
        }

        const std::string txt_psid = txt.substr(0, pos);
        const std::string txt_psid_len = txt.substr(pos + 1);

        uint16_t psid = 0;
        uint8_t psid_len = 0;

        try {
            psid = lexicalCastWithRangeCheck<uint16_t>(txt_psid);
        } catch (...)  {
            isc_throw(BadDataTypeCast, "provided PSID "
                      << txt_psid << " is not valid");
        }

        try {
            psid_len = lexicalCastWithRangeCheck<uint8_t>(txt_psid_len);
        } catch (...)  {
            isc_throw(BadDataTypeCast, "provided PSID length "
                      << txt_psid_len << " is not valid");
        }

        OptionDataTypeUtil::writePsid(PSIDLen(psid_len), PSID(psid), buf);
        return;
    }
673
674
675
676
    case OPT_STRING_TYPE:
        OptionDataTypeUtil::writeString(value, buf);
        return;
    case OPT_FQDN_TYPE:
677
678
        OptionDataTypeUtil::writeFqdn(value, buf);
        return;
679
680
681
682
683
684
685
    case OPT_TUPLE_TYPE:
    {
        OpaqueDataTuple::LengthFieldType lft = u == Option::V4 ?
            OpaqueDataTuple::LENGTH_1_BYTE : OpaqueDataTuple::LENGTH_2_BYTES;
        OptionDataTypeUtil::writeTuple(value, lft, buf);
        return;
    }
686
687
688
689
690
691
692
693
694
695
696
697
    default:
        // We hit this point because invalid option data type has been specified
        // This may be the case because 'empty' or 'record' data type has been
        // specified. We don't throw exception here because it will be thrown
        // at the exit point from this function.
        ;
    }
    isc_throw(isc::BadValue, "attempt to write invalid option data field type"
              " into the option buffer: " << type);

}

698
OptionPtr
699
OptionDefinition::factoryAddrList4(uint16_t type,
700
701
                                  OptionBufferConstIter begin,
                                  OptionBufferConstIter end) {
702
703
    boost::shared_ptr<Option4AddrLst> option(new Option4AddrLst(type, begin,
                                                                end));
704
705
706
707
    return (option);
}

OptionPtr
708
OptionDefinition::factoryAddrList6(uint16_t type,
709
710
                                   OptionBufferConstIter begin,
                                   OptionBufferConstIter end) {
711
712
    boost::shared_ptr<Option6AddrLst> option(new Option6AddrLst(type, begin,
                                                                end));
713
714
715
716
717
    return (option);
}


OptionPtr
718
OptionDefinition::factoryEmpty(Option::Universe u, uint16_t type) {
719
720
721
722
    OptionPtr option(new Option(u, type));
    return (option);
}

723
OptionPtr
724
725
726
727
OptionDefinition::factoryGeneric(Option::Universe u, uint16_t type,
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter end) {
    OptionPtr option(new Option(u, type, begin, end));
728
729
730
    return (option);
}

731
OptionPtr
732
OptionDefinition::factoryIA6(uint16_t type,
733
734
                             OptionBufferConstIter begin,
                             OptionBufferConstIter end) {
735
    if (std::distance(begin, end) < Option6IA::OPTION6_IA_LEN) {
736
737
738
        isc_throw(isc::OutOfRange, "input option buffer has invalid size,"
                  << " expected at least " << Option6IA::OPTION6_IA_LEN
                  << " bytes");
739
    }
740
    boost::shared_ptr<Option6IA> option(new Option6IA(type, begin, end));
741
742
743
744
    return (option);
}

OptionPtr
745
OptionDefinition::factoryIAAddr6(uint16_t type,
746
747
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter end) {
748
    if (std::distance(begin, end) < Option6IAAddr::OPTION6_IAADDR_LEN) {
749
750
751
        isc_throw(isc::OutOfRange,
                  "input option buffer has invalid size, expected at least "
                  << Option6IAAddr::OPTION6_IAADDR_LEN << " bytes");
752
    }
753
754
    boost::shared_ptr<Option6IAAddr> option(new Option6IAAddr(type, begin,
                                                              end));
755
756
757
    return (option);
}

758
759
760
761
762
763
764
765
766
767
768
769
770
771
OptionPtr
OptionDefinition::factoryIAPrefix6(uint16_t type,
                                 OptionBufferConstIter begin,
                                 OptionBufferConstIter end) {
    if (std::distance(begin, end) < Option6IAPrefix::OPTION6_IAPREFIX_LEN) {
        isc_throw(isc::OutOfRange,
                  "input option buffer has invalid size, expected at least "
                  << Option6IAPrefix::OPTION6_IAPREFIX_LEN << " bytes");
    }
    boost::shared_ptr<Option6IAPrefix> option(new Option6IAPrefix(type, begin,
                                                                  end));
    return (option);
}

772
773
774
775
776
777
778
779
780
781
782
OptionPtr
OptionDefinition::factoryOpaqueDataTuples(Option::Universe u,
                                          uint16_t type,
                                          OptionBufferConstIter begin,
                                          OptionBufferConstIter end) {
    boost::shared_ptr<OptionOpaqueDataTuples>
        option(new OptionOpaqueDataTuples(u, type, begin, end));

    return (option);
}

783
784
785
786
787
788
OptionPtr
OptionDefinition::factoryFqdnList(Option::Universe u,
                                  OptionBufferConstIter begin,
                                  OptionBufferConstIter end) const {
    
    const std::vector<uint8_t> data(begin, end);
Tomek Mrugalski's avatar
Tomek Mrugalski committed
789
790
791
    if (data.empty()) {
        isc_throw(InvalidOptionValue, "FQDN list option has invalid length of 0");
    }
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
    InputBuffer in_buf(static_cast<const void*>(&data[0]), data.size());
    std::vector<uint8_t> out_buf;
    out_buf.reserve(data.size());
    while (in_buf.getPosition() < in_buf.getLength()) {
        // Reuse readFqdn and writeFqdn code but on the whole buffer
        // so the DNS name code handles compression for us.
        try {
            isc::dns::Name name(in_buf);
            isc::dns::LabelSequence labels(name);
            if (labels.getDataLength() > 0) {
                size_t read_len = 0;
                const uint8_t* label = labels.getData(&read_len);
                out_buf.insert(out_buf.end(), label, label + read_len);
            }
        } catch (const isc::Exception& ex) {
            isc_throw(InvalidOptionValue, ex.what());
        }
    }
    return OptionPtr(new OptionCustom(*this, u,
                                      out_buf.begin(), out_buf.end()));
}

814
815
816
OptionPtr
OptionDefinition::factorySpecialFormatOption(Option::Universe u,
                                             OptionBufferConstIter begin,
817
                                             OptionBufferConstIter end) const {
818
819
820
821
822
823
824
825
826
827
828
829
830
831
    if (u == Option::V6) {
        if ((getCode() == D6O_IA_NA || getCode() == D6O_IA_PD) &&
            haveIA6Format()) {
            // Return Option6IA instance for IA_PD and IA_NA option
            // types only. We don't want to return Option6IA for other
            // options that comprise 3 UINT32 data fields because
            // Option6IA accessors' and modifiers' names are derived
            // from the IA_NA and IA_PD options' field names: IAID,
            // T1, T2. Using functions such as getIAID, getT1 etc. for
            // options other than IA_NA and IA_PD would be bad practice
            // and cause confusion.
            return (factoryIA6(getCode(), begin, end));

        } else if (getCode() == D6O_IAADDR && haveIAAddr6Format()) {
Josh Soref's avatar
Josh Soref committed
832
            // Return Option6IAAddr option instance for the IAADDR
833
834
835
            // option only for the same reasons as described in
            // for IA_NA and IA_PD above.
            return (factoryIAAddr6(getCode(), begin, end));
836
837
        } else if (getCode() == D6O_IAPREFIX && haveIAPrefix6Format()) {
            return (factoryIAPrefix6(getCode(), begin, end));
838
839
840
841
        } else if (getCode() == D6O_CLIENT_FQDN && haveClientFqdnFormat()) {
            // FQDN option requires special processing. Thus, there is
            // a specialized class to handle it.
            return (OptionPtr(new Option6ClientFqdn(begin, end)));
842
        } else if (getCode() == D6O_VENDOR_OPTS && haveVendor6Format()) {
843
            // Vendor-Specific Information (option code 17)
844
            return (OptionPtr(new OptionVendor(Option::V6, begin, end)));
845
        } else if (getCode() == D6O_VENDOR_CLASS && haveVendorClass6Format()) {
846
            // Vendor Class (option code 16).
847
            return (OptionPtr(new OptionVendorClass(Option::V6, begin, end)));
848
849
850
        } else if (getCode() == D6O_STATUS_CODE && haveStatusCodeFormat()) {
            // Status Code (option code 13)
            return (OptionPtr(new Option6StatusCode(begin, end)));
Shawn Routhier's avatar
Shawn Routhier committed
851
852
        } else if (getCode() == D6O_BOOTFILE_PARAM && haveOpaqueDataTuplesFormat()) {
            // Bootfile params (option code 60)
853
            return (factoryOpaqueDataTuples(Option::V6, getCode(), begin, end));
854
855
856
        } else if ((getCode() == D6O_PD_EXCLUDE) && haveType(OPT_IPV6_PREFIX_TYPE)) {
            // Prefix Exclude (option code 67)
            return (OptionPtr(new Option6PDExclude(begin, end)));
857
858
859
860
        }
    } else {
        if ((getCode() == DHO_FQDN) && haveFqdn4Format()) {
            return (OptionPtr(new Option4ClientFqdn(begin, end)));
861
        } else if (haveCompressedFqdnListFormat()) {
862
            return (factoryFqdnList(Option::V4, begin, end));
863
864
        } else if ((getCode() == DHO_VIVCO_SUBOPTIONS) &&
                   haveVendorClass4Format()) {
865
            // V-I Vendor Class (option code 124).
866
            return (OptionPtr(new OptionVendorClass(Option::V4, begin, end)));
867
        } else if (getCode() == DHO_VIVSO_SUBOPTIONS && haveVendor4Format()) {
868
            // Vendor-Specific Information (option code 125).
869
870
871
872
873
874
            return (OptionPtr(new OptionVendor(Option::V4, begin, end)));

        }
    }
    return (OptionPtr());
}
875

876
877
} // end of isc::dhcp namespace
} // end of isc namespace