Buffer.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. /*
  2. * Copyright (c)2019 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2026-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #ifndef ZT_BUFFER_HPP
  14. #define ZT_BUFFER_HPP
  15. #include "Constants.hpp"
  16. #include "Utils.hpp"
  17. #include <algorithm>
  18. #include <stdexcept>
  19. #include <stdint.h>
  20. #include <string.h>
  21. #include <string>
  22. #include <utility>
  23. #if defined(__GNUC__) && (! defined(ZT_NO_TYPE_PUNNING))
  24. #define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
  25. #else
  26. #define ZT_VAR_MAY_ALIAS
  27. #endif
  28. namespace ZeroTier {
  29. /**
  30. * A variable length but statically allocated buffer
  31. *
  32. * Bounds-checking is done everywhere, since this is used in security
  33. * critical code. This supports construction and assignment from buffers
  34. * of differing capacities, provided the data actually in them fits.
  35. * It throws std::out_of_range on any boundary violation.
  36. *
  37. * The at(), append(), etc. methods encode integers larger than 8-bit in
  38. * big-endian (network) byte order.
  39. *
  40. * @tparam C Total capacity
  41. */
  42. template <unsigned int C> class Buffer {
  43. // I love me!
  44. template <unsigned int C2> friend class Buffer;
  45. public:
  46. // STL container idioms
  47. typedef unsigned char value_type;
  48. typedef unsigned char* pointer;
  49. typedef const char* const_pointer;
  50. typedef char& reference;
  51. typedef const char& const_reference;
  52. typedef char* iterator;
  53. typedef const char* const_iterator;
  54. typedef unsigned int size_type;
  55. typedef int difference_type;
  56. typedef std::reverse_iterator<iterator> reverse_iterator;
  57. typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
  58. inline iterator begin()
  59. {
  60. return _b;
  61. }
  62. inline iterator end()
  63. {
  64. return (_b + _l);
  65. }
  66. inline const_iterator begin() const
  67. {
  68. return _b;
  69. }
  70. inline const_iterator end() const
  71. {
  72. return (_b + _l);
  73. }
  74. inline reverse_iterator rbegin()
  75. {
  76. return reverse_iterator(begin());
  77. }
  78. inline reverse_iterator rend()
  79. {
  80. return reverse_iterator(end());
  81. }
  82. inline const_reverse_iterator rbegin() const
  83. {
  84. return const_reverse_iterator(begin());
  85. }
  86. inline const_reverse_iterator rend() const
  87. {
  88. return const_reverse_iterator(end());
  89. }
  90. Buffer() : _l(0)
  91. {
  92. }
  93. Buffer(unsigned int l)
  94. {
  95. if (l > C) {
  96. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  97. }
  98. _l = l;
  99. }
  100. template <unsigned int C2> Buffer(const Buffer<C2>& b)
  101. {
  102. *this = b;
  103. }
  104. Buffer(const void* b, unsigned int l)
  105. {
  106. copyFrom(b, l);
  107. }
  108. template <unsigned int C2> inline Buffer& operator=(const Buffer<C2>& b)
  109. {
  110. if (unlikely(b._l > C)) {
  111. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  112. }
  113. if (C2 == C) {
  114. memcpy(this, &b, sizeof(Buffer<C>));
  115. }
  116. else {
  117. memcpy(_b, b._b, _l = b._l);
  118. }
  119. return *this;
  120. }
  121. inline void copyFrom(const void* b, unsigned int l)
  122. {
  123. if (unlikely(l > C)) {
  124. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  125. }
  126. memcpy(_b, b, l);
  127. _l = l;
  128. }
  129. unsigned char operator[](const unsigned int i) const
  130. {
  131. if (unlikely(i >= _l)) {
  132. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  133. }
  134. return (unsigned char)_b[i];
  135. }
  136. unsigned char& operator[](const unsigned int i)
  137. {
  138. if (unlikely(i >= _l)) {
  139. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  140. }
  141. return ((unsigned char*)_b)[i];
  142. }
  143. /**
  144. * Get a raw pointer to a field with bounds checking
  145. *
  146. * This isn't perfectly safe in that the caller could still overflow
  147. * the pointer, but its use provides both a sanity check and
  148. * documentation / reminder to the calling code to treat the returned
  149. * pointer as being of size [l].
  150. *
  151. * @param i Index of field in buffer
  152. * @param l Length of field in bytes
  153. * @return Pointer to field data
  154. * @throws std::out_of_range Field extends beyond data size
  155. */
  156. unsigned char* field(unsigned int i, unsigned int l)
  157. {
  158. if (unlikely((i + l) > _l)) {
  159. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  160. }
  161. return (unsigned char*)(_b + i);
  162. }
  163. const unsigned char* field(unsigned int i, unsigned int l) const
  164. {
  165. if (unlikely((i + l) > _l)) {
  166. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  167. }
  168. return (const unsigned char*)(_b + i);
  169. }
  170. /**
  171. * Place a primitive integer value at a given position
  172. *
  173. * @param i Index to place value
  174. * @param v Value
  175. * @tparam T Integer type (e.g. uint16_t, int64_t)
  176. */
  177. template <typename T> inline void setAt(unsigned int i, const T v)
  178. {
  179. if (unlikely((i + sizeof(T)) > _l)) {
  180. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  181. }
  182. #ifdef ZT_NO_TYPE_PUNNING
  183. uint8_t* p = reinterpret_cast<uint8_t*>(_b + i);
  184. for (unsigned int x = 1; x <= sizeof(T); ++x) {
  185. *(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
  186. }
  187. #else
  188. T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T*>(_b + i);
  189. *p = Utils::hton(v);
  190. #endif
  191. }
  192. /**
  193. * Get a primitive integer value at a given position
  194. *
  195. * @param i Index to get integer
  196. * @tparam T Integer type (e.g. uint16_t, int64_t)
  197. * @return Integer value
  198. */
  199. template <typename T> inline T at(unsigned int i) const
  200. {
  201. if (unlikely((i + sizeof(T)) > _l)) {
  202. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  203. }
  204. #ifdef ZT_NO_TYPE_PUNNING
  205. T v = 0;
  206. const uint8_t* p = reinterpret_cast<const uint8_t*>(_b + i);
  207. for (unsigned int x = 0; x < sizeof(T); ++x) {
  208. v <<= 8;
  209. v |= (T) * (p++);
  210. }
  211. return v;
  212. #else
  213. const T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T*>(_b + i);
  214. return Utils::ntoh(*p);
  215. #endif
  216. }
  217. /**
  218. * Append an integer type to this buffer
  219. *
  220. * @param v Value to append
  221. * @tparam T Integer type (e.g. uint16_t, int64_t)
  222. * @throws std::out_of_range Attempt to append beyond capacity
  223. */
  224. template <typename T> inline void append(const T v)
  225. {
  226. if (unlikely((_l + sizeof(T)) > C)) {
  227. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  228. }
  229. #ifdef ZT_NO_TYPE_PUNNING
  230. uint8_t* p = reinterpret_cast<uint8_t*>(_b + _l);
  231. for (unsigned int x = 1; x <= sizeof(T); ++x) {
  232. *(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));
  233. }
  234. #else
  235. T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T*>(_b + _l);
  236. *p = Utils::hton(v);
  237. #endif
  238. _l += sizeof(T);
  239. }
  240. /**
  241. * Append a run of bytes
  242. *
  243. * @param c Character value to append
  244. * @param n Number of times to append
  245. * @throws std::out_of_range Attempt to append beyond capacity
  246. */
  247. inline void append(unsigned char c, unsigned int n)
  248. {
  249. if (unlikely((_l + n) > C)) {
  250. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  251. }
  252. for (unsigned int i = 0; i < n; ++i) {
  253. _b[_l++] = (char)c;
  254. }
  255. }
  256. /**
  257. * Append secure random bytes
  258. *
  259. * @param n Number of random bytes to append
  260. */
  261. inline void appendRandom(unsigned int n)
  262. {
  263. if (unlikely((_l + n) > C)) {
  264. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  265. }
  266. Utils::getSecureRandom(_b + _l, n);
  267. _l += n;
  268. }
  269. /**
  270. * Append a C-array of bytes
  271. *
  272. * @param b Data
  273. * @param l Length
  274. * @throws std::out_of_range Attempt to append beyond capacity
  275. */
  276. inline void append(const void* b, unsigned int l)
  277. {
  278. if (unlikely((_l + l) > C)) {
  279. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  280. }
  281. memcpy(_b + _l, b, l);
  282. _l += l;
  283. }
  284. /**
  285. * Append a C string including null termination byte
  286. *
  287. * @param s C string
  288. * @throws std::out_of_range Attempt to append beyond capacity
  289. */
  290. inline void appendCString(const char* s)
  291. {
  292. for (;;) {
  293. if (unlikely(_l >= C)) {
  294. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  295. }
  296. if (! (_b[_l++] = *(s++))) {
  297. break;
  298. }
  299. }
  300. }
  301. /**
  302. * Append a buffer
  303. *
  304. * @param b Buffer to append
  305. * @tparam C2 Capacity of second buffer (typically inferred)
  306. * @throws std::out_of_range Attempt to append beyond capacity
  307. */
  308. template <unsigned int C2> inline void append(const Buffer<C2>& b)
  309. {
  310. append(b._b, b._l);
  311. }
  312. /**
  313. * Increment size and return pointer to field of specified size
  314. *
  315. * Nothing is actually written to the memory. This is a shortcut
  316. * for addSize() followed by field() to reference the previous
  317. * position and the new size.
  318. *
  319. * @param l Length of field to append
  320. * @return Pointer to beginning of appended field of length 'l'
  321. */
  322. inline char* appendField(unsigned int l)
  323. {
  324. if (unlikely((_l + l) > C)) {
  325. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  326. }
  327. char* r = _b + _l;
  328. _l += l;
  329. return r;
  330. }
  331. /**
  332. * Increment size by a given number of bytes
  333. *
  334. * The contents of new space are undefined.
  335. *
  336. * @param i Bytes to increment
  337. * @throws std::out_of_range Capacity exceeded
  338. */
  339. inline void addSize(unsigned int i)
  340. {
  341. if (unlikely((i + _l) > C)) {
  342. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  343. }
  344. _l += i;
  345. }
  346. /**
  347. * Set size of data in buffer
  348. *
  349. * The contents of new space are undefined.
  350. *
  351. * @param i New size
  352. * @throws std::out_of_range Size larger than capacity
  353. */
  354. inline void setSize(const unsigned int i)
  355. {
  356. if (unlikely(i > C)) {
  357. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  358. }
  359. _l = i;
  360. }
  361. /**
  362. * Move everything after 'at' to the buffer's front and truncate
  363. *
  364. * @param at Truncate before this position
  365. * @throws std::out_of_range Position is beyond size of buffer
  366. */
  367. inline void behead(const unsigned int at)
  368. {
  369. if (! at) {
  370. return;
  371. }
  372. if (unlikely(at > _l)) {
  373. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  374. }
  375. ::memmove(_b, _b + at, _l -= at);
  376. }
  377. /**
  378. * Erase something from the middle of the buffer
  379. *
  380. * @param start Starting position
  381. * @param length Length of block to erase
  382. * @throws std::out_of_range Position plus length is beyond size of buffer
  383. */
  384. inline void erase(const unsigned int at, const unsigned int length)
  385. {
  386. const unsigned int endr = at + length;
  387. if (unlikely(endr > _l)) {
  388. throw ZT_EXCEPTION_OUT_OF_BOUNDS;
  389. }
  390. ::memmove(_b + at, _b + endr, _l - endr);
  391. _l -= length;
  392. }
  393. /**
  394. * Set buffer data length to zero
  395. */
  396. inline void clear()
  397. {
  398. _l = 0;
  399. }
  400. /**
  401. * Zero buffer up to size()
  402. */
  403. inline void zero()
  404. {
  405. memset(_b, 0, _l);
  406. }
  407. /**
  408. * Zero unused capacity area
  409. */
  410. inline void zeroUnused()
  411. {
  412. memset(_b + _l, 0, C - _l);
  413. }
  414. /**
  415. * Unconditionally and securely zero buffer's underlying memory
  416. */
  417. inline void burn()
  418. {
  419. Utils::burn(_b, sizeof(_b));
  420. }
  421. /**
  422. * @return Constant pointer to data in buffer
  423. */
  424. inline const void* data() const
  425. {
  426. return _b;
  427. }
  428. /**
  429. * @return Non-constant pointer to data in buffer
  430. */
  431. inline void* unsafeData()
  432. {
  433. return _b;
  434. }
  435. /**
  436. * @return Size of data in buffer
  437. */
  438. inline unsigned int size() const
  439. {
  440. return _l;
  441. }
  442. /**
  443. * @return Capacity of buffer
  444. */
  445. inline unsigned int capacity() const
  446. {
  447. return C;
  448. }
  449. template <unsigned int C2> inline bool operator==(const Buffer<C2>& b) const
  450. {
  451. return ((_l == b._l) && (! memcmp(_b, b._b, _l)));
  452. }
  453. template <unsigned int C2> inline bool operator!=(const Buffer<C2>& b) const
  454. {
  455. return ((_l != b._l) || (memcmp(_b, b._b, _l)));
  456. }
  457. template <unsigned int C2> inline bool operator<(const Buffer<C2>& b) const
  458. {
  459. return (memcmp(_b, b._b, std::min(_l, b._l)) < 0);
  460. }
  461. template <unsigned int C2> inline bool operator>(const Buffer<C2>& b) const
  462. {
  463. return (b < *this);
  464. }
  465. template <unsigned int C2> inline bool operator<=(const Buffer<C2>& b) const
  466. {
  467. return ! (b < *this);
  468. }
  469. template <unsigned int C2> inline bool operator>=(const Buffer<C2>& b) const
  470. {
  471. return ! (*this < b);
  472. }
  473. private:
  474. char ZT_VAR_MAY_ALIAS _b[C];
  475. unsigned int _l;
  476. };
  477. } // namespace ZeroTier
  478. #endif