libtins  4.0
pdu_option.h
1 /*
2  * Copyright (c) 2017, Matias Fontanini
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 #ifndef TINS_PDU_OPTION_H
31 #define TINS_PDU_OPTION_H
32 
33 #include <vector>
34 #include <string>
35 #include <cstring>
36 #include <stdint.h>
37 #include <tins/exceptions.h>
38 #include <tins/detail/type_traits.h>
39 
40 namespace Tins {
41 
42 class IPv4Address;
43 class IPv6Address;
44 template <size_t n>
45 class HWAddress;
46 
50 template <typename OptionType, typename PDUType>
51 class PDUOption;
52 
53 namespace Internals {
54  namespace Converters {
55  uint8_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
56  type_to_type<uint8_t>);
57  int8_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
58  type_to_type<int8_t>);
59  uint16_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
60  type_to_type<uint16_t>);
61  uint32_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
62  type_to_type<uint32_t>);
63  uint64_t convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
64  type_to_type<uint64_t>);
65  HWAddress<6> convert(const uint8_t* ptr, uint32_t data_size,
66  PDU::endian_type endian, type_to_type<HWAddress<6> >);
67  IPv4Address convert(const uint8_t* ptr, uint32_t data_size,
68  PDU::endian_type endian, type_to_type<IPv4Address>);
69  IPv6Address convert(const uint8_t* ptr, uint32_t data_size, PDU::endian_type endian,
70  type_to_type<IPv6Address>);
71  std::string convert(const uint8_t* ptr, uint32_t data_size,
72  PDU::endian_type endian, type_to_type<std::string>);
73  std::vector<float> convert(const uint8_t* ptr, uint32_t data_size,
74  PDU::endian_type endian, type_to_type<std::vector<float> >);
75  std::vector<uint8_t> convert(const uint8_t* ptr, uint32_t data_size,
76  PDU::endian_type endian, type_to_type<std::vector<uint8_t> >);
77  std::vector<uint16_t> convert(const uint8_t* ptr, uint32_t data_size,
78  PDU::endian_type endian,
79  type_to_type<std::vector<uint16_t> >);
80  std::vector<uint32_t> convert(const uint8_t* ptr, uint32_t data_size,
81  PDU::endian_type endian,
82  type_to_type<std::vector<uint32_t> >);
83  std::vector<IPv4Address> convert(const uint8_t* ptr, uint32_t data_size,
84  PDU::endian_type endian,
85  type_to_type<std::vector<IPv4Address> >);
86  std::vector<IPv6Address> convert(const uint8_t* ptr, uint32_t data_size,
87  PDU::endian_type endian,
88  type_to_type<std::vector<IPv6Address> >);
89  std::vector<std::pair<uint8_t, uint8_t> > convert(const uint8_t* ptr, uint32_t data_size,
90  PDU::endian_type endian,
91  type_to_type<std::vector<std::pair<uint8_t, uint8_t> > >);
92  std::pair<uint8_t, uint8_t> convert(const uint8_t* ptr, uint32_t data_size,
93  PDU::endian_type endian,
94  type_to_type<std::pair<uint8_t, uint8_t> >);
95  std::pair<uint16_t, uint32_t> convert(const uint8_t* ptr, uint32_t data_size,
96  PDU::endian_type endian,
97  type_to_type<std::pair<uint16_t, uint32_t> >);
98  std::pair<uint32_t, uint32_t> convert(const uint8_t* ptr, uint32_t data_size,
99  PDU::endian_type endian,
100  type_to_type<std::pair<uint32_t, uint32_t> >);
101  } // Converters
102 
103  struct converter {
104  template <typename T, typename X, typename PDUType>
105  static T do_convert(const PDUOption<X, PDUType>& opt, type_to_type<T>) {
106  return T::from_option(opt);
107  }
108 
109  template <typename U, typename X, typename PDUType>
110  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<uint8_t> type) {
111  return Converters::convert(opt.data_ptr(), opt.data_size(),
112  PDUType::endianness, type);
113  }
114 
115  template <typename U, typename X, typename PDUType>
116  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<int8_t> type) {
117  return Converters::convert(opt.data_ptr(), opt.data_size(),
118  PDUType::endianness, type);
119  }
120 
121  template <typename U, typename X, typename PDUType>
122  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<uint16_t> type) {
123  return Converters::convert(opt.data_ptr(), opt.data_size(),
124  PDUType::endianness, type);
125  }
126 
127  template <typename U, typename X, typename PDUType>
128  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<uint32_t> type) {
129  return Converters::convert(opt.data_ptr(), opt.data_size(),
130  PDUType::endianness, type);
131  }
132 
133  template <typename U, typename X, typename PDUType>
134  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<uint64_t> type) {
135  return Converters::convert(opt.data_ptr(), opt.data_size(),
136  PDUType::endianness, type);
137  }
138 
139  template <typename U, typename X, typename PDUType>
140  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<HWAddress<6> > type) {
141  return Converters::convert(opt.data_ptr(), opt.data_size(),
142  PDUType::endianness, type);
143  }
144 
145  template <typename U, typename X, typename PDUType>
146  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<IPv4Address> type) {
147  return Converters::convert(opt.data_ptr(), opt.data_size(),
148  PDUType::endianness, type);
149  }
150 
151  template <typename U, typename X, typename PDUType>
152  static U do_convert(const PDUOption<X, PDUType>& opt, type_to_type<IPv6Address> type) {
153  return Converters::convert(opt.data_ptr(), opt.data_size(),
154  PDUType::endianness, type);
155  }
156 
157  template <typename U, typename X, typename PDUType>
158  static U do_convert(const PDUOption<X, PDUType>& opt,
159  type_to_type<std::string> type) {
160  return Converters::convert(opt.data_ptr(), opt.data_size(),
161  PDUType::endianness, type);
162  }
163 
164  template <typename U, typename X, typename PDUType, typename Z>
165  static U do_convert(const PDUOption<X, PDUType>& opt,
166  type_to_type<std::vector<Z> > type) {
167  return Converters::convert(opt.data_ptr(), opt.data_size(),
168  PDUType::endianness, type);
169  }
170 
171  template <typename U, typename X, typename PDUType, typename Z, typename W>
172  static U do_convert(const PDUOption<X, PDUType>& opt,
173  type_to_type<std::pair<Z, W> > type) {
174  return Converters::convert(opt.data_ptr(), opt.data_size(),
175  PDUType::endianness, type);
176  }
177 
178  template <typename T, typename X, typename PDUType>
179  static T convert(const PDUOption<X, PDUType>& opt) {
180  return do_convert<T>(opt, type_to_type<T>());
181  }
182  };
183 }
184 
200 template <typename OptionType, typename PDUType>
201 class PDUOption {
202 private:
203  static const int small_buffer_size = 8;
204 public:
205  typedef uint8_t data_type;
206  typedef OptionType option_type;
207 
214  PDUOption(option_type opt = option_type(),
215  size_t length = 0,
216  const data_type* data = 0)
217  : option_(opt), size_(static_cast<uint16_t>(length)), real_size_(0) {
218  if (data != 0) {
219  set_payload_contents(data, data + length);
220  }
221  }
222 
227  PDUOption(const PDUOption& rhs) {
228  real_size_ = 0;
229  *this = rhs;
230  }
231 
232  #if TINS_IS_CXX11
233 
237  PDUOption(PDUOption&& rhs) TINS_NOEXCEPT {
238  real_size_ = 0;
239  *this = std::move(rhs);
240  }
241 
246  PDUOption& operator=(PDUOption&& rhs) TINS_NOEXCEPT {
247  option_ = rhs.option_;
248  size_ = rhs.size_;
249  if (real_size_ > small_buffer_size) {
250  delete[] payload_.big_buffer_ptr;
251  }
252  real_size_ = rhs.real_size_;
253  if (real_size_ > small_buffer_size) {
254  payload_.big_buffer_ptr = 0;
255  std::swap(payload_.big_buffer_ptr, rhs.payload_.big_buffer_ptr);
256  rhs.real_size_ = 0;
257  }
258  else {
259  std::memcpy(payload_.small_buffer, rhs.data_ptr(), rhs.data_size());
260  }
261  return *this;
262  }
263 
264  #endif // TINS_IS_CXX11
265 
271  option_ = rhs.option_;
272  size_ = rhs.size_;
273  if (real_size_ > small_buffer_size) {
274  delete[] payload_.big_buffer_ptr;
275  }
276  real_size_ = rhs.real_size_;
277  set_payload_contents(rhs.data_ptr(), rhs.data_ptr() + rhs.data_size());
278  return* this;
279  }
280 
285  if (real_size_ > small_buffer_size) {
286  delete[] payload_.big_buffer_ptr;
287  }
288  }
289 
298  template<typename ForwardIterator>
299  PDUOption(option_type opt, ForwardIterator start, ForwardIterator end)
300  : option_(opt), size_(static_cast<uint16_t>(std::distance(start, end))) {
301  set_payload_contents(start, end);
302  }
303 
319  template<typename ForwardIterator>
320  PDUOption(option_type opt, uint16_t length, ForwardIterator start, ForwardIterator end)
321  : option_(opt), size_(length) {
322  set_payload_contents(start, end);
323  }
324 
329  option_type option() const {
330  return option_;
331  }
332 
337  void option(option_type opt) {
338  option_ = opt;
339  }
340 
350  const data_type* data_ptr() const {
351  return real_size_ <= small_buffer_size ?
352  payload_.small_buffer :
353  payload_.big_buffer_ptr;
354  }
355 
361  size_t data_size() const {
362  return real_size_;
363  }
364 
377  size_t length_field() const {
378  return size_;
379  }
380 
388  template<typename T>
389  T to() const {
390  return Internals::converter::convert<T>(*this);
391  }
392 private:
393  template<typename ForwardIterator>
394  void set_payload_contents(ForwardIterator start, ForwardIterator end) {
395  size_t total_size = std::distance(start, end);
396  if (total_size > 65535) {
397  throw option_payload_too_large();
398  }
399  real_size_ = static_cast<uint16_t>(total_size);
400  if (real_size_ <= small_buffer_size) {
401  if (total_size > 0) {
402  std::memcpy(payload_.small_buffer, &*start, total_size);
403  }
404  }
405  else {
406  payload_.big_buffer_ptr = new data_type[real_size_];
407  uint8_t* ptr = payload_.big_buffer_ptr;
408  while (start < end) {
409  *ptr = *start;
410  ++ptr;
411  ++start;
412  }
413  }
414  }
415 
416  option_type option_;
417  uint16_t size_, real_size_;
418  union {
419  data_type small_buffer[small_buffer_size];
420  data_type* big_buffer_ptr;
421  } payload_;
422 };
423 
424 namespace Internals {
425 /*
426  * \cond
427  */
428 
429 template <typename Option, typename Container>
430 typename Container::iterator find_option(Container& cont, typename Option::option_type type) {
431  typename Container::iterator iter;
432  for (iter = cont.begin(); iter != cont.end(); ++iter) {
433  if (iter->option() == type) {
434  break;
435  }
436  }
437  return iter;
438 }
439 
440 template <typename Option, typename Container>
441 typename Container::const_iterator find_option_const(const Container& cont,
442  typename Option::option_type type) {
443  typename Container::const_iterator iter;
444  for (iter = cont.begin(); iter != cont.end(); ++iter) {
445  if (iter->option() == type) {
446  break;
447  }
448  }
449  return iter;
450 }
451 
452 /*
453  * \endcond
454  */
455 } // Internals
456 
457 } // namespace Tins
458 #endif // TINS_PDU_OPTION_H
PDUOption & operator=(PDUOption &&rhs) TINS_NOEXCEPT
Move assignment operator.
Definition: pdu_option.h:246
~PDUOption()
Destructor.
Definition: pdu_option.h:284
size_t length_field() const
Retrieves the data length field.
Definition: pdu_option.h:377
PDUOption(PDUOption &&rhs) TINS_NOEXCEPT
Move constructor.
Definition: pdu_option.h:237
Definition: hw_address.h:456
Represents a PDU option field.
Definition: pdu_option.h:201
const data_type * data_ptr() const
Definition: pdu_option.h:350
PDUOption(option_type opt=option_type(), size_t length=0, const data_type *data=0)
Constructs a PDUOption.
Definition: pdu_option.h:214
PDUOption(const PDUOption &rhs)
Copy constructor.
Definition: pdu_option.h:227
The Tins namespace.
Definition: address_range.h:38
void option(option_type opt)
Definition: pdu_option.h:337
T to() const
Constructs a T from this PDUOption.
Definition: pdu_option.h:389
PDUOption(option_type opt, ForwardIterator start, ForwardIterator end)
Constructs a PDUOption from iterators, which indicate the data to be stored in it.
Definition: pdu_option.h:299
option_type option() const
Definition: pdu_option.h:329
PDUOption & operator=(const PDUOption &rhs)
Copy assignment operator.
Definition: pdu_option.h:270
size_t data_size() const
Retrieves the length of this option&#39;s data.
Definition: pdu_option.h:361
endian_type
Definition: pdu.h:117
Exception thrown when a payload is too large to fit into a PDUOption.
Definition: exceptions.h:202
PDUOption(option_type opt, uint16_t length, ForwardIterator start, ForwardIterator end)
Constructs a PDUOption from iterators, which indicate the data to be stored in it.
Definition: pdu_option.h:320