1 // Copyright (C) 2012-2019 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 #ifndef OPTION_INT_H
8 #define OPTION_INT_H
9 
10 #include <dhcp/libdhcp++.h>
11 #include <dhcp/option.h>
12 #include <dhcp/option_data_types.h>
13 #include <dhcp/option_space.h>
14 #include <util/io_utilities.h>
15 
16 #include <stdint.h>
17 #include <sstream>
18 
19 namespace isc {
20 namespace dhcp {
21 
22 template<typename T>
23 class OptionInt;
24 
25 /// @defgroup option_int_defs Typedefs for OptionInt class.
26 ///
27 /// @brief Classes that represent options comprising an integer.
28 ///
29 /// @{
30 typedef OptionInt<uint8_t> OptionUint8;
31 typedef boost::shared_ptr<OptionUint8> OptionUint8Ptr;
32 typedef OptionInt<uint16_t> OptionUint16;
33 typedef boost::shared_ptr<OptionUint16> OptionUint16Ptr;
34 typedef OptionInt<uint32_t> OptionUint32;
35 typedef boost::shared_ptr<OptionUint32> OptionUint32Ptr;
36 /// @}
37 
38 /// This template class represents DHCP option with single value.
39 /// This value is of integer type and can be any of the following:
40 /// - uint8_t,
41 /// - uint16_t,
42 /// - uint32_t,
43 /// - int8_t,
44 /// - int16_t,
45 /// - int32_t.
46 ///
47 /// @param T data field type (see above).
48 template<typename T>
49 class OptionInt: public Option {
50 private:
51 
52     /// @brief Pointer to the option object for a specified type T.
53     typedef boost::shared_ptr<OptionInt<T> > OptionIntTypePtr;
54 
55 public:
56     /// @brief Constructor.
57     ///
58     /// @param u universe (V4 or V6)
59     /// @param type option type.
60     /// @param value option value.
61     ///
62     /// @throw isc::dhcp::InvalidDataType if data field type provided
63     /// as template parameter is not a supported integer type.
64     /// @todo Extend constructor to set encapsulated option space name.
OptionInt(Option::Universe u,uint16_t type,T value)65     OptionInt(Option::Universe u, uint16_t type, T value)
66         : Option(u, type), value_(value) {
67         if (!OptionDataTypeTraits<T>::integer_type) {
68             isc_throw(dhcp::InvalidDataType, "non-integer type");
69         }
70         setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE);
71     }
72 
73     /// @brief Constructor.
74     ///
75     /// This constructor creates option from a buffer. This constructor
76     /// may throw exception if \ref unpack function throws during buffer
77     /// parsing.
78     ///
79     /// @param u universe (V4 or V6)
80     /// @param type option type.
81     /// @param begin iterator to first byte of option data.
82     /// @param end iterator to end of option data (first byte after option end).
83     ///
84     /// @throw isc::OutOfRange if provided buffer is shorter than data size.
85     /// @throw isc::dhcp::InvalidDataType if data field type provided
86     /// as template parameter is not a supported integer type.
87     /// @todo Extend constructor to set encapsulated option space name.
OptionInt(Option::Universe u,uint16_t type,OptionBufferConstIter begin,OptionBufferConstIter end)88     OptionInt(Option::Universe u, uint16_t type, OptionBufferConstIter begin,
89                OptionBufferConstIter end)
90         : Option(u, type) {
91         if (!OptionDataTypeTraits<T>::integer_type) {
92             isc_throw(dhcp::InvalidDataType, "non-integer type");
93         }
94         setEncapsulatedSpace(u == Option::V4 ? DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE);
95         unpack(begin, end);
96     }
97 
98     /// @brief Copies this option and returns a pointer to the copy.
clone()99     virtual OptionPtr clone() const {
100         return (cloneInternal<OptionInt<T> >());
101     }
102 
103     /// Writes option in wire-format to buf, returns pointer to first unused
104     /// byte after stored option.
105     ///
106     /// @param [out] buf buffer (option will be stored here)
107     ///
108     /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
109     /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
110     /// because it is checked in a constructor.
pack(isc::util::OutputBuffer & buf)111     void pack(isc::util::OutputBuffer& buf) const {
112         // Pack option header.
113         packHeader(buf);
114         // Depending on the data type length we use different utility functions
115         // writeUint16 or writeUint32 which write the data in the network byte
116         // order to the provided buffer. The same functions can be safely used
117         // for either unsigned or signed integers so there is not need to create
118         // special cases for intX_t types.
119         switch (OptionDataTypeTraits<T>::len) {
120         case 1:
121             buf.writeUint8(value_);
122             break;
123         case 2:
124             buf.writeUint16(value_);
125             break;
126         case 4:
127             buf.writeUint32(value_);
128             break;
129         default:
130             isc_throw(dhcp::InvalidDataType, "non-integer type");
131         }
132         packOptions(buf);
133     }
134 
135     /// @brief Parses received buffer
136     ///
137     /// Parses received buffer and returns offset to the first unused byte after
138     /// parsed option.
139     ///
140     /// @param begin iterator to first byte of option data
141     /// @param end iterator to end of option data (first byte after option end)
142     ///
143     /// @throw isc::OutOfRange if provided buffer is shorter than data size.
144     /// @throw isc::dhcp::InvalidDataType if size of a data field type is not
145     /// equal to 1, 2 or 4 bytes. The data type is not checked in this function
146     /// because it is checked in a constructor.
unpack(OptionBufferConstIter begin,OptionBufferConstIter end)147     virtual void unpack(OptionBufferConstIter begin, OptionBufferConstIter end) {
148         if (distance(begin, end) < sizeof(T)) {
149             isc_throw(OutOfRange, "OptionInt " << getType() << " truncated");
150         }
151         // @todo consider what to do if buffer is longer than data type.
152 
153         // Depending on the data type length we use different utility functions
154         // readUint16 or readUint32 which read the data laid in the network byte
155         // order from the provided buffer. The same functions can be safely used
156         // for either unsigned or signed integers so there is not need to create
157         // special cases for intX_t types.
158         int data_size_len = OptionDataTypeTraits<T>::len;
159         switch (data_size_len) {
160         case 1:
161             value_ = *begin;
162             break;
163         case 2:
164             value_ = isc::util::readUint16(&(*begin),
165                                            std::distance(begin, end));
166             break;
167         case 4:
168             value_ = isc::util::readUint32(&(*begin),
169                                            std::distance(begin, end));
170             break;
171         default:
172             isc_throw(dhcp::InvalidDataType, "non-integer type");
173         }
174         // Use local variable to set a new value for this iterator.
175         // When using OptionDataTypeTraits<T>::len directly some versions
176         // of clang complain about unresolved reference to
177         // OptionDataTypeTraits structure during linking.
178         begin += data_size_len;
179         unpackOptions(OptionBuffer(begin, end));
180     }
181 
182     /// @brief Set option value.
183     ///
184     /// @param value new option value.
setValue(T value)185     void setValue(T value) { value_ = value; }
186 
187     /// @brief Return option value.
188     ///
189     /// @return option value.
getValue()190     T getValue() const { return value_; }
191 
192     /// @brief returns complete length of option
193     ///
194     /// Returns length of this option, including option header and suboptions
195     ///
196     /// @return length of this option
len()197     virtual uint16_t len() const {
198         // Calculate the length of the header.
199         uint16_t length = (getUniverse() == Option::V4) ? OPTION4_HDR_LEN : OPTION6_HDR_LEN;
200         // The data length is equal to size of T.
201         length += sizeof(T);;
202         // length of all suboptions
203         for (OptionCollection::const_iterator it = options_.begin();
204              it != options_.end();
205              ++it) {
206             length += (*it).second->len();
207         }
208         return (length);
209     }
210 
211     /// @brief Returns option carrying an integer value in the textual
212     /// format.
213     ///
214     /// The returned value also includes the suboptions if present.
215     ///
216     /// @param indent Number of spaces to be inserted before the text.
217     virtual std::string toText(int indent = 0) const {
218         std::stringstream output;
219         output << headerToText(indent) << ": ";
220 
221         // For 1 byte long data types we need to cast to the integer
222         // because they are usually implemented as "char" types, in
223         // which case the character rather than number would be printed.
224         if (OptionDataTypeTraits<T>::len == 1) {
225             output << static_cast<int>(getValue());
226         } else {
227             output << getValue();
228         }
229 
230         // Append data type name.
231         output << " ("
232                << OptionDataTypeUtil::getDataTypeName(OptionDataTypeTraits<T>::type)
233                << ")";
234 
235         // Append suboptions.
236         output << suboptionsToText(indent + 2);
237 
238         return (output.str());
239     }
240 
241 private:
242 
243     T value_;  ///< Value conveyed by the option.
244 };
245 
246 } // isc::dhcp namespace
247 } // isc namespace
248 
249 #endif // OPTION_INT_H
250