libtins  3.4
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Pages
pdu_option.h
1 /*
2  * Copyright (c) 2016, 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 <iterator>
35 #include <cstring>
36 #include <algorithm>
37 #include <string>
38 #include <limits>
39 #include <stdint.h>
40 #include "exceptions.h"
41 #include "endianness.h"
42 #include "internals.h"
43 #include "ip_address.h"
44 #include "ipv6_address.h"
45 #include "hw_address.h"
46 
47 namespace Tins {
51 template <typename OptionType, typename PDUType>
52 class PDUOption;
53 
54 namespace Internals {
55  template <typename T, typename X, typename PDUType>
56  T convert_to_integral(const PDUOption<X, PDUType> & opt) {
57  if (opt.data_size() != sizeof(T)) {
58  throw malformed_option();
59  }
60  T data = *(T*)opt.data_ptr();
61  if (PDUType::endianness == PDUType::BE) {
62  data = Endian::be_to_host(data);
63  }
64  else {
65  data = Endian::le_to_host(data);
66  }
67  return data;
68  }
69 
70  template <typename T, typename = void>
71  struct converter {
72  template <typename X, typename PDUType>
73  static T convert(const PDUOption<X, PDUType>& opt) {
74  return T::from_option(opt);
75  }
76  };
77 
78  template <>
79  struct converter<uint8_t> {
80  template <typename X, typename PDUType>
81  static uint8_t convert(const PDUOption<X, PDUType>& opt) {
82  if (opt.data_size() != 1) {
83  throw malformed_option();
84  }
85  return* opt.data_ptr();
86  }
87  };
88 
89  template<>
90  struct converter<uint16_t> {
91  template<typename X, typename PDUType>
92  static uint16_t convert(const PDUOption<X, PDUType>& opt) {
93  return convert_to_integral<uint16_t>(opt);
94  }
95  };
96 
97  template<>
98  struct converter<uint32_t> {
99  template<typename X, typename PDUType>
100  static uint32_t convert(const PDUOption<X, PDUType>& opt) {
101  return convert_to_integral<uint32_t>(opt);
102  }
103  };
104 
105  template<>
106  struct converter<uint64_t> {
107  template<typename X, typename PDUType>
108  static uint64_t convert(const PDUOption<X, PDUType>& opt) {
109  return convert_to_integral<uint64_t>(opt);
110  }
111  };
112 
113  template<size_t n>
114  struct converter<HWAddress<n> > {
115  template<typename X, typename PDUType>
116  static HWAddress<n> convert(const PDUOption<X, PDUType>& opt) {
117  if (opt.data_size() != n) {
118  throw malformed_option();
119  }
120  return HWAddress<n>(opt.data_ptr());
121  }
122  };
123 
124  template<>
125  struct converter<IPv4Address> {
126  template<typename X, typename PDUType>
127  static IPv4Address convert(const PDUOption<X, PDUType>& opt) {
128  if (opt.data_size() != sizeof(uint32_t)) {
129  throw malformed_option();
130  }
131  const uint32_t* ptr = (const uint32_t*)opt.data_ptr();
132  if (PDUType::endianness == PDUType::BE) {
133  return IPv4Address(*ptr);
134  }
135  else {
136  return IPv4Address(Endian::change_endian(*ptr));
137  }
138  }
139  };
140 
141  template<>
142  struct converter<IPv6Address> {
143  template<typename X, typename PDUType>
144  static IPv6Address convert(const PDUOption<X, PDUType>& opt) {
145  if (opt.data_size() != IPv6Address::address_size) {
146  throw malformed_option();
147  }
148  return IPv6Address(opt.data_ptr());
149  }
150  };
151 
152  template<>
153  struct converter<std::string> {
154  template<typename X, typename PDUType>
155  static std::string convert(const PDUOption<X, PDUType>& opt) {
156  return std::string(
157  opt.data_ptr(),
158  opt.data_ptr() + opt.data_size()
159  );
160  }
161  };
162 
163  template<>
164  struct converter<std::vector<float> > {
165  template<typename X, typename PDUType>
166  static std::vector<float> convert(const PDUOption<X, PDUType>& opt) {
167  std::vector<float> output;
168  const uint8_t* ptr = opt.data_ptr(), *end = ptr + opt.data_size();
169  while (ptr != end) {
170  output.push_back(float(*(ptr++) & 0x7f) / 2);
171  }
172  return output;
173  }
174  };
175 
176  template<typename T>
177  struct converter<std::vector<T>, typename enable_if<is_unsigned_integral<T>::value>::type> {
178  template<typename X, typename PDUType>
179  static std::vector<T> convert(const PDUOption<X, PDUType>& opt) {
180  if (opt.data_size() % sizeof(T) != 0) {
181  throw malformed_option();
182  }
183  const T* ptr = (const T*)opt.data_ptr();
184  const T* end = (const T*)(opt.data_ptr() + opt.data_size());
185 
186  std::vector<T> output(std::distance(ptr, end));
187  typename std::vector<T>::iterator it = output.begin();
188  while (ptr < end) {
189  if (PDUType::endianness == PDUType::BE) {
190  *it++ = Endian::be_to_host(*ptr++);
191  }
192  else {
193  *it++ = Endian::le_to_host(*ptr++);
194  }
195  }
196  return output;
197  }
198  };
199 
200  template<typename T, typename U>
201  struct converter<
202  std::vector<std::pair<T, U> >,
203  typename enable_if<
204  is_unsigned_integral<T>::value && is_unsigned_integral<U>::value
205  >::type
206  > {
207  template<typename X, typename PDUType>
208  static std::vector<std::pair<T, U> > convert(const PDUOption<X, PDUType>& opt) {
209  if (opt.data_size() % (sizeof(T) + sizeof(U)) != 0) {
210  throw malformed_option();
211  }
212  const uint8_t* ptr = opt.data_ptr(), *end = ptr + opt.data_size();
213 
214  std::vector<std::pair<T, U> > output;
215  while (ptr < end) {
216  std::pair<T, U> data;
217  data.first = *(const T*)ptr;
218  ptr += sizeof(T);
219  data.second = *(const U*)ptr;
220  ptr += sizeof(U);
221  if (PDUType::endianness == PDUType::BE) {
222  data.first = Endian::be_to_host(data.first);
223  data.second = Endian::be_to_host(data.second);
224  }
225  else {
226  data.first = Endian::le_to_host(data.first);
227  data.second = Endian::le_to_host(data.second);
228  }
229  output.push_back(data);
230  }
231  return output;
232  }
233  };
234 
235  template<>
236  struct converter<std::vector<IPv4Address> > {
237  template<typename X, typename PDUType>
238  static std::vector<IPv4Address> convert(const PDUOption<X, PDUType>& opt) {
239  if (opt.data_size() % 4 != 0) {
240  throw malformed_option();
241  }
242  const uint32_t* ptr = (const uint32_t*)opt.data_ptr();
243  const uint32_t* end = (const uint32_t*)(opt.data_ptr() + opt.data_size());
244 
245  std::vector<IPv4Address> output(std::distance(ptr, end));
246  std::vector<IPv4Address>::iterator it = output.begin();
247  while (ptr < end) {
248  if (PDUType::endianness == PDUType::BE) {
249  *it++ = IPv4Address(*ptr++);
250  }
251  else {
252  *it++ = IPv4Address(Endian::change_endian(*ptr++));
253  }
254  }
255  return output;
256  }
257  };
258 
259  template<>
260  struct converter<std::vector<IPv6Address> > {
261  template<typename X, typename PDUType>
262  static std::vector<IPv6Address> convert(const PDUOption<X, PDUType>& opt) {
263  if (opt.data_size() % IPv6Address::address_size != 0) {
264  throw malformed_option();
265  }
266  const uint8_t* ptr = opt.data_ptr(), *end = opt.data_ptr() + opt.data_size();
267  std::vector<IPv6Address> output;
268  while (ptr < end) {
269  output.push_back(IPv6Address(ptr));
270  ptr += IPv6Address::address_size;
271  }
272  return output;
273  }
274  };
275 
276  template<typename T, typename U>
277  struct converter<
278  std::pair<T, U>,
279  typename enable_if<
280  is_unsigned_integral<T>::value && is_unsigned_integral<U>::value
281  >::type
282  > {
283  template<typename X, typename PDUType>
284  static std::pair<T, U> convert(const PDUOption<X, PDUType>& opt) {
285  if (opt.data_size() != sizeof(T) + sizeof(U)) {
286  throw malformed_option();
287  }
288  std::pair<T, U> output;
289  std::memcpy(&output.first, opt.data_ptr(), sizeof(T));
290  std::memcpy(&output.second, opt.data_ptr() + sizeof(T), sizeof(U));
291  if (PDUType::endianness == PDUType::BE) {
292  output.first = Endian::be_to_host(output.first);
293  output.second = Endian::be_to_host(output.second);
294  }
295  else {
296  output.first = Endian::le_to_host(output.first);
297  output.second = Endian::le_to_host(output.second);
298  }
299  return output;
300  }
301  };
302 }
303 
319 template <typename OptionType, typename PDUType>
320 class PDUOption {
321 private:
322  static const int small_buffer_size = 8;
323 public:
324  typedef uint8_t data_type;
325  typedef OptionType option_type;
326 
333  PDUOption(option_type opt = option_type(),
334  size_t length = 0,
335  const data_type* data = 0)
336  : option_(opt), size_(static_cast<uint16_t>(length)), real_size_(0) {
337  if (data != 0) {
338  set_payload_contents(data, data + length);
339  }
340  }
341 
346  PDUOption(const PDUOption& rhs) {
347  real_size_ = 0;
348  *this = rhs;
349  }
350 
351  #if TINS_IS_CXX11
352 
357  real_size_ = 0;
358  *this = std::move(rhs);
359  }
360 
366  option_ = rhs.option_;
367  size_ = rhs.size_;
368  if (real_size_ > small_buffer_size) {
369  delete[] payload_.big_buffer_ptr;
370  }
371  real_size_ = rhs.real_size_;
372  if (real_size_ > small_buffer_size) {
373  payload_.big_buffer_ptr = 0;
374  std::swap(payload_.big_buffer_ptr, rhs.payload_.big_buffer_ptr);
375  rhs.real_size_ = 0;
376  }
377  else {
378  std::copy(
379  rhs.data_ptr(),
380  rhs.data_ptr() + rhs.data_size(),
381  payload_.small_buffer
382  );
383  }
384  return* this;
385  }
386 
387  #endif // TINS_IS_CXX11
388 
394  option_ = rhs.option_;
395  size_ = rhs.size_;
396  if (real_size_ > small_buffer_size) {
397  delete[] payload_.big_buffer_ptr;
398  }
399  real_size_ = rhs.real_size_;
400  set_payload_contents(rhs.data_ptr(), rhs.data_ptr() + rhs.data_size());
401  return* this;
402  }
403 
408  if (real_size_ > small_buffer_size) {
409  delete[] payload_.big_buffer_ptr;
410  }
411  }
412 
421  template<typename ForwardIterator>
422  PDUOption(option_type opt, ForwardIterator start, ForwardIterator end)
423  : option_(opt), size_(static_cast<uint16_t>(std::distance(start, end))) {
424  set_payload_contents(start, end);
425  }
426 
442  template<typename ForwardIterator>
443  PDUOption(option_type opt, uint16_t length, ForwardIterator start, ForwardIterator end)
444  : option_(opt), size_(length) {
445  set_payload_contents(start, end);
446  }
447 
452  option_type option() const {
453  return option_;
454  }
455 
460  void option(option_type opt) {
461  option_ = opt;
462  }
463 
473  const data_type* data_ptr() const {
474  return real_size_ <= small_buffer_size ?
475  payload_.small_buffer :
476  payload_.big_buffer_ptr;
477  }
478 
484  size_t data_size() const {
485  return real_size_;
486  }
487 
500  size_t length_field() const {
501  return size_;
502  }
503 
511  template<typename T>
512  T to() const {
513  return Internals::converter<T>::convert(*this);
514  }
515 private:
516  template<typename ForwardIterator>
517  void set_payload_contents(ForwardIterator start, ForwardIterator end) {
518  size_t total_size = std::distance(start, end);
519  if (total_size > std::numeric_limits<uint16_t>::max()) {
520  throw option_payload_too_large();
521  }
522  real_size_ = static_cast<uint16_t>(total_size);
523  if (real_size_ <= small_buffer_size) {
524  std::copy(
525  start,
526  end,
527  payload_.small_buffer
528  );
529  }
530  else {
531  payload_.big_buffer_ptr = new data_type[real_size_];
532  uint8_t* ptr = payload_.big_buffer_ptr;
533  while (start < end) {
534  *ptr = *start;
535  ++ptr;
536  ++start;
537  }
538  }
539  }
540 
541  option_type option_;
542  uint16_t size_, real_size_;
543  union {
544  data_type small_buffer[small_buffer_size];
545  data_type* big_buffer_ptr;
546  } payload_;
547 };
548 
549 namespace Internals {
550  /*
551  * \cond
552  */
553  template <typename Option>
554  struct option_type_equality_comparator {
555  option_type_equality_comparator(typename Option::option_type type) : type(type) { }
556 
557  bool operator()(const Option& opt) const {
558  return opt.option() == type;
559  }
560 
561  typename Option::option_type type;
562  };
563  /*
564  * \endcond
565  */
566 } // Internals
567 
568 } // namespace Tins
569 #endif // TINS_PDU_OPTION_H
~PDUOption()
Destructor.
Definition: pdu_option.h:407
size_t length_field() const
Retrieves the data length field.
Definition: pdu_option.h:500
PDUOption & operator=(PDUOption &&rhs)
Move assignment operator.
Definition: pdu_option.h:365
Represents a PDU option field.
Definition: pdu_option.h:320
const data_type * data_ptr() const
Definition: pdu_option.h:473
PDUOption(option_type opt=option_type(), size_t length=0, const data_type *data=0)
Constructs a PDUOption.
Definition: pdu_option.h:333
PDUOption(const PDUOption &rhs)
Copy constructor.
Definition: pdu_option.h:346
void option(option_type opt)
Definition: pdu_option.h:460
T to() const
Constructs a T from this PDUOption.
Definition: pdu_option.h:512
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:422
PDUOption(PDUOption &&rhs)
Move constructor.
Definition: pdu_option.h:356
option_type option() const
Definition: pdu_option.h:452
PDUOption & operator=(const PDUOption &rhs)
Copy assignment operator.
Definition: pdu_option.h:393
size_t data_size() const
Retrieves the length of this option's data.
Definition: pdu_option.h:484
Exception thrown when a payload is too large to fit into a PDUOption.
Definition: exceptions.h:218
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:443