| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 | /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. * * (c) ZeroTier, Inc. * https://www.zerotier.com/ */#ifndef ZT_BUFFER_HPP#define ZT_BUFFER_HPP#include "Constants.hpp"#include "Utils.hpp"#include <algorithm>#include <stdint.h>#include <string.h>#if defined(__GNUC__) && (! defined(ZT_NO_TYPE_PUNNING))#define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))#else#define ZT_VAR_MAY_ALIAS#endifnamespace ZeroTier {/** * A variable length but statically allocated buffer * * Bounds-checking is done everywhere, since this is used in security * critical code. This supports construction and assignment from buffers * of differing capacities, provided the data actually in them fits. * It throws std::out_of_range on any boundary violation. * * The at(), append(), etc. methods encode integers larger than 8-bit in * big-endian (network) byte order. * * @tparam C Total capacity */template <unsigned int C> class Buffer {	// I love me!	template <unsigned int C2> friend class Buffer;  public:	// STL container idioms	typedef unsigned char value_type;	typedef unsigned char* pointer;	typedef const char* const_pointer;	typedef char& reference;	typedef const char& const_reference;	typedef char* iterator;	typedef const char* const_iterator;	typedef unsigned int size_type;	typedef int difference_type;	typedef std::reverse_iterator<iterator> reverse_iterator;	typedef std::reverse_iterator<const_iterator> const_reverse_iterator;	inline iterator begin()	{		return _b;	}	inline iterator end()	{		return (_b + _l);	}	inline const_iterator begin() const	{		return _b;	}	inline const_iterator end() const	{		return (_b + _l);	}	inline reverse_iterator rbegin()	{		return reverse_iterator(begin());	}	inline reverse_iterator rend()	{		return reverse_iterator(end());	}	inline const_reverse_iterator rbegin() const	{		return const_reverse_iterator(begin());	}	inline const_reverse_iterator rend() const	{		return const_reverse_iterator(end());	}	Buffer() : _l(0)	{	}	Buffer(unsigned int l)	{		if (l > C) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		_l = l;	}	template <unsigned int C2> Buffer(const Buffer<C2>& b)	{		*this = b;	}	Buffer(const void* b, unsigned int l)	{		copyFrom(b, l);	}	template <unsigned int C2> inline Buffer& operator=(const Buffer<C2>& b)	{		if (unlikely(b._l > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		if (C2 == C) {			memcpy(this, &b, sizeof(Buffer<C>));		}		else {			memcpy(_b, b._b, _l = b._l);		}		return *this;	}	inline void copyFrom(const void* b, unsigned int l)	{		if (unlikely(l > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		memcpy(_b, b, l);		_l = l;	}	unsigned char operator[](const unsigned int i) const	{		if (unlikely(i >= _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		return (unsigned char)_b[i];	}	unsigned char& operator[](const unsigned int i)	{		if (unlikely(i >= _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		return ((unsigned char*)_b)[i];	}	/**	 * Get a raw pointer to a field with bounds checking	 *	 * This isn't perfectly safe in that the caller could still overflow	 * the pointer, but its use provides both a sanity check and	 * documentation / reminder to the calling code to treat the returned	 * pointer as being of size [l].	 *	 * @param i Index of field in buffer	 * @param l Length of field in bytes	 * @return Pointer to field data	 * @throws std::out_of_range Field extends beyond data size	 */	unsigned char* field(unsigned int i, unsigned int l)	{		if (unlikely((i + l) > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		return (unsigned char*)(_b + i);	}	const unsigned char* field(unsigned int i, unsigned int l) const	{		if (unlikely((i + l) > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		return (const unsigned char*)(_b + i);	}	/**	 * Place a primitive integer value at a given position	 *	 * @param i Index to place value	 * @param v Value	 * @tparam T Integer type (e.g. uint16_t, int64_t)	 */	template <typename T> inline void setAt(unsigned int i, const T v)	{		if (unlikely((i + sizeof(T)) > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}#ifdef ZT_NO_TYPE_PUNNING		uint8_t* p = reinterpret_cast<uint8_t*>(_b + i);		for (unsigned int x = 1; x <= sizeof(T); ++x) {			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));		}#else		T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T*>(_b + i);		*p = Utils::hton(v);#endif	}	/**	 * Get a primitive integer value at a given position	 *	 * @param i Index to get integer	 * @tparam T Integer type (e.g. uint16_t, int64_t)	 * @return Integer value	 */	template <typename T> inline T at(unsigned int i) const	{		if (unlikely((i + sizeof(T)) > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}#ifdef ZT_NO_TYPE_PUNNING		T v = 0;		const uint8_t* p = reinterpret_cast<const uint8_t*>(_b + i);		for (unsigned int x = 0; x < sizeof(T); ++x) {			v <<= 8;			v |= (T) * (p++);		}		return v;#else		const T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T*>(_b + i);		return Utils::ntoh(*p);#endif	}	/**	 * Append an integer type to this buffer	 *	 * @param v Value to append	 * @tparam T Integer type (e.g. uint16_t, int64_t)	 * @throws std::out_of_range Attempt to append beyond capacity	 */	template <typename T> inline void append(const T v)	{		if (unlikely((_l + sizeof(T)) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}#ifdef ZT_NO_TYPE_PUNNING		uint8_t* p = reinterpret_cast<uint8_t*>(_b + _l);		for (unsigned int x = 1; x <= sizeof(T); ++x) {			*(p++) = (uint8_t)(v >> (8 * (sizeof(T) - x)));		}#else		T* const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T*>(_b + _l);		*p = Utils::hton(v);#endif		_l += sizeof(T);	}	/**	 * Append a run of bytes	 *	 * @param c Character value to append	 * @param n Number of times to append	 * @throws std::out_of_range Attempt to append beyond capacity	 */	inline void append(unsigned char c, unsigned int n)	{		if (unlikely((_l + n) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		for (unsigned int i = 0; i < n; ++i) {			_b[_l++] = (char)c;		}	}	/**	 * Append secure random bytes	 *	 * @param n Number of random bytes to append	 */	inline void appendRandom(unsigned int n)	{		if (unlikely((_l + n) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		Utils::getSecureRandom(_b + _l, n);		_l += n;	}	/**	 * Append a C-array of bytes	 *	 * @param b Data	 * @param l Length	 * @throws std::out_of_range Attempt to append beyond capacity	 */	inline void append(const void* b, unsigned int l)	{		if (unlikely((_l + l) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		memcpy(_b + _l, b, l);		_l += l;	}	/**	 * Append a C string including null termination byte	 *	 * @param s C string	 * @throws std::out_of_range Attempt to append beyond capacity	 */	inline void appendCString(const char* s)	{		for (;;) {			if (unlikely(_l >= C)) {				throw ZT_EXCEPTION_OUT_OF_BOUNDS;			}			if (! (_b[_l++] = *(s++))) {				break;			}		}	}	/**	 * Append a buffer	 *	 * @param b Buffer to append	 * @tparam C2 Capacity of second buffer (typically inferred)	 * @throws std::out_of_range Attempt to append beyond capacity	 */	template <unsigned int C2> inline void append(const Buffer<C2>& b)	{		append(b._b, b._l);	}	/**	 * Increment size and return pointer to field of specified size	 *	 * Nothing is actually written to the memory. This is a shortcut	 * for addSize() followed by field() to reference the previous	 * position and the new size.	 *	 * @param l Length of field to append	 * @return Pointer to beginning of appended field of length 'l'	 */	inline char* appendField(unsigned int l)	{		if (unlikely((_l + l) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		char* r = _b + _l;		_l += l;		return r;	}	/**	 * Increment size by a given number of bytes	 *	 * The contents of new space are undefined.	 *	 * @param i Bytes to increment	 * @throws std::out_of_range Capacity exceeded	 */	inline void addSize(unsigned int i)	{		if (unlikely((i + _l) > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		_l += i;	}	/**	 * Set size of data in buffer	 *	 * The contents of new space are undefined.	 *	 * @param i New size	 * @throws std::out_of_range Size larger than capacity	 */	inline void setSize(const unsigned int i)	{		if (unlikely(i > C)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		_l = i;	}	/**	 * Move everything after 'at' to the buffer's front and truncate	 *	 * @param at Truncate before this position	 * @throws std::out_of_range Position is beyond size of buffer	 */	inline void behead(const unsigned int at)	{		if (! at) {			return;		}		if (unlikely(at > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		::memmove(_b, _b + at, _l -= at);	}	/**	 * Erase something from the middle of the buffer	 *	 * @param start Starting position	 * @param length Length of block to erase	 * @throws std::out_of_range Position plus length is beyond size of buffer	 */	inline void erase(const unsigned int at, const unsigned int length)	{		const unsigned int endr = at + length;		if (unlikely(endr > _l)) {			throw ZT_EXCEPTION_OUT_OF_BOUNDS;		}		::memmove(_b + at, _b + endr, _l - endr);		_l -= length;	}	/**	 * Set buffer data length to zero	 */	inline void clear()	{		_l = 0;	}	/**	 * Zero buffer up to size()	 */	inline void zero()	{		memset(_b, 0, _l);	}	/**	 * Zero unused capacity area	 */	inline void zeroUnused()	{		memset(_b + _l, 0, C - _l);	}	/**	 * Unconditionally and securely zero buffer's underlying memory	 */	inline void burn()	{		Utils::burn(_b, sizeof(_b));	}	/**	 * @return Constant pointer to data in buffer	 */	inline const void* data() const	{		return _b;	}	/**	 * @return Non-constant pointer to data in buffer	 */	inline void* unsafeData()	{		return _b;	}	/**	 * @return Size of data in buffer	 */	inline unsigned int size() const	{		return _l;	}	/**	 * @return Capacity of buffer	 */	inline unsigned int capacity() const	{		return C;	}	template <unsigned int C2> inline bool operator==(const Buffer<C2>& b) const	{		return ((_l == b._l) && (! memcmp(_b, b._b, _l)));	}	template <unsigned int C2> inline bool operator!=(const Buffer<C2>& b) const	{		return ((_l != b._l) || (memcmp(_b, b._b, _l)));	}	template <unsigned int C2> inline bool operator<(const Buffer<C2>& b) const	{		return (memcmp(_b, b._b, std::min(_l, b._l)) < 0);	}	template <unsigned int C2> inline bool operator>(const Buffer<C2>& b) const	{		return (b < *this);	}	template <unsigned int C2> inline bool operator<=(const Buffer<C2>& b) const	{		return ! (b < *this);	}	template <unsigned int C2> inline bool operator>=(const Buffer<C2>& b) const	{		return ! (*this < b);	}  private:	char ZT_VAR_MAY_ALIAS _b[C];	unsigned int _l;};}	// namespace ZeroTier#endif
 |