From cdc6c42375fb898ea54791f7d775d1215129b724 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 25 Jan 2020 18:02:11 -0800 Subject: [PATCH] Take Dictionary behind the barn... --- {node => attic}/Str.hpp | 109 +++-------- node/Buf.cpp | 92 ++++++++- node/Buf.hpp | 118 +++--------- node/CMakeLists.txt | 1 - node/Constants.hpp | 3 - node/Dictionary.cpp | 408 +++++++++++++++++++++++----------------- node/Dictionary.hpp | 254 +++++++++++-------------- node/Hashtable.hpp | 6 +- node/NetworkConfig.cpp | 294 ++++++++++++++--------------- node/Utils.cpp | 79 ++++++++ node/Utils.hpp | 23 +-- 11 files changed, 727 insertions(+), 660 deletions(-) rename {node => attic}/Str.hpp (66%) diff --git a/node/Str.hpp b/attic/Str.hpp similarity index 66% rename from node/Str.hpp rename to attic/Str.hpp index 53f21611c..6738dd5b8 100644 --- a/node/Str.hpp +++ b/attic/Str.hpp @@ -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(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)); } diff --git a/node/Buf.cpp b/node/Buf.cpp index f5ef31fc7..e41fe152a 100644 --- a/node/Buf.cpp +++ b/node/Buf.cpp @@ -16,9 +16,97 @@ namespace ZeroTier { #ifdef __GNUC__ -uintptr_t Buf_pool = 0; +uintptr_t _Buf_pool = 0; #else -std::atomic Buf_pool(0); +std::atomic _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 diff --git a/node/Buf.hpp b/node/Buf.hpp index 650663fa8..a3bc7ad56 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef __GNUC__ #include @@ -37,10 +38,21 @@ namespace ZeroTier { #ifdef __GNUC__ -extern uintptr_t Buf_pool; +extern uintptr_t _Buf_pool; #else -extern std::atomic Buf_pool; +extern std::atomic _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 Buf_pool; * * @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE) */ -template +template class Buf { friend class SharedPtr< Buf >; + 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 &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 > 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(); - } 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(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 *)b); + throw std::bad_alloc(); } /** diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index 0d1e59591..5dfc6b1bb 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -38,7 +38,6 @@ set(core_headers SelfAwareness.hpp SHA512.hpp SharedPtr.hpp - Str.hpp Switch.hpp Tag.hpp Topology.hpp diff --git a/node/Constants.hpp b/node/Constants.hpp index fe44406f5..8371c97bc 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -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 diff --git a/node/Dictionary.cpp b/node/Dictionary.cpp index 61a3d1725..bf668535c 100644 --- a/node/Dictionary.cpp +++ b/node/Dictionary.cpp @@ -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(&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 &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 &Dictionary::operator[](const char *k) const +{ + static const std::vector emptyEntry; + const std::vector *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 &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 &e = (*this)[k]; + e.resize(5); + Utils::hex(v,(char *)e.data()); +} + +void Dictionary::add(const char *k,uint32_t v) +{ + std::vector &e = (*this)[k]; + e.resize(9); + Utils::hex(v,(char *)e.data()); +} + +void Dictionary::add(const char *k,uint64_t v) +{ + std::vector &e = (*this)[k]; + e.resize(17); + Utils::hex(v,(char *)e.data()); +} + +void Dictionary::add(const char *k,const Address &v) +{ + std::vector &e = (*this)[k]; + e.resize(11); + v.toString((char *)e.data()); +} + +void Dictionary::add(const char *k,const char *v) +{ + std::vector &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 &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 &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 &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 &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 &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 >::Iterator ti(const_cast(this)->_t); + uint64_t *kk = nullptr; + std::vector *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::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 *v = nullptr; + bool escape = false; + for(unsigned int di=0;di(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(&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 diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 546eb2510..e86fc5e59 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -18,181 +18,157 @@ #include "Utils.hpp" #include "Address.hpp" #include "Buf.hpp" +#include "Hashtable.hpp" #include - -#define ZT_DICTIONARY_MAX_CAPACITY 65536 +#include 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 &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 &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 &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 > _t; }; } // namespace ZeroTier diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index e262abfc0..972cf2505 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -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) diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 8b504d31f..626dcb40c 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -44,138 +44,105 @@ NetworkConfig::NetworkConfig() : bool NetworkConfig::toDictionary(Dictionary &d,bool includeLegacy) const { uint8_t tmp[16384]; - std::vector 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;icapabilityCount;++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;itagCount;++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;icertificateOfOwnershipCount;++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;ispecialistCount;++i) { - Utils::storeBigEndian(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;irouteCount;++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;istaticIpCount;++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 *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(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 > tmp(new Buffer()); - 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 *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(p); + while (((p + 8) <= blob->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) { + this->specialists[this->specialistCount++] = Utils::loadBigEndian(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(&(this->routes[this->routeCount].target))->deserialize(*tmp,p); - p += reinterpret_cast(&(this->routes[this->routeCount].via))->deserialize(*tmp,p); - this->routes[this->routeCount].flags = tmp->at(p); p += 2; - this->routes[this->routeCount].metric = tmp->at(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(blob->data() + p); p += 2; + this->routes[this->routeCount].metric = Utils::loadBigEndian(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) diff --git a/node/Utils.cpp b/node/Utils.cpp index 691441953..92ca9836f 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -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]; diff --git a/node/Utils.hpp b/node/Utils.hpp index 31601d0b7..2855151ed 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -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 -static ZT_ALWAYS_INLINE char *hex(I x,char *s) -{ - char *const r = s; - for(unsigned int i=0,b=(sizeof(x)*8);i> (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