/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 |