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