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