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

View file

@ -16,9 +16,97 @@
namespace ZeroTier {
#ifdef __GNUC__
uintptr_t Buf_pool = 0;
uintptr_t _Buf_pool = 0;
#else
std::atomic<uintptr_t> Buf_pool(0);
std::atomic<uintptr_t> _Buf_pool(0);
#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

View file

@ -23,6 +23,7 @@
#include <cstdint>
#include <cstring>
#include <cstdlib>
#include <stdexcept>
#ifndef __GNUC__
#include <atomic>
@ -37,10 +38,21 @@
namespace ZeroTier {
#ifdef __GNUC__
extern uintptr_t Buf_pool;
extern uintptr_t _Buf_pool;
#else
extern std::atomic<uintptr_t> Buf_pool;
extern std::atomic<uintptr_t> _Buf_pool;
#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
@ -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)
*/
template<typename U = void>
template<typename U = int>
class Buf
{
friend class SharedPtr< Buf<U> >;
friend void _Buf_release(void *,std::size_t);
friend void *_Buf_get();
friend void freeBufPool();
private:
// 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); }
public:
static void operator delete(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
}
}
static void operator delete(void *ptr,std::size_t sz) { _Buf_release(ptr,sz); }
/**
* 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()
{
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 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;
}
void *const b = _Buf_get();
if (b)
return SharedPtr<Buf>((Buf *)b);
throw std::bad_alloc();
}
/**

View file

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

View file

@ -231,9 +231,6 @@
#define ZT_CRYPTO_ALG_C25519 0
#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 */
#define ZT_ETHERTYPE_IPV4 0x0800
#define ZT_ETHERTYPE_ARP 0x0806

View file

@ -15,210 +15,264 @@
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) {
if ((s)&&(i < len)) {
if (!(_d[i] = *s))
s = (const char *)0;
else ++s;
} else _d[i] = (char)0;
uint64_t n = 0;
unsigned int i = 0;
for(;;) {
char c = k[i];
if (!c) break;
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
return -1;
std::vector<uint8_t> &Dictionary::operator[](const char *k)
{
return _t[_toKey(k)];
}
while (*p) {
k = key;
while ((*k)&&(*p)) {
if (*p != *k)
break;
++k;
if (++p == eof) {
dest[0] = (char)0;
return -1;
}
}
const std::vector<uint8_t> &Dictionary::operator[](const char *k) const
{
static const std::vector<uint8_t> emptyEntry;
const std::vector<uint8_t> *const e = _t.get(_toKey(k));
return (e) ? *e : emptyEntry;
}
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;
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;
}
}
dest[0] = (char)0;
return -1;
}
bool Dictionary::add(const char *key,const char *value,int vlen)
void Dictionary::add(const char *k,const void *data,unsigned int len)
{
for(unsigned int i=0;i<ZT_DICTIONARY_MAX_CAPACITY;++i) {
if (!_d[i]) {
unsigned int j = i;
std::vector<uint8_t> &e = (*this)[k];
if (len != 0) {
e.assign((const uint8_t *)data,(const uint8_t *)data + len);
} else {
e.clear();
}
}
if (j > 0) {
_d[j++] = (char)10;
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
}
const char *p = key;
while (*p) {
_d[j++] = *(p++);
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
}
_d[j++] = '=';
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
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;
}
p = value;
int k = 0;
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
switch(*p) {
case 0:
case 13:
case 10:
case '\\':
case '=':
_d[j++] = '\\';
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
switch(*p) {
case 0: _d[j++] = '0'; break;
case 13: _d[j++] = 'r'; break;
case 10: _d[j++] = 'n'; break;
case '\\': _d[j++] = '\\'; break;
case '=': _d[j++] = 'e'; break;
}
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
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;
v[i] = (char)e[i];
++i;
}
v[i] = 0;
}
void Dictionary::clear()
{
_t.clear();
}
void Dictionary::encode(std::vector<uint8_t> &out) const
{
uint64_t str[2] = { 0,0 }; // second entry causes all strings to be null-terminated even if 8 chars in length
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);
}
out.push_back(61); // =
for(std::vector<uint8_t>::const_iterator i(vv->begin());i!=vv->end();++i) {
uint8_t c = *i;
switch(c) {
case 0:
out.push_back(92);
out.push_back(48);
break;
case 10:
out.push_back(92);
out.push_back(110);
break;
case 13:
out.push_back(92);
out.push_back(114);
break;
case 61:
out.push_back(92);
out.push_back(101);
break;
case 92:
out.push_back(92);
out.push_back(92);
break;
default:
out.push_back(c);
break;
}
}
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:
_d[j++] = *p;
if (j == ZT_DICTIONARY_MAX_CAPACITY) {
_d[i] = (char)0;
return false;
}
v->push_back(c);
break;
}
++p;
++k;
} 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;
}
_d[j] = (char)0;
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);
return true;
}
} // namespace ZeroTier

View file

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

View file

@ -35,7 +35,7 @@ private:
struct _Bucket
{
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 &operator=(const _Bucket &b) { k = b.k; v = b.v; return *this; }
_Bucket *next; // must be set manually for each _Bucket
@ -57,7 +57,7 @@ public:
/**
* @param ht Hash table to iterate over
*/
ZT_ALWAYS_INLINE Iterator(Hashtable &ht) :
explicit ZT_ALWAYS_INLINE Iterator(Hashtable &ht) :
_idx(0),
_ht(&ht),
_b(ht._t[0])
@ -95,7 +95,7 @@ public:
/**
* @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))),
_bc(bc),
_s(0)

View file

@ -44,138 +44,105 @@ NetworkConfig::NetworkConfig() :
bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const
{
uint8_t tmp[16384];
std::vector<uint8_t> buf;
char tmp2[128];
try {
d.clear();
d.clear();
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);
// Try to put the more human-readable fields first
if (this->com)
d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,tmp,this->com.marshal(tmp));
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
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
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);
if (l < 0)
return false;
buf.insert(buf.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();
for(unsigned int i=0;i<this->tagCount;++i) {
int l = this->tags[i].marshal(tmp);
if (l < 0)
return false;
buf.insert(buf.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();
for(unsigned int i=0;i<this->certificateOfOwnershipCount;++i) {
int l = this->certificatesOfOwnership[i].marshal(tmp);
if (l < 0)
return false;
buf.insert(buf.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();
for(unsigned int i=0;i<this->specialistCount;++i) {
Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]);
buf.insert(buf.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();
for(unsigned int i=0;i<this->routeCount;++i) {
int l = asInetAddress(this->routes[i].target).marshal(tmp);
if (l < 0)
return false;
buf.insert(buf.end(),tmp,tmp + l);
l = asInetAddress(this->routes[i].via).marshal(tmp);
if (l < 0)
return false;
buf.insert(buf.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();
for(unsigned int i=0;i<this->staticIpCount;++i) {
int l = this->staticIps[i].marshal(tmp);
if (l < 0)
return false;
buf.insert(buf.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;
}
if (this->ruleCount) {
buf.resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX);
int l = Capability::marshalVirtualNetworkRules(buf.data(),rules,ruleCount);
if (l > 0) {
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,(const char *)buf.data(),l))
std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES]);
for (unsigned int i = 0; i < this->capabilityCount; ++i) {
int l = this->capabilities[i].marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
}
}
return true;
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_TAGS]);
for (unsigned int i = 0; i < this->tagCount; ++i) {
int l = this->tags[i].marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
}
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
for (unsigned int i = 0; i < this->certificateOfOwnershipCount; ++i) {
int l = this->certificatesOfOwnership[i].marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
}
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS]);
for (unsigned int i = 0; i < this->specialistCount; ++i) {
Utils::storeBigEndian<uint64_t>(tmp,this->specialists[i]);
blob->insert(blob->end(),tmp,tmp + 8);
}
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_ROUTES]);
for (unsigned int i = 0; i < this->routeCount; ++i) {
int l = asInetAddress(this->routes[i].target).marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
l = asInetAddress(this->routes[i].via).marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
}
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS]);
for (unsigned int i = 0; i < this->staticIpCount; ++i) {
int l = this->staticIps[i].marshal(tmp);
if (l < 0)
return false;
blob->insert(blob->end(),tmp,tmp + l);
}
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_RULES]);
if (this->ruleCount) {
blob->resize(ruleCount * ZT_VIRTUALNETWORKRULE_MARSHAL_SIZE_MAX);
int l = Capability::marshalVirtualNetworkRules(blob->data(),rules,ruleCount);
if (l > 0)
blob->resize(l);
}
return true;
} catch ( ... ) {}
return false;
}
bool NetworkConfig::fromDictionary(const Dictionary &d)
{
static const NetworkConfig NIL_NC;
ScopedPtr< Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> > tmp(new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>());
try {
*this = NIL_NC;
// Fields that are always present, new or old
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
if (!this->networkId)
return false;
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->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
if (!this->issuedTo)
return false;
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);
if (this->mtu < 1280)
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) {
return false;
} else {
// Otherwise we can use the new fields
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);
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
this->com.deserialize(*tmp,0);
const std::vector<uint8_t> *blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_COM]);
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 {
unsigned int p = 0;
while (p < tmp->size()) {
while (p < blob->size()) {
Capability cap;
p += cap.deserialize(*tmp,p);
this->capabilities[this->capabilityCount++] = cap;
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;
}
} catch ( ... ) {}
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 {
unsigned int p = 0;
while (p < tmp->size()) {
while (p < blob->size()) {
Tag tag;
p += tag.deserialize(*tmp,p);
this->tags[this->tagCount++] = tag;
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;
}
} catch ( ... ) {}
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
}
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP,*tmp)) {
unsigned int p = 0;
while (p < tmp->size()) {
if (certificateOfOwnershipCount < ZT_MAX_CERTIFICATES_OF_OWNERSHIP)
p += certificatesOfOwnership[certificateOfOwnershipCount++].deserialize(*tmp,p);
else {
CertificateOfOwnership foo;
p += foo.deserialize(*tmp,p);
blob = &(d[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATES_OF_OWNERSHIP]);
if (!blob->empty()) {
try {
unsigned int p = 0;
while (p < blob->size()) {
CertificateOfOwnership coo;
int l = coo.unmarshal(blob->data() + p,(int)(blob->size() - p));
if (l < 0)
return false;
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;
while ((p + 8) <= tmp->size()) {
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS)
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
while (((p + 8) <= blob->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
this->specialists[this->specialistCount++] = Utils::loadBigEndian<uint64_t>(blob->data() + p);
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;
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
while ((p < blob->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
int l = asInetAddress(this->routes[this->routeCount].target).unmarshal(blob->data(),(int)(blob->size() - p));
if (l < 0)
return false;
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;
}
}
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;
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
while ((p < blob->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
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;
unsigned int p = 0;
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
if (Capability::unmarshalVirtualNetworkRules(blob->data(),(int)blob->size(),this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES) < 0)
return false;
}
}
return true;
} catch ( ... ) {
return false;
}
} catch ( ... ) {}
return false;
}
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;
}
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])
{
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.
* @return Pointer to s containing hex string with trailing zero byte
*/
template<typename I>
static ZT_ALWAYS_INLINE char *hex(I x,char *s)
{
char *const r = s;
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];
}
*s = (char)0;
return r;
}
char *hex(uint8_t i,char s[3]);
char *hex(uint16_t i,char s[5]);
char *hex(uint32_t i,char s[9]);
char *hex(uint64_t i,char s[17]);
/**
* Decode an unsigned integer in hex format
*
* @param s String to decode, non-hex chars are ignored
* @return Unsigned integer
*/
uint64_t unhex(const char *s);
/**
* Convert the least significant 40 bits of a uint64_t to hex