1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
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
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
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
// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC")
//
// 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/.

#include <config.h>

#include <dhcp/option_data_types.h>
#include <dns/labelsequence.h>
#include <dns/name.h>
#include <util/io.h>
#include <util/str.h>
#include <util/encode/encode.h>
#include <algorithm><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <limits><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

using namespace isc::asiolink;

namespace {
/// @brief Bit mask used to compute PSID value.
///
/// The mask represents the useful bits of the PSID. The value 0 is a special
/// case because the RFC explicitly specifies that PSID value should be ignored
/// if psid_len is 0.
std::vector<uint16_t> psid_bitmask = { 0xffff,
    0x8000, 0xc000, 0xe000, 0xf000,
    0xf800, 0xfc00, 0xfe00, 0xff00,
    0xff80, 0xffc0, 0xffe0, 0xfff0,
    0xfff8, 0xfffc, 0xfffe, 0xffff
};
}

namespace isc {
namespace dhcp {

OptionDataTypeUtil::OptionDataTypeUtil() {
    data_types_["empty"] = OPT_EMPTY_TYPE;
    data_types_["binary"] = OPT_BINARY_TYPE;
    data_types_["boolean"] = OPT_BOOLEAN_TYPE;
    data_types_["int8"] = OPT_INT8_TYPE;
    data_types_["int16"] = OPT_INT16_TYPE;
    data_types_["int32"] = OPT_INT32_TYPE;
    data_types_["uint8"] = OPT_UINT8_TYPE;
    data_types_["uint16"] = OPT_UINT16_TYPE;
    data_types_["uint32"] = OPT_UINT32_TYPE;
    data_types_["ipv4-address"] = OPT_IPV4_ADDRESS_TYPE;
    data_types_["ipv6-address"] = OPT_IPV6_ADDRESS_TYPE;
    data_types_["ipv6-prefix"] = OPT_IPV6_PREFIX_TYPE;
    data_types_["psid"] = OPT_PSID_TYPE;
    data_types_["string"] = OPT_STRING_TYPE;
    data_types_["tuple"] = OPT_TUPLE_TYPE;
    data_types_["fqdn"] = OPT_FQDN_TYPE;
    data_types_["internal"] = OPT_INTERNAL_TYPE;
    data_types_["record"] = OPT_RECORD_TYPE;

    data_type_names_[OPT_EMPTY_TYPE] = "empty";
    data_type_names_[OPT_BINARY_TYPE] = "binary";
    data_type_names_[OPT_BOOLEAN_TYPE] = "boolean";
    data_type_names_[OPT_INT8_TYPE] = "int8";
    data_type_names_[OPT_INT16_TYPE] = "int16";
    data_type_names_[OPT_INT32_TYPE] = "int32";
    data_type_names_[OPT_UINT8_TYPE] = "uint8";
    data_type_names_[OPT_UINT16_TYPE] = "uint16";
    data_type_names_[OPT_UINT32_TYPE] = "uint32";
    data_type_names_[OPT_IPV4_ADDRESS_TYPE] = "ipv4-address";
    data_type_names_[OPT_IPV6_ADDRESS_TYPE] = "ipv6-address";
    data_type_names_[OPT_IPV6_PREFIX_TYPE] = "ipv6-prefix";
    data_type_names_[OPT_PSID_TYPE] = "psid";
    data_type_names_[OPT_STRING_TYPE] = "string";
    data_type_names_[OPT_TUPLE_TYPE] = "tuple";
    data_type_names_[OPT_FQDN_TYPE] = "fqdn";
    data_type_names_[OPT_INTERNAL_TYPE] = "internal";
    data_type_names_[OPT_RECORD_TYPE] = "record";
    // The "unknown" data type is declared here so as
    // it can be returned by reference by a getDataTypeName
    // function it no other type is suitable. Other than that
    // this is unused.
    data_type_names_[OPT_UNKNOWN_TYPE] = "unknown";
}

OptionDataType
OptionDataTypeUtil::getDataType(const std::string& data_type) {
    return (OptionDataTypeUtil::instance().getDataTypeImpl(data_type));
}

OptionDataType
OptionDataTypeUtil::getDataTypeImpl(const std::string& data_type) const {
    std::map<std::string, OptionDataType>::const_iterator data_type_it =
        data_types_.find(data_type);
    if (data_type_it != data_types_.end()) {
        return (data_type_it->second);
    }
    return (OPT_UNKNOWN_TYPE);
}

int
OptionDataTypeUtil::getDataTypeLen(const OptionDataType data_type) {
    switch (data_type) {
    case OPT_BOOLEAN_TYPE:
    case OPT_INT8_TYPE:
    case OPT_UINT8_TYPE:
        return (1);

    case OPT_INT16_TYPE:
    case OPT_UINT16_TYPE:
        return (2);

    case OPT_INT32_TYPE:
    case OPT_UINT32_TYPE:
        return (4);

    case OPT_IPV4_ADDRESS_TYPE:
        return (asiolink::V4ADDRESS_LEN);

    case OPT_IPV6_ADDRESS_TYPE:
        return (asiolink::V6ADDRESS_LEN);

    case OPT_PSID_TYPE:
        return (3);

    default:
        ;
    }
    return (0);
}

const std::string&
OptionDataTypeUtil::getDataTypeName(const OptionDataType data_type) {
    return (OptionDataTypeUtil::instance().getDataTypeNameImpl(data_type));
}

const std::string&
OptionDataTypeUtil::getDataTypeNameImpl(const OptionDataType data_type) const {
    std::map<OptionDataType, std::string>::const_iterator data_type_it =
        data_type_names_.find(data_type);
    if (data_type_it != data_type_names_.end()) {
        return (data_type_it->second);
    }
    return (data_type_names_.find(OPT_UNKNOWN_TYPE)->second);
}

OptionDataTypeUtil&
OptionDataTypeUtil::instance() {
    static OptionDataTypeUtil instance;
    return (instance);
}

asiolink::IOAddress
OptionDataTypeUtil::readAddress(const std::vector<uint8_t>& buf,
                                const short family) {
    using namespace isc::asiolink;
    if (family == AF_INET) {
        if (buf.size() < V4ADDRESS_LEN) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " IPv4 address. Invalid buffer size: " << buf.size());
        }
        return (IOAddress::fromBytes(AF_INET, &buf[0]));
    } else if (family == AF_INET6) {
        if (buf.size() < V6ADDRESS_LEN) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " IPv6 address. Invalid buffer size: " << buf.size());
        }
        return (IOAddress::fromBytes(AF_INET6, &buf[0]));
    } else {
        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                  << " IP address. Invalid family: " << family);
    }
}

void
OptionDataTypeUtil::writeAddress(const asiolink::IOAddress& address,
                                 std::vector<uint8_t>& buf) {
    const std::vector<uint8_t>& vec = address.toBytes();
    buf.insert(buf.end(), vec.begin(), vec.end());
}

void
OptionDataTypeUtil::writeBinary(const std::string& hex_str,
                                std::vector<uint8_t>& buf) {
    // Binary value means that the value is encoded as a string
    // of hexadecimal digits. We need to decode this string
    // to the binary format here.
    OptionBuffer binary;
    try {
        util::encode::decodeHex(hex_str, binary);
    } catch (const Exception& ex) {
        isc_throw(BadDataTypeCast, "unable to cast " << hex_str
                  << " to binary data type: " << ex.what());
    }
    // Decode was successful so append decoded binary value
    // to the buffer.
    buf.insert(buf.end(), binary.begin(), binary.end());
}

OpaqueDataTuple::LengthFieldType
OptionDataTypeUtil::getTupleLenFieldType(Option::Universe u) {
    if (u == Option::V4) {
        return (OpaqueDataTuple::LENGTH_1_BYTE);
    }
    return (OpaqueDataTuple::LENGTH_2_BYTES);
}

std::string
OptionDataTypeUtil::readTuple(const std::vector<uint8_t>& buf,
                              OpaqueDataTuple::LengthFieldType lengthfieldtype) {
    if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
        if (buf.size() < 1) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " tuple (length). Invalid buffer size: "
                      << buf.size());
        }
        uint8_t len = buf[0];
        if (buf.size() < 1 + len) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " tuple (length " << static_cast<unsigned>(len)
                      << "). Invalid buffer size: " << buf.size());
        }
        std::string value;
        value.resize(len);
        std::memcpy(&value[0], &buf[1], len);
        return (value);
    } else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
        if (buf.size() < 2) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " tuple (length). Invalid buffer size: "
                      << buf.size());
        }
        uint16_t len = isc::util::readUint16(&buf[0], 2);
        if (buf.size() < 2 + len) {
            isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                      << " tuple (length " << len
                      << "). Invalid buffer size: " << buf.size());
        }
        std::string value;
        value.resize(len);
        std::memcpy(&value[0], &buf[2], len);
        return (value);
    } else {
        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
                  << " tuple. Invalid length type field: "
                  << static_cast<unsigned>(lengthfieldtype));
    }
}

void
OptionDataTypeUtil::readTuple(const std::vector<uint8_t>& buf,
                              OpaqueDataTuple& tuple) {
    try {
        tuple.unpack(buf.begin(), buf.end());
    } catch (const OpaqueDataTupleError& ex) {
        isc_throw(BadDataTypeCast, ex.what());
    }
}

void
OptionDataTypeUtil::writeTuple(const std::string& value,
                               OpaqueDataTuple::LengthFieldType lengthfieldtype,
                               std::vector<uint8_t>& buf) {
    if (lengthfieldtype == OpaqueDataTuple::LENGTH_1_BYTE) {
        if (value.size() > std::numeric_limits<uint8_t>::max()) {
            isc_throw(BadDataTypeCast, "invalid tuple value (size "
                      << value.size() << " larger than "
                      << +std::numeric_limits<uint8_t>::max() << ")");
        }
        buf.push_back(static_cast<uint8_t>(value.size()));

    } else if (lengthfieldtype == OpaqueDataTuple::LENGTH_2_BYTES) {
        if (value.size() > std::numeric_limits<uint16_t>::max()) {
            isc_throw(BadDataTypeCast, "invalid tuple value (size "
                      << value.size() << " larger than "
                      << std::numeric_limits<uint16_t>::max() << ")");
        }
        buf.resize(buf.size() + 2);
        isc::util::writeUint16(static_cast<uint16_t>(value.size()),
                               &buf[buf.size() - 2], 2);
    } else {
        isc_throw(BadDataTypeCast, "unable to write data to the buffer as"
                  << " tuple. Invalid length type field: "
                  << static_cast<unsigned>(lengthfieldtype));
    }
    buf.insert(buf.end(), value.begin(), value.end());
}

void
OptionDataTypeUtil::writeTuple(const OpaqueDataTuple& tuple,
                               std::vector<uint8_t>& buf) {
    if (tuple.getLength() == 0) {
        isc_throw(BadDataTypeCast, "invalid empty tuple value");
    }
    if (tuple.getLengthFieldType() == OpaqueDataTuple::LENGTH_1_BYTE) {
        if (tuple.getLength() > std::numeric_limits<uint8_t>::max()) {
            isc_throw(BadDataTypeCast, "invalid tuple value (size "
                      << tuple.getLength() << " larger than "
                      << +std::numeric_limits<uint8_t>::max() << ")");
        }
        buf.push_back(static_cast<uint8_t>(tuple.getLength()));

    } else if (tuple.getLengthFieldType() == OpaqueDataTuple::LENGTH_2_BYTES) {
        if (tuple.getLength() > std::numeric_limits<uint16_t>::max()) {
            isc_throw(BadDataTypeCast, "invalid tuple value (size "
                      << tuple.getLength() << " larger than "
                      << std::numeric_limits<uint16_t>::max() << ")");
        }
        buf.resize(buf.size() + 2);
        isc::util::writeUint16(static_cast<uint16_t>(tuple.getLength()),
                               &buf[buf.size() - 2], 2);
    } else {
        isc_throw(BadDataTypeCast, "unable to write data to the buffer as"
                  << " tuple. Invalid length type field: "
                  << tuple.getLengthFieldType());
    }
    buf.insert(buf.end(), tuple.getData().begin(), tuple.getData().end());
}

bool
OptionDataTypeUtil::readBool(const std::vector<uint8_t>& buf) {
    if (buf.empty()) {
        isc_throw(BadDataTypeCast, "unable to read the buffer as boolean"
                  << " value. Invalid buffer size " << buf.size());
    }
    if (buf[0] == 1) {
        return (true);
    } else if (buf[0] == 0) {
        return (false);
    }
    isc_throw(BadDataTypeCast, "unable to read the buffer as boolean"
              << " value. Invalid value " << static_cast<int>(buf[0]));
}

void
OptionDataTypeUtil::writeBool(const bool value,
                              std::vector<uint8_t>& buf) {
    buf.push_back(static_cast<uint8_t>(value ? 1 : 0));
}

std::string
OptionDataTypeUtil::readFqdn(const std::vector<uint8_t>& buf) {
    // If buffer is empty emit an error.
    if (buf.empty()) {
        isc_throw(BadDataTypeCast, "unable to read FQDN from a buffer."
                  << " The buffer is empty.");
    }
    // Set up an InputBuffer so as we can use isc::dns::Name object to get the FQDN.
    isc::util::InputBuffer in_buf(static_cast<const void*>(&buf[0]), buf.size());
    try {
        // Try to create an object from the buffer. If exception is thrown
        // it means that the buffer doesn't hold a valid domain name (invalid
        // syntax).
        isc::dns::Name name(in_buf);
        return (name.toText());
    } catch (const isc::Exception& ex) {
        // Unable to convert the data in the buffer into FQDN.
        isc_throw(BadDataTypeCast, ex.what());
    }
}

void
OptionDataTypeUtil::writeFqdn(const std::string& fqdn,
                              std::vector<uint8_t>& buf,
                              bool downcase) {
    try {
        isc::dns::Name name(fqdn, downcase);
        isc::dns::LabelSequence labels(name);
        if (labels.getDataLength() > 0) {
            size_t read_len = 0;
            const uint8_t* data = labels.getData(&read_len);
            buf.insert(buf.end(), data, data + read_len);
        }
    } catch (const isc::Exception& ex) {
        isc_throw(BadDataTypeCast, ex.what());
    }
}

unsigned int
OptionDataTypeUtil::getLabelCount(const std::string& text_name) {
    // The isc::dns::Name class doesn't accept empty names. However, in some
    // cases we may be dealing with empty names (e.g. sent by the DHCP clients).
    // Empty names should not be sent as hostnames but if they are, for some
    // reason, we don't want to throw an exception from this function. We
    // rather want to signal empty name by returning 0 number of labels.
    if (text_name.empty()) {
        return (0);
    }
    try {
        isc::dns::Name name(text_name);
        return (name.getLabelCount());
    } catch (const isc::Exception& ex) {
        isc_throw(BadDataTypeCast, ex.what());
    }
}

PrefixTuple
OptionDataTypeUtil::readPrefix(const std::vector<uint8_t>& buf) {
    // Prefix typically consists of the prefix length and the
    // actual value. If prefix length is 0, the buffer length should
    // be at least 1 byte to hold this length value.
    if (buf.empty()) {
        isc_throw(BadDataTypeCast, "unable to read prefix length from "
                  "a truncated buffer");
    }

    // Surround everything with try-catch to unify exceptions being
    // thrown by various functions and constructors.
    try {
        // Try to create PrefixLen object from the prefix length held
        // in the buffer. This may cause an exception if the length is
        // invalid (greater than 128).
        PrefixLen prefix_len(buf.at(0));

        // Convert prefix length to bytes, because we operate on bytes,
        // rather than bits.
        uint8_t prefix_len_bytes = (prefix_len.asUint8() / 8);
        // Check if we need to zero pad any bits. This is the case when
        // the prefix length is not divisible by 8 (bits per byte). The
        // calculations below may require some explanations. We first
        // perform prefix_len % 8 to get the number of useful bits beyond
        // the current prefix_len_bytes value. By substracting it from 8
        // we get the number of zero padded bits, but with the special
        // case of 8 when the result of substraction is 0. The value of
        // 8 really means no padding so we make a modulo division once
        // again to turn 8s to 0s.
        const uint8_t zero_padded_bits =
            static_cast<uint8_t>((8 - (prefix_len.asUint8() % 8)) % 8);
        // If there are zero padded bits, it means that we need an extra
        // byte to be retrieved from the buffer.
        if (zero_padded_bits > 0) {
            ++prefix_len_bytes;
        }

        // Make sure that the buffer is long enough. We substract 1 to
        // also account for the fact that the buffer includes a prefix
        // length besides a prefix.
        if ((buf.size() - 1) < prefix_len_bytes) {
            isc_throw(BadDataTypeCast, "unable to read a prefix having length of "
                      << prefix_len.asUnsigned() << " from a truncated buffer");
        }

        // It is possible for a prefix to be zero if the prefix length
        // is zero.
        IOAddress prefix(IOAddress::IPV6_ZERO_ADDRESS());

        // If there is anything more than prefix length is this buffer
        // we need to read it.
        if (buf.size() > 1) {
            // Buffer has to be copied, because we will modify its
            // contents by setting certain bits to 0, if necessary.
            std::vector<uint8_t> prefix_buf(buf.begin() + 1, buf.end());
            // All further conversions require that the buffer length is
            // 16 bytes.
            if (prefix_buf.size() < V6ADDRESS_LEN) {
                prefix_buf.resize(V6ADDRESS_LEN);
                if (prefix_len_bytes < prefix_buf.size()) {
                    // Zero all bits in the buffer beyond prefix length
                    // position.
                    std::fill(prefix_buf.begin() + prefix_len_bytes,
                              prefix_buf.end(), 0);

                    if (zero_padded_bits) {
                        // There is a byte that require zero padding. We
                        // achieve that by shifting the value of that byte
                        // back and forth by the number of zeroed bits.
                        prefix_buf.at(prefix_len_bytes - 1) =
                            (prefix_buf.at(prefix_len_bytes - 1)
                             >> zero_padded_bits)
                            << zero_padded_bits;
                    }
                }
            }
            // Convert the buffer to the IOAddress object.
            prefix = IOAddress::fromBytes(AF_INET6, &prefix_buf[0]);
        }

        return (std::make_pair(prefix_len, prefix));

    } catch (const BadDataTypeCast& ex) {
        // Pass through the BadDataTypeCast exceptions.
        throw;

    } catch (const std::exception& ex) {
        // If an exception of a different type has been thrown, insert
        // a text that indicates that the failure occurred during reading
        // the prefix and modify exception type to BadDataTypeCast.
        isc_throw(BadDataTypeCast, "unable to read a prefix from a buffer: "
                  << ex.what());
    }
}

void
OptionDataTypeUtil::writePrefix(const PrefixLen& prefix_len,
                                const IOAddress& prefix,
                                std::vector<uint8_t>& buf) {
    // Prefix must be an IPv6 prefix.
    if (!prefix.isV6()) {
        isc_throw(BadDataTypeCast, "illegal prefix value "
                  << prefix);
    }

    // We don't need to validate the prefix_len value, because it is
    // already validated by the PrefixLen class.
    buf.push_back(prefix_len.asUint8());

    // Convert the prefix length to a number of bytes.
    uint8_t prefix_len_bytes = prefix_len.asUint8() / 8;
    // Check if there are any bits that require zero padding. See the
    // commentary in readPrefix to see how this is calculated.
    const uint8_t zero_padded_bits =
        static_cast<uint8_t>((8 - (prefix_len.asUint8() % 8)) % 8);
    // If zero padding is needed it means that we need to extend the
    // buffer to hold the "partially occupied" byte.
    if (zero_padded_bits > 0) {
        ++prefix_len_bytes;
    }

    // Convert the prefix to byte representation and append it to
    // our output buffer.
    std::vector<uint8_t> prefix_bytes = prefix.toBytes();
    buf.insert(buf.end(), prefix_bytes.begin(),
               prefix_bytes.begin() + prefix_len_bytes);
    // If the last byte requires zero padding we achieve that by shifting
    // bits back and forth by the number of insignificant bits.
    if (zero_padded_bits) {
        *buf.rbegin() = (*buf.rbegin() >> zero_padded_bits) << zero_padded_bits;
    }
}

PSIDTuple
OptionDataTypeUtil::readPsid(const std::vector<uint8_t>& buf) {
    if (buf.size() < 3) {
        isc_throw(BadDataTypeCast, "unable to read PSID from the buffer."
                  << " Invalid buffer size " << buf.size()
                  << ". Expected 3 bytes (PSID length and PSID value)");
    }

    // Read PSID length.
    uint8_t psid_len = buf[0];

    // PSID length must not be greater than 16 bits.
    if (psid_len > (sizeof(uint16_t) * 8)) {
        isc_throw(BadDataTypeCast, "invalid PSID length value "
                  << static_cast<unsigned>(psid_len)
                  << ", this value is expected to be in range of 0 to 16");
    }

    // Read two bytes of PSID value.
    uint16_t psid = isc::util::readUint16(&buf[1], 2);

    // We need to check that the PSID value does not exceed the maximum value
    // for a specified PSID length. That means that all bits placed further than
    // psid_len from the left must be set to 0.
    // The value 0 is a special case because the RFC explicitly says that the
    // PSID value should be ignored if psid_len is 0.
    if ((psid & ~psid_bitmask[psid_len]) != 0) {
        isc_throw(BadDataTypeCast, "invalid PSID value " << psid
                  << " for a specified PSID length "
                  << static_cast<unsigned>(psid_len));
    }

    // All is good, so we can convert the PSID value read from the buffer to
    // the port set number.
    if (psid_len == 0) {
        // Shift by 16 always gives zero (CID 1398333)
        psid = 0;
    } else {
        psid >>= (sizeof(psid) * 8 - psid_len);
    }
    return (std::make_pair(PSIDLen(psid_len), PSID(psid)));
}

void
OptionDataTypeUtil::writePsid(const PSIDLen& psid_len, const PSID& psid,
                              std::vector<uint8_t>& buf) {
    if (psid_len.asUint8() > (sizeof(psid) * 8)) {
        isc_throw(BadDataTypeCast, "invalid PSID length value "
                  << psid_len.asUnsigned()
                  << ", this value is expected to be in range of 0 to 16");
    }

    if ((psid_len.asUint8() > 0) &&
        (psid.asUint16() > (0xFFFF >> (sizeof(uint16_t) * 8 - psid_len.asUint8())))) {
        isc_throw(BadDataTypeCast, "invalid PSID value " << psid.asUint16()
                  << " for a specified PSID length "
                  << psid_len.asUnsigned());
    }

    buf.resize(buf.size() + 3);
    buf.at(buf.size() - 3) = psid_len.asUint8();
    isc::util::writeUint16(static_cast<uint16_t>
                           (psid.asUint16() << (sizeof(uint16_t) * 8 - psid_len.asUint8())),
                           &buf[buf.size() - 2], 2);
}

std::string
OptionDataTypeUtil::readString(const std::vector<uint8_t>& buf) {
    std::string value;
    if (!buf.empty()) {
        // Per RFC 2132, section 2 we need to drop trailing NULLs
        auto begin = buf.begin();
        auto end = util::str::seekTrimmed(begin, buf.end(), 0x0);
        if (std::distance(begin, end) == 0) {
            isc_throw(isc::OutOfRange, "string value carried by the option "
                                       "contained only NULLs");
        }

        value.insert(value.end(), begin, end);
    }

    return (value);
}

void
OptionDataTypeUtil::writeString(const std::string& value,
                                std::vector<uint8_t>& buf) {
    if (value.size() > 0) {
        buf.insert(buf.end(), value.begin(), value.end());
    }
}

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