summaryrefslogtreecommitdiffstats
path: root/src/lib/dhcp/option_custom.h
blob: 14cb08c5f046f3346380ff7277c8ee1e907b5956 (plain)
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
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef OPTION_CUSTOM_H
#define OPTION_CUSTOM_H

#include <dhcp/option.h>
#include <dhcp/option_definition.h>
#include <util/io_utilities.h>

namespace isc {
namespace dhcp {

/// @brief Option with defined data fields represented as buffers that can
/// be accessed using data field index.
///
/// This class represents an option which has defined structure: data fields
/// of specific types and order. Those fields can be accessed using indexes,
/// where index 0 represents first data field within an option. The last
/// field can be accessed using index equal to 'number of fields' - 1.
/// Internally, the option data is stored as a collection of OptionBuffer
/// objects, each representing data for a particular data field. This data
/// can be converted to the actual data type using methods implemented
/// within this class. This class is used to represent those options that
/// can't be represented by any other specialized class (this excludes the
/// Option class which is generic and can be used to represent any option).
class OptionCustom : public Option {
public:

    /// @brief Constructor, used for options to be sent.
    ///
    /// This constructor creates an instance of an option with default
    /// data set for all data fields. The option buffers are allocated
    /// according to data size being stored in particular data fields.
    /// For variable size data empty buffers are created.
    ///
    /// @param def option definition.
    /// @param u specifies universe (V4 or V6)
    OptionCustom(const OptionDefinition& def, Universe u);

    /// @brief Constructor, used for options to be sent.
    ///
    /// This constructor creates an instance of an option from the whole
    /// supplied buffer. This constructor is mainly used to create an
    /// instances of options to be stored in outgoing DHCP packets.
    /// The buffer used to create the instance of an option can be
    /// created from the option data specified in server's configuration.
    ///
    /// @param def option definition.
    /// @param u specifies universe (V4 or V6).
    /// @param data content of the option.
    ///
    /// @throw OutOfRange if option buffer is truncated.
    ///
    /// @todo list all exceptions thrown by ctor.
    OptionCustom(const OptionDefinition& def, Universe u, const OptionBuffer& data);

    /// @brief Constructor, used for received options.
    ///
    /// This constructor creates an instance an option from the portion
    /// of the buffer specified by iterators. This is mainly useful when
    /// parsing received packets. Such packets are represented by a single
    /// buffer holding option data and all sub options. Methods that are
    /// parsing a packet, supply relevant portions of the packet buffer
    /// to this constructor to create option instances out of it.
    ///
    /// @param def option definition.
    /// @param u specifies universe (V4 or V6).
    /// @param first iterator to the first element that should be copied.
    /// @param last iterator to the next element after the last one
    /// to be copied.
    ///
    /// @throw OutOfRange if option buffer is truncated.
    ///
    /// @todo list all exceptions thrown by ctor.
    OptionCustom(const OptionDefinition& def, Universe u,
                 OptionBufferConstIter first, OptionBufferConstIter last);

    /// @brief Create new buffer and set its value as an IP address.
    ///
    /// @param address IPv4 or IPv6 address to be written to
    /// a buffer being created.
    void addArrayDataField(const asiolink::IOAddress& address);

    /// @brief Create new buffer and store boolean value in it.
    ///
    /// @param value value to be stored in the created buffer.
    void addArrayDataField(const bool value);

    /// @brief Create new buffer and store integer value in it.
    ///
    /// @param value value to be stored in the created buffer.
    /// @tparam T integer type of the value being stored.
    template<typename T>
    void addArrayDataField(const T value) {
        checkArrayType();
        OptionDataType data_type = definition_.getType();
        if (OptionDataTypeTraits<T>::type != data_type) {
            isc_throw(isc::dhcp::InvalidDataType,
                      "specified data type " << data_type << " does not"
                      " match the data type in an option definition");
        }

        OptionBuffer buf;
        OptionDataTypeUtil::writeInt<T>(value, buf);
        buffers_.push_back(buf);
    }

    /// @brief Return a number of the data fields.
    ///
    /// @return number of data fields held by the option.
    uint32_t getDataFieldsNum() const { return (buffers_.size()); }

    /// @brief Read a buffer as IP address.
    ///
    /// @param index buffer index.
    ///
    /// @return IP address read from a buffer.
    /// @throw isc::OutOfRange if index is out of range.
    asiolink::IOAddress readAddress(const uint32_t index = 0) const;

    /// @brief Write an IP address into a buffer.
    ///
    /// @param address IP address being written.
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    /// @throw isc::dhcp::BadDataTypeCast if IP address is invalid.
    void writeAddress(const asiolink::IOAddress& address,
                      const uint32_t index = 0);

    /// @brief Read a buffer as binary data.
    ///
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    /// @return read buffer holding binary data.
    const OptionBuffer& readBinary(const uint32_t index = 0) const;

    /// @brief Write binary data into a buffer.
    ///
    /// @param buf buffer holding binary data to be written.
    /// @param index buffer index.
    void writeBinary(const OptionBuffer& buf, const uint32_t index = 0);

    /// @brief Read a buffer as boolean value.
    ///
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    /// @return read boolean value.
    bool readBoolean(const uint32_t index = 0) const;

    /// @brief Write a boolean value into a buffer.
    ///
    /// @param value boolean value to be written.
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    void writeBoolean(const bool value, const uint32_t index = 0);

    /// @brief Read a buffer as FQDN.
    ///
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if buffer index is out of range.
    /// @throw isc::dhcp::BadDataTypeCast if a buffer being read
    /// does not hold a valid FQDN.
    /// @return string representation if FQDN.
    std::string readFqdn(const uint32_t index = 0) const;

    /// @brief Write an FQDN into a buffer.
    ///
    /// @param fqdn text representation of FQDN.
    /// @param index buffer index.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    void writeFqdn(const std::string& fqdn, const uint32_t index = 0);

    /// @brief Read a buffer as integer value.
    ///
    /// @param index buffer index.
    /// @tparam integer type of a value being returned.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    /// @throw isc::dhcp::InvalidDataType if T is invalid.
    /// @return read integer value.
    template<typename T>
    T readInteger(const uint32_t index = 0) const {
        // Check that the index is not out of range.
        checkIndex(index);
        // Check that T points to a valid integer type and this type
        // is consistent with an option definition.
        checkDataType<T>(index);
        // When we created the buffer we have checked that it has a
        // valid size so this condition here should be always fulfilled.
        assert(buffers_[index].size() == OptionDataTypeTraits<T>::len);
        // Read an integer value.
        return (OptionDataTypeUtil::readInt<T>(buffers_[index]));
    }

    /// @brief Write an integer value into a buffer.
    ///
    /// @param value integer value to be written.
    /// @param index buffer index.
    /// @tparam T integer type of a value being written.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    /// @throw isc::dhcp::InvalidDataType if T is invalid.
    template<typename T>
    void writeInteger(const T value, const uint32_t index = 0) {
        // Check that the index is not out of range.
        checkIndex(index);
        // Check that T points to a valid integer type and this type
        // is consistent with an option definition.
        checkDataType<T>(index);
        // Get some temporary buffer.
        OptionBuffer buf;
        // Try to write to the buffer.
        OptionDataTypeUtil::writeInt<T>(value, buf);
        // If successful, replace the old buffer with new one.
        std::swap(buffers_[index], buf);
    }

    /// @brief Read a buffer as string value.
    ///
    /// @param index buffer index.
    ///
    /// @return string value read from buffer.
    /// @throw isc::OutOfRange if index is out of range.
    std::string readString(const uint32_t index = 0) const;

    /// @brief Write a string value into a buffer.
    ///
    /// @param text the string value to be written.
    /// @param index buffer index.
    void writeString(const std::string& text,
                     const uint32_t index = 0);

    /// @brief Writes DHCP option in a wire format to a buffer.
    ///
    /// @param buf output buffer (option will be stored there).
    virtual void pack(isc::util::OutputBuffer& buf);

    /// @brief Parses received buffer.
    ///
    /// @param begin iterator to first byte of option data
    /// @param end iterator to end of option data (first byte after option end)
    virtual void unpack(OptionBufferConstIter begin,
                        OptionBufferConstIter end);

    /// @brief Returns string representation of the option.
    ///
    /// @param indent number of spaces before printed text.
    ///
    /// @return string with text representation.
    virtual std::string toText(int indent = 0);

    /// @brief Returns length of the complete option (data length +
    ///        DHCPv4/DHCPv6 option header)
    ///
    /// @return length of the option
    virtual uint16_t len();

    /// @brief Sets content of this option from buffer.
    ///
    /// Option will be resized to length of buffer.
    ///
    /// @param first iterator pointing to beginning of buffer to copy.
    /// @param last iterator pointing to end of buffer to copy.
    void initialize(const OptionBufferConstIter first,
                    const OptionBufferConstIter last);

private:

    /// @brief Verify that the option comprises an array of values.
    ///
    /// This helper function is used by createArrayEntry functions
    /// and throws an exception if the particular option is not
    /// an array.
    ///
    /// @throw isc::InvalidOperation if option is not an array.
    inline void checkArrayType() const {
        if (!definition_.getArrayType()) {
            isc_throw(InvalidOperation, "failed to add new array entry to an"
                      << " option. The option is not an array.");
        }
    }

    /// @brief Verify that the integer type is consistent with option
    /// field type.
    ///
    /// This convenience function checks that the data type specified as T
    /// is consistent with a type of a data field identified by index.
    ///
    /// @param index data field index.
    /// @tparam data type to be validated.
    ///
    /// @throw isc::dhcp::InvalidDataType if the type is invalid.
    template<typename T>
    // cppcheck-suppress unusedPrivateFunction
    void checkDataType(const uint32_t index) const;

    /// @brief Check if data field index is valid.
    ///
    /// @param index Data field index to check.
    ///
    /// @throw isc::OutOfRange if index is out of range.
    void checkIndex(const uint32_t index) const;

    /// @brief Create a collection of non initialized buffers.
    void createBuffers();

    /// @brief Create collection of buffers representing data field values.
    ///
    /// @param data_buf a buffer to be parsed.
    void createBuffers(const OptionBuffer& data_buf);

    /// @brief Return a text representation of a data field.
    ///
    /// @param data_type data type of a field.
    /// @param index data field buffer index within a custom option.
    ///
    /// @return text representation of a data field.
    std::string dataFieldToText(const OptionDataType data_type,
                                const uint32_t index) const;

    /// Make this function private as we don't want it to be invoked
    /// on OptionCustom object. We rather want that initialize to
    /// be called instead.
    using Option::setData;

    /// Option definition used to create an option.
    OptionDefinition definition_;

    /// The collection of buffers holding data for option fields.
    /// The order of buffers corresponds to the order of option
    /// fields.
    std::vector<OptionBuffer> buffers_;
};

/// A pointer to the OptionCustom object.
typedef boost::shared_ptr<OptionCustom> OptionCustomPtr;

template<typename T>
void
OptionCustom::checkDataType(const uint32_t index) const {
    // Check that the requested return type is a supported integer.
    if (!OptionDataTypeTraits<T>::integer_type) {
        isc_throw(isc::dhcp::InvalidDataType, "specified data type"
                  " is not a supported integer type.");
    }

    // Get the option definition type.
    OptionDataType data_type = definition_.getType();
    if (data_type == OPT_RECORD_TYPE) {
        const OptionDefinition::RecordFieldsCollection& record_fields =
            definition_.getRecordFields();
        // When we initialized buffers we have already checked that
        // the number of these buffers is equal to number of option
        // fields in the record so the condition below should be met.
        assert(index < record_fields.size());
        // Get the data type to be returned.
        data_type = record_fields[index];
    }

    if (OptionDataTypeTraits<T>::type != data_type) {
        isc_throw(isc::dhcp::InvalidDataType,
                  "specified data type " << data_type << " does not"
                  " match the data type in an option definition for field"
                  " index " << index);
    }
}
} // namespace isc::dhcp
} // namespace isc

#endif // OPTION_CUSTOM_H