Coverage Report

Created: 2026-07-14 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/dhcp/option_custom.cc
Line
Count
Source
1
// Copyright (C) 2012-2026 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
#include <dhcp/libdhcp++.h>
9
#include <dhcp/option_data_types.h>
10
#include <dhcp/option_custom.h>
11
#include <exceptions/isc_assert.h>
12
#include <util/str.h>
13
#include <util/encode/encode.h>
14
15
using namespace isc::asiolink;
16
using namespace isc::util;
17
18
namespace isc {
19
namespace dhcp {
20
21
OptionCustom::OptionCustom(const OptionDefinition& def,
22
                           Universe u)
23
263
    : Option(u, def.getCode(), OptionBuffer()),
24
263
      definition_(def) {
25
263
    setEncapsulatedSpace(def.getEncapsulatedSpace());
26
263
    createBuffers();
27
263
}
28
29
OptionCustom::OptionCustom(const OptionDefinition& def,
30
                           Universe u,
31
                           const OptionBuffer& data)
32
0
    : Option(u, def.getCode(), data.begin(), data.end()),
33
0
      definition_(def) {
34
0
    setEncapsulatedSpace(def.getEncapsulatedSpace());
35
0
    createBuffers(getData());
36
0
}
37
38
OptionCustom::OptionCustom(const OptionDefinition& def,
39
                           Universe u,
40
                           OptionBufferConstIter first,
41
                           OptionBufferConstIter last,
42
                           size_t rec_level)
43
232k
    : Option(u, def.getCode(), first, last),
44
232k
      definition_(def) {
45
232k
    setEncapsulatedSpace(def.getEncapsulatedSpace());
46
232k
    createBuffers(getData(), rec_level);
47
232k
}
48
49
OptionPtr
50
863
OptionCustom::clone() const {
51
863
    return (cloneInternal<OptionCustom>());
52
863
}
53
54
void
55
0
OptionCustom::addArrayDataField(const IOAddress& address) {
56
0
    checkArrayType();
57
58
0
    if ((address.isV4() && definition_.getType() != OPT_IPV4_ADDRESS_TYPE) ||
59
0
        (address.isV6() && definition_.getType() != OPT_IPV6_ADDRESS_TYPE)) {
60
0
        isc_throw(BadDataTypeCast, "invalid address specified "
61
0
                  << address << ". Expected a valid IPv"
62
0
                  << (definition_.getType() == OPT_IPV4_ADDRESS_TYPE ?
63
0
                      "4" : "6") << " address.");
64
0
    }
65
66
0
    OptionBuffer buf;
67
0
    OptionDataTypeUtil::writeAddress(address, buf);
68
0
    buffers_.push_back(buf);
69
0
}
70
71
void
72
0
OptionCustom::addArrayDataField(const std::string& value) {
73
0
    checkArrayType();
74
75
0
    OpaqueDataTuple::LengthFieldType lft = OptionDataTypeUtil::getTupleLenFieldType(getUniverse());
76
0
    OptionBuffer buf;
77
0
    OptionDataTypeUtil::writeTuple(value, lft, buf);
78
0
    buffers_.push_back(buf);
79
0
}
80
81
void
82
0
OptionCustom::addArrayDataField(const OpaqueDataTuple& value) {
83
0
    checkArrayType();
84
85
0
    OptionBuffer buf;
86
0
    OptionDataTypeUtil::writeTuple(value, buf);
87
0
    buffers_.push_back(buf);
88
0
}
89
90
void
91
0
OptionCustom::addArrayDataField(const bool value) {
92
0
    checkArrayType();
93
94
0
    OptionBuffer buf;
95
0
    OptionDataTypeUtil::writeBool(value, buf);
96
0
    buffers_.push_back(buf);
97
0
}
98
99
void
100
OptionCustom::addArrayDataField(const PrefixLen& prefix_len,
101
0
                                const asiolink::IOAddress& prefix) {
102
0
    checkArrayType();
103
104
0
    if (definition_.getType() != OPT_IPV6_PREFIX_TYPE) {
105
0
        isc_throw(BadDataTypeCast, "IPv6 prefix can be specified only for"
106
0
                  " an option comprising an array of IPv6 prefix values");
107
0
    }
108
109
0
    OptionBuffer buf;
110
0
    OptionDataTypeUtil::writePrefix(prefix_len, prefix, buf);
111
0
    buffers_.push_back(buf);
112
0
}
113
114
void
115
0
OptionCustom::addArrayDataField(const PSIDLen& psid_len, const PSID& psid) {
116
0
    checkArrayType();
117
118
0
    if (definition_.getType() != OPT_PSID_TYPE) {
119
0
        isc_throw(BadDataTypeCast, "PSID value can be specified onlu for"
120
0
                  " an option comprising an array of PSID length / value"
121
0
                  " tuples");
122
0
    }
123
124
0
    OptionBuffer buf;
125
0
    OptionDataTypeUtil::writePsid(psid_len, psid, buf);
126
0
    buffers_.push_back(buf);
127
0
}
128
129
void
130
175k
OptionCustom::checkIndex(const uint32_t index) const {
131
175k
    if (index >= buffers_.size()) {
132
0
        isc_throw(isc::OutOfRange, "specified data field index " << index
133
0
                  << " is out of range.");
134
0
    }
135
175k
}
136
137
void
138
OptionCustom::createBuffer(OptionBuffer& buffer,
139
263
                           const OptionDataType data_type) const {
140
    // For data types that have a fixed size we can use the
141
    // utility function to get the buffer's size.
142
263
    size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
143
144
    // For variable data sizes the utility function returns zero.
145
    // It is ok for string values because the default string
146
    // is 'empty'. However for FQDN the empty value is not valid
147
    // so we initialize it to '.'. For prefix there is a prefix
148
    // length fixed field.
149
263
    if (data_size == 0) {
150
0
        if (data_type == OPT_FQDN_TYPE) {
151
0
            OptionDataTypeUtil::writeFqdn(".", buffer);
152
153
0
        } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
154
0
            OptionDataTypeUtil::writePrefix(PrefixLen(0),
155
0
                                            IOAddress::IPV6_ZERO_ADDRESS(),
156
0
                                            buffer);
157
0
        }
158
263
    } else {
159
        // At this point we can resize the buffer. Note that
160
        // for string values we are setting the empty buffer
161
        // here.
162
263
        buffer.resize(data_size);
163
263
    }
164
263
}
165
166
void
167
263
OptionCustom::createBuffers() {
168
263
    definition_.validate();
169
170
263
    std::vector<OptionBuffer> buffers;
171
172
263
    OptionDataType data_type = definition_.getType();
173
    // This function is called when an empty data buffer has been
174
    // passed to the constructor. In such cases values for particular
175
    // data fields will be set using modifier functions but for now
176
    // we need to initialize a set of buffers that are specified
177
    // for an option by its definition. Since there is no data yet,
178
    // we are going to fill these buffers with default values.
179
263
    if (data_type == OPT_RECORD_TYPE) {
180
        // For record types we need to iterate over all data fields
181
        // specified in option definition and create corresponding
182
        // buffers for each of them.
183
0
        const OptionDefinition::RecordFieldsCollection fields =
184
0
            definition_.getRecordFields();
185
186
0
        for (auto const& field : fields) {
187
0
            OptionBuffer buf;
188
0
            createBuffer(buf, field);
189
            // We have the buffer with default value prepared so we
190
            // add it to the set of buffers.
191
0
            buffers.push_back(buf);
192
0
        }
193
263
    } else if (!definition_.getArrayType() &&
194
263
               data_type != OPT_EMPTY_TYPE) {
195
        // For either 'empty' options we don't have to create any buffers
196
        // for obvious reason. For arrays we also don't create any buffers
197
        // yet because the set of fields that belong to the array is open
198
        // ended so we can't allocate required buffers until we know how
199
        // many of them are needed.
200
        // For non-arrays we have a single value being held by the option
201
        // so we have to allocate exactly one buffer.
202
263
        OptionBuffer buf;
203
263
        createBuffer(buf, data_type);
204
        // Add a buffer that we have created and leave.
205
263
        buffers.push_back(buf);
206
263
    }
207
    // The 'swap' is used here because we want to make sure that we
208
    // don't touch buffers_ until we successfully allocate all
209
    // buffers to be stored there.
210
263
    std::swap(buffers, buffers_);
211
263
}
212
213
size_t
214
OptionCustom::bufferLength(const OptionDataType data_type, bool in_array,
215
                           OptionBuffer::const_iterator begin,
216
7.27M
                           OptionBuffer::const_iterator end) const {
217
    // For fixed-size data type such as boolean, integer, even
218
    // IP address we can use the utility function to get the required
219
    // buffer size.
220
7.27M
    size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
221
222
    // For variable size types (e.g. string) the function above will
223
    // return 0 so we need to do a runtime check of the length.
224
7.27M
    if (data_size == 0) {
225
        // FQDN is a special data type as it stores variable length data
226
        // but the data length is encoded in the buffer. The easiest way
227
        // to obtain the length of the data is to read the FQDN. The
228
        // utility function will return the size of the buffer on success.
229
1.81M
        if (data_type == OPT_FQDN_TYPE) {
230
1.78M
            try {
231
1.78M
                std::string fqdn =
232
1.78M
                    OptionDataTypeUtil::readFqdn(OptionBuffer(begin, end));
233
                // The size of the buffer holding an FQDN is always
234
                // 1 byte larger than the size of the string
235
                // representation of this FQDN.
236
1.78M
                data_size = fqdn.size() + 1;
237
1.78M
            } catch (const std::exception& ex) {
238
2.91k
                if (Option::lenient_parsing_) {
239
0
                    isc_throw(SkipThisOptionError, "failed to read "
240
0
                              "domain-name from wire format: "
241
0
                              << ex.what());
242
0
                }
243
244
2.91k
                throw;
245
2.91k
            }
246
1.78M
        } else if (!definition_.getArrayType() &&
247
32.8k
                   ((data_type == OPT_BINARY_TYPE) ||
248
21.3k
                    (data_type == OPT_STRING_TYPE))) {
249
            // In other case we are dealing with string or binary value
250
            // which size can't be determined. Thus we consume the
251
            // remaining part of the buffer for it. Note that variable
252
            // size data can be laid at the end of the option only and
253
            // that the validate() function in OptionDefinition object
254
            // should have checked wheter it is a case for this option.
255
12.1k
            data_size = std::distance(begin, end);
256
20.7k
        } else if (data_type == OPT_IPV6_PREFIX_TYPE) {
257
            // The size of the IPV6 prefix type is determined as
258
            // one byte (which is the size of the prefix in bits)
259
            // followed by the prefix bits (right-padded with
260
            // zeros to the nearest octet boundary)
261
20.7k
            if ((begin == end) && !in_array)
262
1.99k
                return 0;
263
18.7k
            PrefixTuple prefix =
264
18.7k
                OptionDataTypeUtil::readPrefix(OptionBuffer(begin, end));
265
            // Data size comprises 1 byte holding a prefix length and the
266
            // prefix length (in bytes) rounded to the nearest byte boundary.
267
18.7k
            data_size = sizeof(uint8_t) + (prefix.first.asUint8() + 7) / 8;
268
18.7k
        } else if (data_type == OPT_TUPLE_TYPE) {
269
0
            OpaqueDataTuple::LengthFieldType lft =
270
0
                OptionDataTypeUtil::getTupleLenFieldType(getUniverse());
271
0
            std::string value =
272
0
                OptionDataTypeUtil::readTuple(OptionBuffer(begin, end), lft);
273
0
            data_size = value.size();
274
            // The size of the buffer holding a tuple is always
275
            // 1 or 2 byte larger than the size of the string
276
0
            data_size += getUniverse() == Option::V4 ? 1 : 2;
277
0
        } else {
278
            // If we reached the end of buffer we assume that this option is
279
            // truncated because there is no remaining data to initialize
280
            // an option field.
281
0
            isc_throw(OutOfRange, "option buffer truncated");
282
0
        }
283
1.81M
    }
284
285
7.26M
    return data_size;
286
7.27M
}
287
288
void
289
232k
OptionCustom::createBuffers(const OptionBuffer& data_buf, size_t rec_level) {
290
    // Check that the option definition is correct as we are going
291
    // to use it to split the data_ buffer into set of sub buffers.
292
232k
    definition_.validate();
293
294
232k
    std::vector<OptionBuffer> buffers;
295
232k
    OptionBuffer::const_iterator data = data_buf.begin();
296
297
232k
    OptionDataType data_type = definition_.getType();
298
232k
    if (data_type == OPT_RECORD_TYPE) {
299
        // An option comprises a record of data fields. We need to
300
        // get types of these data fields to allocate enough space
301
        // for each buffer.
302
48.4k
        const OptionDefinition::RecordFieldsCollection& fields =
303
48.4k
            definition_.getRecordFields();
304
305
        // Go over all data fields within a record.
306
142k
        for (auto const& field : fields) {
307
142k
            size_t data_size = bufferLength(field, false,
308
142k
                                            data, data_buf.end());
309
310
            // Our data field requires that there is a certain chunk of
311
            // data left in the buffer. If not, option is truncated.
312
142k
            if (static_cast<size_t>(std::distance(data, data_buf.end())) < data_size) {
313
417
                isc_throw(OutOfRange, "option buffer truncated");
314
417
            }
315
316
            // Store the created buffer.
317
142k
            buffers.push_back(OptionBuffer(data, data + data_size));
318
            // Proceed to the next data field.
319
142k
            data += data_size;
320
142k
        }
321
322
        // Get extra buffers when the last field is an array.
323
48.0k
        if (definition_.getArrayType()) {
324
5.74M
            while (data != data_buf.end()) {
325
                // Code copied from the standard array case
326
5.73M
                size_t data_size = bufferLength(fields.back(), true,
327
5.73M
                                                data, data_buf.end());
328
5.73M
                isc_throw_assert(data_size > 0);
329
5.73M
                if (static_cast<size_t>(std::distance(data, data_buf.end())) < data_size) {
330
4.75k
                    break;
331
4.75k
                }
332
5.72M
                buffers.push_back(OptionBuffer(data, data + data_size));
333
5.72M
                data += data_size;
334
5.72M
            }
335
10.7k
        }
336
337
        // Unpack suboptions if any.
338
37.2k
        else if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
339
11.9k
            unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
340
11.9k
        }
341
342
184k
    } else if (data_type != OPT_EMPTY_TYPE) {
343
        // If data_type value is other than OPT_RECORD_TYPE, our option is
344
        // empty (have no data at all) or it comprises one or more
345
        // data fields of the same type. The type of those fields
346
        // is held in the data_type variable so let's use it to determine
347
        // a size of buffers.
348
90.6k
        size_t data_size = OptionDataTypeUtil::getDataTypeLen(data_type);
349
        // The check below will fail if the input buffer is too short
350
        // for the data size being held by this option.
351
        // Note that data_size returned by getDataTypeLen may be zero
352
        // if variable length data is being held by the option but
353
        // this will not cause this check to throw exception.
354
90.6k
        if (static_cast<size_t>(std::distance(data, data_buf.end())) < data_size) {
355
806
            isc_throw(OutOfRange, "option buffer truncated");
356
806
        }
357
        // For an array of values we are taking different path because
358
        // we have to handle multiple buffers.
359
89.8k
        if (definition_.getArrayType()) {
360
1.39M
            while (data != data_buf.end()) {
361
1.35M
                data_size = bufferLength(data_type, true, data, data_buf.end());
362
                // We don't perform other checks for data types that can't be
363
                // used together with array indicator such as strings, empty field
364
                // etc. This is because OptionDefinition::validate function should
365
                // have checked this already. Thus data_size must be greater than
366
                // zero.
367
1.35M
                isc_throw_assert(data_size > 0);
368
                // Get chunks of data and store as a collection of buffers.
369
                // Truncate any remaining part which length is not divisible by
370
                // data_size. Note that it is ok to truncate the data if and only
371
                // if the data buffer is long enough to keep at least one value.
372
                // This has been checked above already.
373
1.35M
                if (static_cast<size_t>(std::distance(data, data_buf.end())) < data_size) {
374
7.90k
                    break;
375
7.90k
                }
376
1.34M
                buffers.push_back(OptionBuffer(data, data + data_size));
377
1.34M
                data += data_size;
378
1.34M
            }
379
51.4k
        } else {
380
            // For non-arrays the data_size can be zero because
381
            // getDataTypeLen returns zero for variable size data types
382
            // such as strings. Simply take whole buffer.
383
38.3k
            data_size = bufferLength(data_type, false, data, data_buf.end());
384
38.3k
            if ((data_size > 0) &&
385
38.0k
                (static_cast<size_t>(std::distance(data, data_buf.end())) >= data_size)) {
386
37.7k
                buffers.push_back(OptionBuffer(data, data + data_size));
387
37.7k
                data += data_size;
388
37.7k
            } else {
389
577
                isc_throw(OutOfRange, "option buffer truncated");
390
577
            }
391
392
            // Unpack suboptions if any.
393
37.7k
            if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
394
0
                unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
395
0
            }
396
37.7k
        }
397
93.5k
    } else {
398
        // Unpack suboptions if any.
399
93.5k
        if (data != data_buf.end() && !getEncapsulatedSpace().empty()) {
400
67.9k
            unpackOptions(OptionBuffer(data, data_buf.end()), rec_level);
401
67.9k
        }
402
93.5k
    }
403
    // If everything went ok we can replace old buffer set with new ones.
404
230k
    std::swap(buffers_, buffers);
405
230k
}
406
407
std::string
408
OptionCustom::dataFieldToText(const OptionDataType data_type,
409
177k
                              const uint32_t index) const {
410
177k
    std::ostringstream text;
411
412
    // Get the value of the data field.
413
177k
    switch (data_type) {
414
3.88k
    case OPT_BINARY_TYPE:
415
3.88k
    {
416
3.88k
        auto data = readBinary(index);
417
3.88k
        if (data.empty()) {
418
277
            text << "''";
419
3.60k
        } else {
420
3.60k
            text << util::encode::encodeHex(data);
421
3.60k
            if (str::isPrintable(data)) {
422
1.96k
                std::string printable(data.cbegin(), data.cend());
423
1.96k
                text << " '" << printable << "'";
424
1.96k
            }
425
3.60k
        }
426
3.88k
        break;
427
0
    }
428
18
    case OPT_BOOLEAN_TYPE:
429
18
        text << (readBoolean(index) ? "true" : "false");
430
18
        break;
431
0
    case OPT_INT8_TYPE:
432
0
        text << static_cast<int>(readInteger<int8_t>(index));
433
0
        break;
434
0
    case OPT_INT16_TYPE:
435
0
        text << readInteger<int16_t>(index);
436
0
        break;
437
0
    case OPT_INT32_TYPE:
438
0
        text << readInteger<int32_t>(index);
439
0
        break;
440
11.4k
    case OPT_UINT8_TYPE:
441
11.4k
        text << static_cast<unsigned>(readInteger<uint8_t>(index));
442
11.4k
        break;
443
1.48k
    case OPT_UINT16_TYPE:
444
1.48k
        text << readInteger<uint16_t>(index);
445
1.48k
        break;
446
1.18k
    case OPT_UINT32_TYPE:
447
1.18k
        text << readInteger<uint32_t>(index);
448
1.18k
        break;
449
5.26k
    case OPT_IPV4_ADDRESS_TYPE:
450
7.74k
    case OPT_IPV6_ADDRESS_TYPE:
451
7.74k
        text << readAddress(index);
452
7.74k
        break;
453
148k
    case OPT_FQDN_TYPE:
454
148k
        text << "\"" << readFqdn(index) << "\"";
455
148k
        break;
456
0
    case OPT_TUPLE_TYPE:
457
0
        text << "\"" << readTuple(index) << "\"";
458
0
        break;
459
70
    case OPT_STRING_TYPE:
460
70
        text << "\"" << readString(index) << "\"";
461
70
        break;
462
1.38k
    case OPT_PSID_TYPE:
463
1.38k
    {
464
1.38k
        PSIDTuple t = readPsid(index);
465
1.38k
        text << "len=" << t.first.asUnsigned() << ",psid=" << t.second.asUint16();
466
1.38k
        break;
467
5.26k
    }
468
2.42k
    default:
469
2.42k
        break;
470
177k
    }
471
472
    // Append data field type in brackets.
473
177k
    text << " (" << OptionDataTypeUtil::getDataTypeName(data_type) << ")";
474
475
177k
    return (text.str());
476
177k
}
477
478
void
479
15.1k
OptionCustom::pack(isc::util::OutputBuffer& buf, bool check) const {
480
481
    // Pack DHCP header (V4 or V6).
482
15.1k
    packHeader(buf, check);
483
484
    // Write data from buffers.
485
77.1k
    for (auto const& it : buffers_) {
486
        // In theory the createBuffers function should have taken
487
        // care that there are no empty buffers added to the
488
        // collection but it is almost always good to make sure.
489
77.1k
        if (!it.empty()) {
490
76.7k
            buf.writeData(&it[0], it.size());
491
76.7k
        }
492
77.1k
    }
493
494
    // Write suboptions.
495
15.1k
    packOptions(buf, check);
496
15.1k
}
497
498
499
IOAddress
500
7.74k
OptionCustom::readAddress(const uint32_t index) const {
501
7.74k
    checkIndex(index);
502
503
    // The address being read can be either IPv4 or IPv6. The decision
504
    // is made based on the buffer length. If it holds 4 bytes it is IPv4
505
    // address, if it holds 16 bytes it is IPv6.
506
7.74k
    if (buffers_[index].size() == asiolink::V4ADDRESS_LEN) {
507
5.26k
        return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET));
508
5.26k
    } else if (buffers_[index].size() == asiolink::V6ADDRESS_LEN) {
509
2.48k
        return (OptionDataTypeUtil::readAddress(buffers_[index], AF_INET6));
510
2.48k
    } else {
511
0
        isc_throw(BadDataTypeCast, "unable to read data from the buffer as"
512
0
                  << " IP address. Invalid buffer length "
513
0
                  << buffers_[index].size() << ".");
514
0
    }
515
7.74k
}
516
517
void
518
OptionCustom::writeAddress(const IOAddress& address,
519
263
                           const uint32_t index) {
520
263
    checkIndex(index);
521
522
263
    if ((address.isV4() && buffers_[index].size() != V4ADDRESS_LEN) ||
523
263
        (address.isV6() && buffers_[index].size() != V6ADDRESS_LEN)) {
524
0
        isc_throw(BadDataTypeCast, "invalid address specified "
525
0
                  << address << ". Expected a valid IPv"
526
0
                  << (buffers_[index].size() == V4ADDRESS_LEN ? "4" : "6")
527
0
                  << " address.");
528
0
    }
529
530
263
    OptionBuffer buf;
531
263
    OptionDataTypeUtil::writeAddress(address, buf);
532
263
    std::swap(buf, buffers_[index]);
533
263
}
534
535
const OptionBuffer&
536
3.88k
OptionCustom::readBinary(const uint32_t index) const {
537
3.88k
    checkIndex(index);
538
3.88k
    return (buffers_[index]);
539
3.88k
}
540
541
void
542
OptionCustom::writeBinary(const OptionBuffer& buf,
543
0
                          const uint32_t index) {
544
0
    checkIndex(index);
545
0
    buffers_[index] = buf;
546
0
}
547
548
std::string
549
0
OptionCustom::readTuple(const uint32_t index) const {
550
0
    checkIndex(index);
551
0
    OpaqueDataTuple::LengthFieldType lft = OptionDataTypeUtil::getTupleLenFieldType(getUniverse());
552
0
    return (OptionDataTypeUtil::readTuple(buffers_[index], lft));
553
0
}
554
555
void
556
OptionCustom::readTuple(OpaqueDataTuple& tuple,
557
0
                        const uint32_t index) const {
558
0
    checkIndex(index);
559
0
    OptionDataTypeUtil::readTuple(buffers_[index], tuple);
560
0
}
561
562
void
563
0
OptionCustom::writeTuple(const std::string& value, const uint32_t index) {
564
0
    checkIndex(index);
565
566
0
    buffers_[index].clear();
567
0
    OpaqueDataTuple::LengthFieldType lft = OptionDataTypeUtil::getTupleLenFieldType(getUniverse());
568
0
    OptionDataTypeUtil::writeTuple(value, lft, buffers_[index]);
569
0
}
570
571
void
572
0
OptionCustom::writeTuple(const OpaqueDataTuple& value, const uint32_t index) {
573
0
    checkIndex(index);
574
575
0
    buffers_[index].clear();
576
0
    OptionDataTypeUtil::writeTuple(value, buffers_[index]);
577
0
}
578
579
bool
580
18
OptionCustom::readBoolean(const uint32_t index) const {
581
18
    checkIndex(index);
582
18
    return (OptionDataTypeUtil::readBool(buffers_[index]));
583
18
}
584
585
void
586
0
OptionCustom::writeBoolean(const bool value, const uint32_t index) {
587
0
    checkIndex(index);
588
589
0
    buffers_[index].clear();
590
0
    OptionDataTypeUtil::writeBool(value, buffers_[index]);
591
0
}
592
593
std::string
594
148k
OptionCustom::readFqdn(const uint32_t index) const {
595
148k
    checkIndex(index);
596
148k
    return (OptionDataTypeUtil::readFqdn(buffers_[index]));
597
148k
}
598
599
void
600
0
OptionCustom::writeFqdn(const std::string& fqdn, const uint32_t index) {
601
0
    checkIndex(index);
602
603
    // Create a temporary buffer where the FQDN will be written.
604
0
    OptionBuffer buf;
605
    // Try to write to the temporary buffer rather than to the
606
    // buffers_ member directly guarantees that we don't modify
607
    // (clear) buffers_ until we are sure that the provided FQDN
608
    // is valid.
609
0
    OptionDataTypeUtil::writeFqdn(fqdn, buf);
610
    // If we got to this point it means that the FQDN is valid.
611
    // We can move the contents of the temporary buffer to the
612
    // target buffer.
613
0
    std::swap(buffers_[index], buf);
614
0
}
615
616
PrefixTuple
617
0
OptionCustom::readPrefix(const uint32_t index) const {
618
0
    checkIndex(index);
619
0
    return (OptionDataTypeUtil::readPrefix(buffers_[index]));
620
0
}
621
622
void
623
OptionCustom::writePrefix(const PrefixLen& prefix_len,
624
                          const IOAddress& prefix,
625
0
                          const uint32_t index) {
626
0
    checkIndex(index);
627
628
0
    OptionBuffer buf;
629
0
    OptionDataTypeUtil::writePrefix(prefix_len, prefix, buf);
630
    // If there are no errors while writing PSID to a buffer, we can
631
    // replace the current buffer with a new buffer.
632
0
    std::swap(buffers_[index], buf);
633
0
}
634
635
636
PSIDTuple
637
1.38k
OptionCustom::readPsid(const uint32_t index) const {
638
1.38k
    checkIndex(index);
639
1.38k
    return (OptionDataTypeUtil::readPsid(buffers_[index]));
640
1.38k
}
641
642
void
643
OptionCustom::writePsid(const PSIDLen& psid_len, const PSID& psid,
644
0
                        const uint32_t index) {
645
0
    checkIndex(index);
646
647
0
    OptionBuffer buf;
648
0
    OptionDataTypeUtil::writePsid(psid_len, psid, buf);
649
    // If there are no errors while writing PSID to a buffer, we can
650
    // replace the current buffer with a new buffer.
651
0
    std::swap(buffers_[index], buf);
652
0
}
653
654
655
std::string
656
70
OptionCustom::readString(const uint32_t index) const {
657
70
    checkIndex(index);
658
70
    return (OptionDataTypeUtil::readString(buffers_[index]));
659
70
}
660
661
void
662
0
OptionCustom::writeString(const std::string& text, const uint32_t index) {
663
0
    checkIndex(index);
664
665
    // Let's clear a buffer as we want to replace the value of the
666
    // whole buffer. If we fail to clear the buffer the data will
667
    // be appended.
668
0
    buffers_[index].clear();
669
    // If the text value is empty we can leave because the buffer
670
    // is already empty.
671
0
    if (!text.empty()) {
672
0
        OptionDataTypeUtil::writeString(text, buffers_[index]);
673
0
    }
674
0
}
675
676
void
677
OptionCustom::unpack(OptionBufferConstIter begin,
678
0
                     OptionBufferConstIter end) {
679
0
    initialize(begin, end);
680
0
}
681
682
uint16_t
683
598k
OptionCustom::len() const {
684
    // The length of the option is a sum of option header ...
685
598k
    size_t length = getHeaderLen();
686
687
    // ... lengths of all buffers that hold option data ...
688
3.55M
    for (auto const& buf : buffers_) {
689
3.55M
        length += buf.size();
690
3.55M
    }
691
692
    // ... and lengths of all suboptions
693
598k
    for (auto const& it : options_) {
694
134k
        length += it.second->len();
695
134k
    }
696
697
598k
    return (static_cast<uint16_t>(length));
698
598k
}
699
700
void OptionCustom::initialize(const OptionBufferConstIter first,
701
0
                              const OptionBufferConstIter last) {
702
0
    setData(first, last);
703
704
    // Chop the data_ buffer into set of buffers that represent
705
    // option fields data.
706
0
    createBuffers(getData());
707
0
}
708
709
15.1k
std::string OptionCustom::toText(int indent) const {
710
15.1k
    std::stringstream output;
711
712
15.1k
    output << headerToText(indent) << ":";
713
714
15.1k
    OptionDataType data_type = definition_.getType();
715
15.1k
    if (data_type == OPT_RECORD_TYPE) {
716
9.00k
        const OptionDefinition::RecordFieldsCollection& fields =
717
9.00k
            definition_.getRecordFields();
718
719
        // For record types we iterate over fields defined in
720
        // option definition and match the appropriate buffer
721
        // with them.
722
9.00k
        size_t j = 0;
723
27.8k
        for (auto const& field : fields) {
724
27.8k
            output << " " << dataFieldToText(field, j);
725
27.8k
            j++;
726
27.8k
        }
727
728
        // If the last record field is an array iterate on extra buffers
729
9.00k
        if (definition_.getArrayType()) {
730
41.6k
            for (unsigned int i = fields.size(); i < getDataFieldsNum(); ++i) {
731
40.3k
                output << " " << dataFieldToText(fields.back(), i);
732
40.3k
            }
733
1.27k
        }
734
9.00k
    } else {
735
        // For non-record types we iterate over all buffers
736
        // and print the data type set globally for an option
737
        // definition. We take the same code path for arrays
738
        // and non-arrays as they only differ in such a way that
739
        // non-arrays have just single data field.
740
115k
        for (unsigned int i = 0; i < getDataFieldsNum(); ++i) {
741
109k
            output << " " << dataFieldToText(definition_.getType(), i);
742
109k
        }
743
6.10k
    }
744
745
    // Append suboptions.
746
15.1k
    output << suboptionsToText(indent + 2);
747
748
15.1k
    return (output.str());
749
15.1k
}
750
751
} // end of isc::dhcp namespace
752
} // end of isc namespace