Take Dictionary behind the barn...

This commit is contained in:
Adam Ierymenko 2020-01-25 18:02:11 -08:00
parent d5b9a54c55
commit cdc6c42375
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
11 changed files with 727 additions and 660 deletions

View file

@ -34,50 +34,30 @@ public:
typedef char * iterator; typedef char * iterator;
typedef const char * const_iterator; typedef const char * const_iterator;
ZT_ALWAYS_INLINE Str() { _l = 0; _s[0] = 0; } ZT_ALWAYS_INLINE Str() { memset(reinterpret_cast<void *>(this),0,sizeof(Str)); }
ZT_ALWAYS_INLINE Str(const Str &s) explicit ZT_ALWAYS_INLINE Str(const char *s) { *this = s; }
{
_l = s._l;
memcpy(_s,s._s,_l+1);
}
ZT_ALWAYS_INLINE Str(const char *s)
{
_l = 0;
_s[0] = 0;
(*this) << s;
}
ZT_ALWAYS_INLINE Str(const std::string &s) { *this = s; }
ZT_ALWAYS_INLINE Str &operator=(const Str &s)
{
_l = s._l;
memcpy(_s,s._s,_l+1);
return *this;
}
ZT_ALWAYS_INLINE Str &operator=(const char *s) ZT_ALWAYS_INLINE Str &operator=(const char *s)
{ {
_l = 0; if (s) {
_s[0] = 0; unsigned int l = 0;
return ((*this) << s); while (l < C) {
char c = s[l];
if (!c) break;
_s[l++] = c;
} }
ZT_ALWAYS_INLINE Str &operator=(const std::string &s) _s[l] = 0;
{ _l = (uint16_t)l;
if (s.length() > C) {
_l = 0;
_s[0] = 0;
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
} else { } else {
_l = (uint8_t)s.length(); _l = 0;
memcpy(_s,s.data(),s.length()); _s[0] = 0;
_s[s.length()] = 0;
} }
return *this;
} }
ZT_ALWAYS_INLINE char operator[](const unsigned int i) const ZT_ALWAYS_INLINE char operator[](const unsigned int i) const
{ {
if (unlikely(i >= (unsigned int)_l)) if (i >= (unsigned int)_l)
throw ZT_EXCEPTION_OUT_OF_BOUNDS; return 0;
return _s[i]; return _s[i];
} }
@ -85,6 +65,7 @@ public:
ZT_ALWAYS_INLINE const char *c_str() const { return _s; } ZT_ALWAYS_INLINE const char *c_str() const { return _s; }
ZT_ALWAYS_INLINE unsigned int length() const { return (unsigned int)_l; } ZT_ALWAYS_INLINE unsigned int length() const { return (unsigned int)_l; }
ZT_ALWAYS_INLINE bool empty() const { return (_l == 0); } ZT_ALWAYS_INLINE bool empty() const { return (_l == 0); }
ZT_ALWAYS_INLINE iterator begin() { return (iterator)_s; } ZT_ALWAYS_INLINE iterator begin() { return (iterator)_s; }
ZT_ALWAYS_INLINE iterator end() { return (iterator)(_s + (unsigned long)_l); } ZT_ALWAYS_INLINE iterator end() { return (iterator)(_s + (unsigned long)_l); }
ZT_ALWAYS_INLINE const_iterator begin() const { return (const_iterator)_s; } ZT_ALWAYS_INLINE const_iterator begin() const { return (const_iterator)_s; }
@ -92,44 +73,27 @@ public:
ZT_ALWAYS_INLINE Str &operator<<(const char *s) ZT_ALWAYS_INLINE Str &operator<<(const char *s)
{ {
if (likely(s != (const char *)0)) { if (s) {
unsigned long l = _l; unsigned int l = _l;
while (*s) { while (l < C) {
if (unlikely(l >= C)) { char c = s[l];
_s[C] = 0; if (!c) break;
_l = C; _s[l++] = c;
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
}
_s[l++] = *(s++);
} }
_s[l] = 0; _s[l] = 0;
_l = (uint8_t)l; _l = (uint16_t)l;
} }
return *this;
} }
ZT_ALWAYS_INLINE Str &operator<<(const Str &s) { return ((*this) << s._s); } ZT_ALWAYS_INLINE Str &operator<<(const Str &s) { return ((*this) << s._s); }
ZT_ALWAYS_INLINE Str &operator<<(const char c) ZT_ALWAYS_INLINE Str &operator<<(const char c)
{ {
if (unlikely(_l >= C)) { if (_l < C) {
_s[C] = 0; _s[_l++] = c;
throw ZT_EXCEPTION_OUT_OF_BOUNDS; _s[_l] = 0;
} }
_s[(unsigned long)(_l++)] = c;
_s[(unsigned long)_l] = 0;
return *this; return *this;
} }
ZT_ALWAYS_INLINE Str &operator<<(const unsigned long n)
{
char tmp[32];
Utils::decimal(n,tmp);
return ((*this) << tmp);
}
ZT_ALWAYS_INLINE Str &operator<<(const unsigned int n)
{
char tmp[32];
Utils::decimal((unsigned long)n,tmp);
return ((*this) << tmp);
}
ZT_ALWAYS_INLINE Str &operator<<(const Address &a) ZT_ALWAYS_INLINE Str &operator<<(const Address &a)
{ {
char tmp[32]; char tmp[32];
@ -146,27 +110,6 @@ public:
return ((*this) << a.toString(tmp)); return ((*this) << a.toString(tmp));
} }
inline Str &append(const char *s,const unsigned int max)
{
if (likely(s != (const char *)0)) {
unsigned long l = _l;
unsigned int c = 0;
while (*s) {
if (c++ >= max) break;
if (unlikely(l >= C)) {
_s[C] = 0;
_l = C;
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
}
_s[l++] = *s;
++s;
}
_s[l] = 0;
_l = (uint8_t)l;
}
return *this;
}
ZT_ALWAYS_INLINE operator bool() const { return (_l != 0); } ZT_ALWAYS_INLINE operator bool() const { return (_l != 0); }
ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); } ZT_ALWAYS_INLINE bool operator==(const Str &s) const { return ((_l == s._l)&&(memcmp(_s,s._s,_l) == 0)); }

View file

@ -16,9 +16,97 @@
namespace ZeroTier { namespace ZeroTier {
#ifdef __GNUC__ #ifdef __GNUC__
uintptr_t Buf_pool = 0; uintptr_t _Buf_pool = 0;
#else #else
std::atomic<uintptr_t> Buf_pool(0); std::atomic<uintptr_t> _Buf_pool(0);
#endif #endif
void _Buf_release(void *ptr,std::size_t sz)
{
if (ptr) {
uintptr_t bb;
const uintptr_t locked = ~((uintptr_t)0);
for (;;) {
#ifdef __GNUC__
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
((Buf<> *)ptr)->__nextInPool = bb;
#ifdef __GNUC__
__sync_fetch_and_and(&_Buf_pool,(uintptr_t)ptr);
#else
s_pool.store((uintptr_t)ptr);
#endif
}
}
void *_Buf_get()
{
uintptr_t bb;
const uintptr_t locked = ~((uintptr_t)0);
for (;;) {
#ifdef __GNUC__
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
Buf<> *b;
if (bb == 0) {
#ifdef __GNUC__
__sync_fetch_and_and(&_Buf_pool,bb);
#else
s_pool.store(bb);
#endif
b = (Buf<> *)malloc(sizeof(Buf<>));
if (!b)
return nullptr;
} else {
b = (Buf<> *)bb;
#ifdef __GNUC__
__sync_fetch_and_and(&_Buf_pool,b->__nextInPool);
#else
s_pool.store(b->__nextInPool);
#endif
}
b->__refCount.zero();
return (void *)b;
}
void freeBufPool()
{
uintptr_t bb;
const uintptr_t locked = ~((uintptr_t)0);
for (;;) {
#ifdef __GNUC__
bb = __sync_fetch_and_or(&_Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
#ifdef __GNUC__
__sync_fetch_and_and(&_Buf_pool,(uintptr_t)0);
#else
s_pool.store((uintptr_t)0);
#endif
while (bb != 0) {
uintptr_t next = ((Buf<> *)bb)->__nextInPool;
free((void *)bb);
bb = next;
}
}
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -23,6 +23,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <cstdlib> #include <cstdlib>
#include <stdexcept>
#ifndef __GNUC__ #ifndef __GNUC__
#include <atomic> #include <atomic>
@ -37,10 +38,21 @@
namespace ZeroTier { namespace ZeroTier {
#ifdef __GNUC__ #ifdef __GNUC__
extern uintptr_t Buf_pool; extern uintptr_t _Buf_pool;
#else #else
extern std::atomic<uintptr_t> Buf_pool; extern std::atomic<uintptr_t> _Buf_pool;
#endif #endif
void _Buf_release(void *ptr,std::size_t sz);
void *_Buf_get();
/**
* Free buffers in the pool
*
* New buffers will be created and the pool repopulated if get() is called
* and outstanding buffers will still be returned to the pool. This just
* frees buffers currently held in reserve.
*/
void freeBufPool();
/** /**
* Buffer and methods for branch-free bounds-checked data assembly and parsing * Buffer and methods for branch-free bounds-checked data assembly and parsing
@ -82,10 +94,13 @@ extern std::atomic<uintptr_t> Buf_pool;
* *
* @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE) * @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE)
*/ */
template<typename U = void> template<typename U = int>
class Buf class Buf
{ {
friend class SharedPtr< Buf<U> >; friend class SharedPtr< Buf<U> >;
friend void _Buf_release(void *,std::size_t);
friend void *_Buf_get();
friend void freeBufPool();
private: private:
// Direct construction isn't allowed; use get(). // Direct construction isn't allowed; use get().
@ -95,104 +110,19 @@ private:
ZT_ALWAYS_INLINE Buf(const Buf<X> &b) { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); } ZT_ALWAYS_INLINE Buf(const Buf<X> &b) { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); }
public: public:
static void operator delete(void *ptr,std::size_t sz) static void operator delete(void *ptr,std::size_t sz) { _Buf_release(ptr,sz); }
{
if (ptr) {
uintptr_t bb;
const uintptr_t locked = ~((uintptr_t)0);
for (;;) {
#ifdef __GNUC__
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
((Buf *)ptr)->__nextInPool = bb;
#ifdef __GNUC__
__sync_fetch_and_and(&Buf_pool,(uintptr_t)ptr);
#else
s_pool.store((uintptr_t)ptr);
#endif
}
}
/** /**
* Get obtains a buffer from the pool or allocates a new buffer if the pool is empty * Get obtains a buffer from the pool or allocates a new buffer if the pool is empty
* *
* @return Buffer * @return Buffer instance
*/ */
static ZT_ALWAYS_INLINE SharedPtr< Buf<U> > get() static ZT_ALWAYS_INLINE SharedPtr< Buf<U> > get()
{ {
uintptr_t bb; void *const b = _Buf_get();
const uintptr_t locked = ~((uintptr_t)0); if (b)
for (;;) { return SharedPtr<Buf>((Buf *)b);
#ifdef __GNUC__ throw std::bad_alloc();
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
Buf *b;
if (bb == 0) {
#ifdef __GNUC__
__sync_fetch_and_and(&Buf_pool,bb);
#else
s_pool.store(bb);
#endif
b = (Buf *)malloc(sizeof(Buf));
if (!b)
return SharedPtr<Buf>();
} else {
b = (Buf *)bb;
#ifdef __GNUC__
__sync_fetch_and_and(&Buf_pool,b->__nextInPool);
#else
s_pool.store(b->__nextInPool);
#endif
}
b->__refCount.zero();
return SharedPtr<Buf>(b);
}
/**
* Free buffers in the pool
*
* New buffers will be created and the pool repopulated if get() is called
* and outstanding buffers will still be returned to the pool. This just
* frees buffers currently held in reserve.
*/
static inline void freePool()
{
uintptr_t bb;
const uintptr_t locked = ~((uintptr_t)0);
for (;;) {
#ifdef __GNUC__
bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's
#else
bb = s_pool.fetch_or(locked);
#endif
if (bb != locked)
break;
}
#ifdef __GNUC__
__sync_fetch_and_and(&Buf_pool,(uintptr_t)0);
#else
s_pool.store((uintptr_t)0);
#endif
while (bb != 0) {
uintptr_t next = ((Buf *)bb)->__nextInPool;
free((void *)bb);
bb = next;
}
} }
/** /**

View file

@ -38,7 +38,6 @@ set(core_headers
SelfAwareness.hpp SelfAwareness.hpp
SHA512.hpp SHA512.hpp
SharedPtr.hpp SharedPtr.hpp
Str.hpp
Switch.hpp Switch.hpp
Tag.hpp Tag.hpp
Topology.hpp Topology.hpp

View file

@ -231,9 +231,6 @@
#define ZT_CRYPTO_ALG_C25519 0 #define ZT_CRYPTO_ALG_C25519 0
#define ZT_CRYPTO_ALG_P384 1 #define ZT_CRYPTO_ALG_P384 1
// Exceptions thrown in core ZT code
#define ZT_EXCEPTION_OUT_OF_BOUNDS 100
/* Ethernet frame types that might be relevant to us */ /* Ethernet frame types that might be relevant to us */
#define ZT_ETHERTYPE_IPV4 0x0800 #define ZT_ETHERTYPE_IPV4 0x0800
#define ZT_ETHERTYPE_ARP 0x0806 #define ZT_ETHERTYPE_ARP 0x0806

View file

@ -15,210 +15,264 @@
namespace ZeroTier { namespace ZeroTier {
Dictionary::Dictionary(const char *s,unsigned int len) static uint64_t _toKey(const char *k)
{ {
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) { uint64_t n = 0;
if ((s)&&(i < len)) { unsigned int i = 0;
if (!(_d[i] = *s)) for(;;) {
s = (const char *)0; char c = k[i];
else ++s; if (!c) break;
} else _d[i] = (char)0; reinterpret_cast<uint8_t *>(&n)[i & 7U] ^= (uint8_t)c;
++i;
} }
_d[ZT_DICTIONARY_MAX_CAPACITY-1] = (char)0; return n;
} }
bool Dictionary::load(const char *s) Dictionary::Dictionary()
{ {
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) {
if (s) {
if (!(_d[i] = *s))
s = (const char *)0;
else ++s;
} else _d[i] = (char)0;
}
_d[ZT_DICTIONARY_MAX_CAPACITY - 1] = (char)0;
return (!s);
} }
int Dictionary::get(const char *key,char *dest,unsigned int destlen) const Dictionary::~Dictionary()
{ {
const char *p = _d; }
const char *const eof = p + ZT_DICTIONARY_MAX_CAPACITY;
const char *k;
bool esc;
int j;
if (!destlen) // sanity check std::vector<uint8_t> &Dictionary::operator[](const char *k)
return -1; {
return _t[_toKey(k)];
}
while (*p) { const std::vector<uint8_t> &Dictionary::operator[](const char *k) const
k = key; {
while ((*k)&&(*p)) { static const std::vector<uint8_t> emptyEntry;
if (*p != *k) const std::vector<uint8_t> *const e = _t.get(_toKey(k));
return (e) ? *e : emptyEntry;
}
void Dictionary::add(const char *k,bool v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(2);
e[0] = (uint8_t)(v ? '1' : '0');
e[1] = 0;
}
void Dictionary::add(const char *k,uint16_t v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(5);
Utils::hex(v,(char *)e.data());
}
void Dictionary::add(const char *k,uint32_t v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(9);
Utils::hex(v,(char *)e.data());
}
void Dictionary::add(const char *k,uint64_t v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(17);
Utils::hex(v,(char *)e.data());
}
void Dictionary::add(const char *k,const Address &v)
{
std::vector<uint8_t> &e = (*this)[k];
e.resize(11);
v.toString((char *)e.data());
}
void Dictionary::add(const char *k,const char *v)
{
std::vector<uint8_t> &e = (*this)[k];
e.clear();
if (v) {
for(;;) {
const uint8_t c = (uint8_t)*(v++);
e.push_back(c);
if (!c) break;
}
}
}
void Dictionary::add(const char *k,const void *data,unsigned int len)
{
std::vector<uint8_t> &e = (*this)[k];
if (len != 0) {
e.assign((const uint8_t *)data,(const uint8_t *)data + len);
} else {
e.clear();
}
}
bool Dictionary::getB(const char *k,bool dfl) const
{
bool v = dfl;
const std::vector<uint8_t> &e = (*this)[k];
if (!e.empty()) {
switch ((char)e[0]) {
case '1':
case 't':
case 'T':
case 'y':
case 'Y':
return true;
default:
return false;
}
}
return v;
}
uint64_t Dictionary::getUI(const char *k,uint64_t dfl) const
{
uint8_t tmp[18];
uint64_t v = dfl;
const std::vector<uint8_t> &e = (*this)[k];
if (!e.empty()) {
if (e.back() != 0) {
const unsigned long sl = e.size();
memcpy(tmp,e.data(),(sl > 17) ? 17 : sl);
tmp[17] = 0;
return Utils::unhex((const char *)tmp);
}
return Utils::unhex((const char *)e.data());
}
return v;
}
void Dictionary::getS(const char *k,char *v,unsigned int cap) const
{
if (cap == 0) // sanity check
return;
const std::vector<uint8_t> &e = (*this)[k];
unsigned int i = 0;
const unsigned int last = cap - 1;
for(;;) {
if ((i == last)||(i >= (unsigned int)e.size()))
break; break;
++k; v[i] = (char)e[i];
if (++p == eof) { ++i;
dest[0] = (char)0;
return -1;
} }
} v[i] = 0;
if ((!*k)&&(*p == '=')) {
j = 0;
esc = false;
++p;
while ((*p != 0)&&(*p != 13)&&(*p != 10)) {
if (esc) {
esc = false;
switch(*p) {
case 'r': dest[j++] = 13; break;
case 'n': dest[j++] = 10; break;
case '0': dest[j++] = (char)0; break;
case 'e': dest[j++] = '='; break;
default: dest[j++] = *p; break;
}
if (j == (int)destlen) {
dest[j-1] = (char)0;
return j-1;
}
} else if (*p == '\\') {
esc = true;
} else {
dest[j++] = *p;
if (j == (int)destlen) {
dest[j-1] = (char)0;
return j-1;
}
}
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
dest[j] = (char)0;
return j;
} else {
while ((*p)&&(*p != 13)&&(*p != 10)) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
if (*p) {
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
else break;
}
}
dest[0] = (char)0;
return -1;
} }
bool Dictionary::add(const char *key,const char *value,int vlen) void Dictionary::clear()
{ {
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) { _t.clear();
if (!_d[i]) { }
unsigned int j = i;
if (j > 0) { void Dictionary::encode(std::vector<uint8_t> &out) const
_d[j++] = (char)10; {
if (j == ZT_DICTIONARY_MAX_CAPACITY) { uint64_t str[2] = { 0,0 }; // second entry causes all strings to be null-terminated even if 8 chars in length
_d[i] = (char)0;
return false; out.clear();
}
Hashtable< uint64_t,std::vector<uint8_t> >::Iterator ti(const_cast<Dictionary *>(this)->_t);
uint64_t *kk = nullptr;
std::vector<uint8_t> *vv = nullptr;
while (ti.next(kk,vv)) {
str[0] = *kk;
const char *k = (const char *)str;
for(;;) {
char kc = *(k++);
if (!kc) break;
if ((kc >= 33)&&(kc <= 126)&&(kc != 61)&&(kc != 92)) // printable ASCII with no spaces, equals, or backslash
out.push_back((uint8_t)kc);
} }
const char *p = key; out.push_back(61); // =
while (*p) {
_d[j++] = *(p++);
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
}
_d[j++] = '='; for(std::vector<uint8_t>::const_iterator i(vv->begin());i!=vv->end();++i) {
if (j == ZT_DICTIONARY_MAX_CAPACITY) { uint8_t c = *i;
_d[i] = (char)0; switch(c) {
return false;
}
p = value;
int k = 0;
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
switch(*p) {
case 0: case 0:
case 13: out.push_back(92);
out.push_back(48);
break;
case 10: case 10:
case '\\': out.push_back(92);
case '=': out.push_back(110);
_d[j++] = '\\'; break;
if (j == ZT_DICTIONARY_MAX_CAPACITY) { case 13:
_d[i] = (char)0; out.push_back(92);
return false; out.push_back(114);
} break;
switch(*p) { case 61:
case 0: _d[j++] = '0'; break; out.push_back(92);
case 13: _d[j++] = 'r'; break; out.push_back(101);
case 10: _d[j++] = 'n'; break; break;
case '\\': _d[j++] = '\\'; break; case 92:
case '=': _d[j++] = 'e'; break; out.push_back(92);
} out.push_back(92);
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
break; break;
default: default:
_d[j++] = *p; out.push_back(c);
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
break; break;
} }
++p;
++k;
} }
_d[j] = (char)0; out.push_back(10);
}
}
bool Dictionary::decode(const void *data,unsigned int len)
{
clear();
uint64_t k = 0;
unsigned int ki = 0;
std::vector<uint8_t> *v = nullptr;
bool escape = false;
for(unsigned int di=0;di<len;++di) {
uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
if (!c) break;
if (v) {
if (escape) {
escape = false;
switch(c) {
case 48:
v->push_back(0);
break;
case 101:
v->push_back(61);
break;
case 110:
v->push_back(10);
break;
case 114:
v->push_back(13);
break;
default:
v->push_back(c);
break;
}
} else {
if (c == 10) {
k = 0;
ki = 0;
v = nullptr;
} else if (c == 92) {
escape = true;
} else {
v->push_back(c);
}
}
} else {
if ((c < 33)||(c > 126)||(c == 92)) {
return false;
} else if (c == 61) {
v = &_t[k];
} else {
reinterpret_cast<uint8_t *>(&k)[ki & 7U] ^= c;
}
}
}
return true; return true;
}
}
return false;
}
bool Dictionary::add(const char *key,bool value)
{
return this->add(key,(value) ? "1" : "0",1);
}
bool Dictionary::add(const char *key,uint64_t value)
{
char tmp[32];
return this->add(key,Utils::hex(value,tmp),-1);
}
bool Dictionary::add(const char *key,int64_t value)
{
char tmp[32];
if (value >= 0) {
return this->add(key,Utils::hex((uint64_t)value,tmp),-1);
} else {
tmp[0] = '-';
return this->add(key,Utils::hex((uint64_t)(value * -1),tmp+1),-1);
}
}
bool Dictionary::add(const char *key,const Address &a)
{
char tmp[32];
return this->add(key,Utils::hex(a.toInt(),tmp),-1);
} }
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -18,181 +18,157 @@
#include "Utils.hpp" #include "Utils.hpp"
#include "Address.hpp" #include "Address.hpp"
#include "Buf.hpp" #include "Buf.hpp"
#include "Hashtable.hpp"
#include <cstdint> #include <cstdint>
#include <vector>
#define ZT_DICTIONARY_MAX_CAPACITY 65536
namespace ZeroTier { namespace ZeroTier {
/** /**
* A small (in code and data) packed key=value store * A simple key-value store for short keys
* *
* This stores data in the form of a compact blob that is sort of human * This data structure is used for network configurations, node meta-data,
* readable (depending on whether you put binary data in it) and is backward * and other open-definition protocol objects. It consists of a key-value
* compatible with older versions. Binary data is escaped such that the * store with short (under 8 characters) keys that map to strings, blobs,
* serialized form of a Dictionary is always a valid null-terminated C string. * or integers with the latter being by convention in hex format.
* *
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key * If this seems a little odd, it is. It dates back to the very first alpha
* contains these characters it may not be retrievable. This is not checked. * versions of ZeroTier and if it were redesigned today we'd use some kind
* * of simple or standardized binary encoding. Nevertheless it is efficient
* Lookup is via linear search and will be slow with a lot of keys. It's * and it works so there is no need to change it and break backward
* designed for small things. * compatibility.
*
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
* pointer tricks like this is important after any modifications.
*
* This is used for network configurations and for saving some things on disk
* in the ZeroTier One service code.
*
* @tparam C Dictionary max capacity in bytes
*/ */
class Dictionary class Dictionary
{ {
public: public:
ZT_ALWAYS_INLINE Dictionary() { _d[0] = 0; } Dictionary();
explicit ZT_ALWAYS_INLINE Dictionary(const char *s) { this->load(s); } ~Dictionary();
Dictionary(const char *s,unsigned int len);
ZT_ALWAYS_INLINE operator bool() const { return (_d[0] != 0); }
/** /**
* Load a dictionary from a C-string * Get a reference to a value
* *
* @param s Dictionary in string form * @param k Key to look up
* @return False if 's' was longer than our capacity * @return Reference to value
*/ */
bool load(const char *s); std::vector<uint8_t> &operator[](const char *k);
/** /**
* Delete all entries * Get a const reference to a value
*/
ZT_ALWAYS_INLINE void clear() { memset(_d,0,sizeof(_d)); }
/**
* Get an entry
* *
* Note that to get binary values, dest[] should be at least one more than * @param k Key to look up
* the maximum size of the value being retrieved. That's because even if * @return Reference to value or to empty vector if not found
* the data is binary a terminating 0 is still appended to dest[] after it. */
const std::vector<uint8_t> &operator[](const char *k) const;
/**
* Add a boolean as '1' or '0'
*/
void add(const char *k,bool v);
/**
* Add an integer as a hexadecimal string value
*/
void add(const char *k,uint16_t v);
/**
* Add an integer as a hexadecimal string value
*/
void add(const char *k,uint32_t v);
/**
* Add an integer as a hexadecimal string value
*/
void add(const char *k,uint64_t v);
ZT_ALWAYS_INLINE void add(const char *k,int16_t v) { add(k,(uint16_t)v); }
ZT_ALWAYS_INLINE void add(const char *k,int32_t v) { add(k,(uint32_t)v); }
ZT_ALWAYS_INLINE void add(const char *k,int64_t v) { add(k,(uint64_t)v); }
/**
* Add an address in 10-digit hex string format
*/
void add(const char *k,const Address &v);
/**
* Add a C string as a value
*/
void add(const char *k,const char *v);
/**
* Add a binary blob as a value
*/
void add(const char *k,const void *data,unsigned int len);
/**
* Get a boolean
* *
* If the key is not found, dest[0] is set to 0 to make dest[] an empty * @param k Key to look up
* C string in that case. The dest[] array will *never* be unterminated * @param dfl Default value (default: false)
* after this call. * @return Value of key or default if not found
*/
bool getB(const char *k,bool dfl = false) const;
/**
* Get an integer
* *
* Security note: if 'key' is ever directly based on anything that is not * @param k Key to look up
* a hard-code or internally-generated name, it must be checked to ensure * @param dfl Default value (default: 0)
* that the buffer is NULL-terminated since key[] does not take a secondary * @return Value of key or default if not found
* size parameter. In NetworkConfig all keys are hard-coded strings so this */
* isn't a problem in the core. uint64_t getUI(const char *k,uint64_t dfl = 0) const;
/**
* Get a C string
* *
* @param key Key to look up * If the buffer is too small the string will be truncated, but the
* @param dest Destination buffer * buffer will always end in a terminating null no matter what.
* @param destlen Size of destination buffer
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
*/
int get(const char *key,char *dest,unsigned int destlen) const;
/**
* Get a boolean value
* *
* @param key Key to look up * @param k Key to look up
* @param dfl Default value if not found in dictionary * @param v Buffer to hold string
* @return Boolean value of key or 'dfl' if not found * @param cap Maximum size of string (including terminating null)
*/ */
ZT_ALWAYS_INLINE bool getB(const char *key,bool dfl = false) const void getS(const char *k,char *v,unsigned int cap) const;
{
char tmp[4];
if (this->get(key,tmp,sizeof(tmp)) >= 0)
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
return dfl;
}
/** /**
* Get an unsigned int64 stored as hex in the dictionary * Erase all entries in dictionary
*/
void clear();
/**
* @return Number of entries
*/
ZT_ALWAYS_INLINE unsigned int size() const { return _t.size(); }
/**
* @return True if dictionary is not empty
*/
ZT_ALWAYS_INLINE bool empty() const { return _t.empty(); }
/**
* Encode to a string in the supplied vector
* *
* @param key Key to look up * This does not add a terminating zero. This must be pushed afterwords
* @param dfl Default value or 0 if unspecified * if the result is to be handled as a C string.
* @return Decoded hex UInt value or 'dfl' if not found
*/
ZT_ALWAYS_INLINE uint64_t getUI(const char *key,uint64_t dfl = 0) const
{
char tmp[128];
if (this->get(key,tmp,sizeof(tmp)) >= 1)
return Utils::hexStrToU64(tmp);
return dfl;
}
/**
* Get an unsigned int64 stored as hex in the dictionary
* *
* @param key Key to look up * @param out String encoded dictionary
* @param dfl Default value or 0 if unspecified
* @return Decoded hex UInt value or 'dfl' if not found
*/ */
ZT_ALWAYS_INLINE int64_t getI(const char *key,int64_t dfl = 0) const void encode(std::vector<uint8_t> &out) const;
{
char tmp[128];
if (this->get(key,tmp,sizeof(tmp)) >= 1)
return Utils::hexStrTo64(tmp);
return dfl;
}
/** /**
* Add a new key=value pair * Decode a string encoded dictionary
* *
* If the key is already present this will append another, but the first * This will decode up to 'len' but will also abort if it finds a
* will always be returned by get(). This is not checked. If you want to * null/zero as this could be a C string.
* ensure a key is not present use erase() first.
* *
* Use the vlen parameter to add binary values. Nulls will be escaped. * @param data Data to decode
* * @param len Length of data
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters * @return True if dictionary was formatted correctly and valid, false on error
* @param value Value to set
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
* @return True if there was enough room to add this key=value pair
*/ */
bool add(const char *key,const char *value,int vlen = -1); bool decode(const void *data,unsigned int len);
/**
* Add a boolean as a '1' or a '0'
*/
bool add(const char *key,bool value);
/**
* Add a 64-bit integer (unsigned) as a hex value
*/
bool add(const char *key,uint64_t value);
/**
* Add a 64-bit integer (unsigned) as a hex value
*/
bool add(const char *key,int64_t value);
/**
* Add a 64-bit integer (unsigned) as a hex value
*/
bool add(const char *key,const Address &a);
/**
* @param key Key to check
* @return True if key is present
*/
ZT_ALWAYS_INLINE bool contains(const char *key) const
{
char tmp[2];
return (this->get(key,tmp,2) >= 0);
}
/**
* @return Value of C template parameter
*/
ZT_ALWAYS_INLINE unsigned int capacity() const { return sizeof(_d); }
ZT_ALWAYS_INLINE const char *data() const { return _d; }
private: private:
char _d[ZT_DICTIONARY_MAX_CAPACITY]; Hashtable< uint64_t,std::vector<uint8_t> > _t;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -35,7 +35,7 @@ private:
struct _Bucket struct _Bucket
{ {
ZT_ALWAYS_INLINE _Bucket(const K &k,const V &v) : k(k),v(v) {} ZT_ALWAYS_INLINE _Bucket(const K &k,const V &v) : k(k),v(v) {}
ZT_ALWAYS_INLINE _Bucket(const K &k) : k(k),v() {} explicit ZT_ALWAYS_INLINE _Bucket(const K &k) : k(k),v() {}
ZT_ALWAYS_INLINE _Bucket(const _Bucket &b) : k(b.k),v(b.v) {} ZT_ALWAYS_INLINE _Bucket(const _Bucket &b) : k(b.k),v(b.v) {}
ZT_ALWAYS_INLINE _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; } ZT_ALWAYS_INLINE _Bucket &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
_Bucket *next; // must be set manually for each _Bucket _Bucket *next; // must be set manually for each _Bucket
@ -57,7 +57,7 @@ public:
/** /**
* @param ht Hash table to iterate over * @param ht Hash table to iterate over
*/ */
ZT_ALWAYS_INLINE Iterator(Hashtable &ht) : explicit ZT_ALWAYS_INLINE Iterator(Hashtable &ht) :
_idx(0), _idx(0),
_ht(&ht), _ht(&ht),
_b(ht._t[0]) _b(ht._t[0])
@ -95,7 +95,7 @@ public:
/** /**
* @param bc Initial capacity in buckets (default: 32, must be nonzero) * @param bc Initial capacity in buckets (default: 32, must be nonzero)
*/ */
ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) : explicit ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) :
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))), _t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
_bc(bc), _bc(bc),
_s(0) _s(0)

View file

@ -44,138 +44,105 @@ NetworkConfig::NetworkConfig() :
bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const
{ {
uint8_t tmp[16384]; uint8_t tmp[16384];
std::vector<uint8_t> buf; try {
char tmp2[128];
d.clear(); d.clear();
// Try to put the more human-readable fields first d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId);
d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp);
d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta);
d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision);
d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString((char *)tmp));
d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags);
d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit);
d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint16_t)this->type);
d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name);
d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,this->mtu);
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false; if (this->com)
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false; d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp,this->com.marshal(tmp));
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MTU,(uint64_t)this->mtu)) return false;
// Then add binary blobs std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES]);
for (unsigned int i = 0; i < this->capabilityCount; ++i) {
if (this->com) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,(const char *)tmp,this->com.marshal(tmp)))
return false;
}
buf.clear();
for(unsigned int i=0;i<this->capabilityCount;++i) {
int l = this->capabilities[i].marshal(tmp); int l = this->capabilities[i].marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,(const char *)buf.data(),(int)buf.size()))
return false;
} }
buf.clear(); blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_TAGS]);
for(unsigned int i=0;i<this->tagCount;++i) { for (unsigned int i = 0; i < this->tagCount; ++i) {
int l = this->tags[i].marshal(tmp); int l = this->tags[i].marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,(const char *)buf.data(),(int)buf.size()))
return false;
} }
buf.clear(); blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i) { for (unsigned int i = 0; i < this->certificateOfOwnershipCount; ++i) {
int l = this->certificatesOfOwnership[i].marshal(tmp); int l = this->certificatesOfOwnership[i].marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,(const char *)buf.data(),(int)buf.size()))
return false;
} }
buf.clear(); blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS]);
for(unsigned int i=0;i<this->specialistCount;++i) { for (unsigned int i = 0; i < this->specialistCount; ++i) {
Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]); Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]);
buf.insert(buf.end(),tmp,tmp + 8); blob->insert(blob->end(),tmp,tmp + 8);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,(const char *)buf.data(),(int)buf.size()))
return false;
} }
buf.clear(); blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ROUTES]);
for(unsigned int i=0;i<this->routeCount;++i) { for (unsigned int i = 0; i < this->routeCount; ++i) {
int l = asInetAddress(this->routes[i].target).marshal(tmp); int l = asInetAddress(this->routes[i].target).marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
l = asInetAddress(this->routes[i].via).marshal(tmp); l = asInetAddress(this->routes[i].via).marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,(const char *)buf.data(),(int)buf.size()))
return false;
} }
buf.clear(); blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS]);
for(unsigned int i=0;i<this->staticIpCount;++i) { for (unsigned int i = 0; i < this->staticIpCount; ++i) {
int l = this->staticIps[i].marshal(tmp); int l = this->staticIps[i].marshal(tmp);
if (l < 0) if (l < 0)
return false; return false;
buf.insert(buf.end(),tmp,tmp + l); blob->insert(blob->end(),tmp,tmp + l);
}
if (!buf.empty()) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,(const char *)buf.data(),(int)buf.size()))
return false;
} }
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_RULES]);
if (this->ruleCount) { if (this->ruleCount) {
buf.resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX); blob->resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX);
int l = Capability::marshalVirtualNetworkRules(buf.data(),rules,ruleCount); int l = Capability::marshalVirtualNetworkRules(blob->data(),rules,ruleCount);
if (l > 0) { if (l > 0)
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,(const char *)buf.data(),l)) blob->resize(l);
return false;
}
} }
return true; return true;
} catch ( ... ) {}
return false;
} }
bool NetworkConfig::fromDictionary(const Dictionary &d) bool NetworkConfig::fromDictionary(const Dictionary &d)
{ {
static const NetworkConfig NIL_NC; static const NetworkConfig NIL_NC;
ScopedPtr< Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> > tmp(new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>());
try { try {
*this = NIL_NC; *this = NIL_NC;
// Fields that are always present, new or old
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0); this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
if (!this->networkId) if (!this->networkId)
return false; return false;
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0); this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
if (this->timestamp <= 0)
return false;
this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0); this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0); this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0); this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
if (!this->issuedTo) if (!this->issuedTo)
return false; return false;
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0); this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name)); d.getS(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU); this->mtu = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MTU,ZT_DEFAULT_MTU);
if (this->mtu < 1280) if (this->mtu < 1280)
this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others this->mtu = 1280; // minimum MTU allowed by IPv6 standard and others
@ -185,87 +152,120 @@ bool NetworkConfig::fromDictionary(const Dictionary &d)
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) { if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
return false; return false;
} else { } else {
// Otherwise we can use the new fields
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0); this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE); this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) const std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_COM]);
this->com.deserialize(*tmp,0); if (!blob->empty()) {
if (this->com.unmarshal(blob->data(),(int)(blob->size()) < 0))
return false;
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES]);
if (!blob->empty()) {
try { try {
unsigned int p = 0; unsigned int p = 0;
while (p < tmp->size()) { while (p < blob->size()) {
Capability cap; Capability cap;
p += cap.deserialize(*tmp,p); int l = cap.unmarshal(blob->data() + p,(int)(blob->size() - p));
if (l < 0)
return false;
p += l;
if (this->capabilityCount < ZT_MAX_NETWORK_CAPABILITIES)
this->capabilities[this->capabilityCount++] = cap; this->capabilities[this->capabilityCount++] = cap;
} }
} catch ( ... ) {} } catch ( ... ) {}
std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount])); std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_TAGS]);
if (!blob->empty()) {
try { try {
unsigned int p = 0; unsigned int p = 0;
while (p < tmp->size()) { while (p < blob->size()) {
Tag tag; Tag tag;
p += tag.deserialize(*tmp,p); int l = tag.unmarshal(blob->data() + p,(int)(blob->size() - p));
if (l < 0)
return false;
p += l;
if (this->tagCount < ZT_MAX_NETWORK_TAGS)
this->tags[this->tagCount++] = tag; this->tags[this->tagCount++] = tag;
} }
} catch ( ... ) {} } catch ( ... ) {}
std::sort(&(this->tags[0]),&(this->tags[this->tagCount])); std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
if (!blob->empty()) {
try {
unsigned int p = 0; unsigned int p = 0;
while (p < tmp->size()) { while (p < blob->size()) {
if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP) CertificateOfOwnership coo;
p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p); int l = coo.unmarshal(blob->data() + p,(int)(blob->size() - p));
else { if (l < 0)
CertificateOfOwnership foo; return false;
p += foo.deserialize(*tmp,p); p += l;
} if (this->certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
this->certificatesOfOwnership[certificateOfOwnershipCount++] = coo;
} }
} catch ( ... ) {}
std::sort(&(this->certificatesOfOwnership[0]),&(this->certificatesOfOwnership[this->certificateOfOwnershipCount]));
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS]);
if (!blob->empty()) {
unsigned int p = 0; unsigned int p = 0;
while ((p + 8) <= tmp->size()) { while (((p + 8) <= blob->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) this->specialists[this->specialistCount++] = Utils::loadBigEndian<uint64_t>(blob->data() + p);
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
p += 8; p += 8;
} }
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ROUTES]);
if (!blob->empty()) {
unsigned int p = 0; unsigned int p = 0;
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) { while ((p < blob->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p); int l = asInetAddress(this->routes[this->routeCount].target).unmarshal(blob->data(),(int)(blob->size() - p));
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p); if (l < 0)
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2; return false;
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2; p += l;
if (p >= blob->size())
return false;
l = asInetAddress(this->routes[this->routeCount].via).unmarshal(blob->data(),(int)(blob->size() - p));
if (l < 0)
return false;
p += l;
if ((p + 4) > blob->size())
return false;
this->routes[this->routeCount].flags = Utils::loadBigEndian<uint16_t>(blob->data() + p); p += 2;
this->routes[this->routeCount].metric = Utils::loadBigEndian<uint16_t>(blob->data() + p); p += 2;
++this->routeCount; ++this->routeCount;
} }
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS]);
if (!blob->empty()) {
unsigned int p = 0; unsigned int p = 0;
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { while ((p < blob->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p); int l = this->staticIps[this->staticIpCount].unmarshal(blob->data() + p,(int)(blob->size() - p));
if (l < 0)
return false;
p += l;
++this->staticIpCount;
} }
} }
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) { blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_RULES]);
if (!blob->empty()) {
this->ruleCount = 0; this->ruleCount = 0;
unsigned int p = 0; if (Capability::unmarshalVirtualNetworkRules(blob->data(),(int)blob->size(),this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES) < 0)
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES); return false;
} }
} }
return true; return true;
} catch ( ... ) { } catch ( ... ) {}
return false; return false;
}
} }
bool NetworkConfig::addSpecialist(const Address &a,const uint64_t f) bool NetworkConfig::addSpecialist(const Address &a,const uint64_t f)

View file

@ -97,6 +97,85 @@ char *decimal(unsigned long n,char s[24])
return s; return s;
} }
char *hex(uint8_t i,char s[3])
{
s[0] = HEXCHARS[(i >> 4U) & 0xfU];
s[1] = HEXCHARS[i & 0xfU];
s[2] = 0;
return s;
}
char *hex(uint16_t i,char s[5])
{
s[0] = HEXCHARS[(i >> 12U) & 0xfU];
s[1] = HEXCHARS[(i >> 8U) & 0xfU];
s[2] = HEXCHARS[(i >> 4U) & 0xfU];
s[3] = HEXCHARS[i & 0xfU];
s[4] = 0;
return s;
}
char *hex(uint32_t i,char s[9])
{
s[0] = HEXCHARS[(i >> 28U) & 0xfU];
s[1] = HEXCHARS[(i >> 24U) & 0xfU];
s[2] = HEXCHARS[(i >> 20U) & 0xfU];
s[3] = HEXCHARS[(i >> 16U) & 0xfU];
s[4] = HEXCHARS[(i >> 12U) & 0xfU];
s[5] = HEXCHARS[(i >> 8U) & 0xfU];
s[6] = HEXCHARS[(i >> 4U) & 0xfU];
s[7] = HEXCHARS[i & 0xfU];
s[8] = 0;
return s;
}
char *hex(uint64_t i,char s[17])
{
s[0] = HEXCHARS[(i >> 60U) & 0xfU];
s[1] = HEXCHARS[(i >> 56U) & 0xfU];
s[2] = HEXCHARS[(i >> 52U) & 0xfU];
s[3] = HEXCHARS[(i >> 48U) & 0xfU];
s[4] = HEXCHARS[(i >> 44U) & 0xfU];
s[5] = HEXCHARS[(i >> 40U) & 0xfU];
s[6] = HEXCHARS[(i >> 36U) & 0xfU];
s[7] = HEXCHARS[(i >> 32U) & 0xfU];
s[8] = HEXCHARS[(i >> 28U) & 0xfU];
s[9] = HEXCHARS[(i >> 24U) & 0xfU];
s[10] = HEXCHARS[(i >> 20U) & 0xfU];
s[11] = HEXCHARS[(i >> 16U) & 0xfU];
s[12] = HEXCHARS[(i >> 12U) & 0xfU];
s[13] = HEXCHARS[(i >> 8U) & 0xfU];
s[14] = HEXCHARS[(i >> 4U) & 0xfU];
s[15] = HEXCHARS[i & 0xfU];
s[16] = 0;
return s;
}
uint64_t unhex(const char *s)
{
uint64_t n = 0;
if (s) {
int k = 0;
while (k < 16) {
char hc = *(s++);
if (!hc) break;
uint8_t c = 0;
if ((hc >= 48)&&(hc <= 57))
c = hc - 48;
else if ((hc >= 97)&&(hc <= 102))
c = hc - 87;
else if ((hc >= 65)&&(hc <= 70))
c = hc - 55;
n <<= 4U;
n |= (uint64_t)c;
++k;
}
}
return n;
}
char *hex10(uint64_t i,char s[11]) char *hex10(uint64_t i,char s[11])
{ {
s[0] = HEXCHARS[(i >> 36U) & 0xfU]; s[0] = HEXCHARS[(i >> 36U) & 0xfU];

View file

@ -85,17 +85,18 @@ char *decimal(unsigned long n,char s[24]);
* @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur. * @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur.
* @return Pointer to s containing hex string with trailing zero byte * @return Pointer to s containing hex string with trailing zero byte
*/ */
template<typename I> char *hex(uint8_t i,char s[3]);
static ZT_ALWAYS_INLINE char *hex(I x,char *s) char *hex(uint16_t i,char s[5]);
{ char *hex(uint32_t i,char s[9]);
char *const r = s; char *hex(uint64_t i,char s[17]);
for(unsigned int i=0,b=(sizeof(x)*8);i<sizeof(x);++i) {
*(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf]; /**
*(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf]; * Decode an unsigned integer in hex format
} *
*s = (char)0; * @param s String to decode, non-hex chars are ignored
return r; * @return Unsigned integer
} */
uint64_t unhex(const char *s);
/** /**
* Convert the least significant 40 bits of a uint64_t to hex * Convert the least significant 40 bits of a uint64_t to hex