From b104bb4762a07dcb0a950b33c7419b298c2b6267 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 15 Jun 2016 18:47:35 -0700 Subject: New super-packed dictionary -- we are going back to a backward compatibile format with the old netconf but in an embedded-friendly way. This is simpler. --- node/Dictionary.hpp | 411 +++++++++++++++++++++++++++++----------------------- 1 file changed, 230 insertions(+), 181 deletions(-) (limited to 'node/Dictionary.hpp') diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index d4cdd230..66072952 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -20,261 +20,310 @@ #define ZT_DICTIONARY_HPP #include "Constants.hpp" - -#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF +#include "Utils.hpp" #include #include -#include -#include -#include - -#include "Utils.hpp" -// Three fields are added/updated by sign() -#define ZT_DICTIONARY_SIGNATURE "~!ed25519" -#define ZT_DICTIONARY_SIGNATURE_IDENTITY "~!sigid" -#define ZT_DICTIONARY_SIGNATURE_TIMESTAMP "~!sigts" +#define ZT_DICTIONARY_MAX_SIZE 16384 namespace ZeroTier { -class Identity; - /** - * Simple key/value dictionary with string serialization + * A small key=value store + * + * This stores data in the form of a blob of max size ZT_DICTIONARY_MAX_SIZE. + * It's *technically* human-readable to be backward compatible with old format + * netconfs, but it can store binary data and doing this will negatively impact + * its human-readability. * - * The serialization format is a flat key=value with backslash escape. - * It does not support comments or other syntactic complexities. It is - * human-readable if the keys and values in the dictionary are also - * human-readable. Otherwise it might contain unprintable characters. + * In any case nulls are always escaped, making the serialized form of this + * object a valid null-terminated C-string. Appending it to a buffer appends + * it as such. * - * Keys beginning with "~!" are reserved for signature data fields. + * Keys cannot contain binary data, CR/LF, nulls, or the equals (=) sign. + * Adding such a key will result in an invalid entry (but isn't dangerous). * - * It's stored as a simple vector and can be linearly scanned or - * binary searched. Dictionaries are only used for very small things - * outside the core loop, so this is not a significant performance - * issue and it reduces memory use and code footprint. + * There is code to test and fuzz this in selftest.cpp. */ -class Dictionary : public std::vector< std::pair > +class Dictionary { public: - Dictionary() {} - - /** - * @param s String-serialized dictionary - * @param maxlen Maximum length of buffer - */ - Dictionary(const char *s,unsigned int maxlen) { fromString(s,maxlen); } - - /** - * @param s String-serialized dictionary - */ - Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); } - - iterator find(const std::string &key); - const_iterator find(const std::string &key) const; - - /** - * Get a key, returning a default if not present - * - * @param key Key to look up - * @param dfl Default if not present - * @return Value or default - */ - inline const std::string &get(const std::string &key,const std::string &dfl) const + Dictionary() { - const_iterator e(find(key)); - if (e == end()) - return dfl; - return e->second; + _d[0] = (char)0; } - /** - * @param key Key to get - * @param dfl Default boolean result if key not found or empty (default: false) - * @return Boolean value of key - */ - bool getBoolean(const std::string &key,bool dfl = false) const; + Dictionary(const char *s) + { + Utils::scopy(_d,sizeof(_d),s); + } - /** - * @param key Key to get - * @param dfl Default value if not present (default: 0) - * @return Value converted to unsigned 64-bit int or 0 if not found - */ - inline uint64_t getUInt(const std::string &key,uint64_t dfl = 0) const + inline void load(const char *s) { - const_iterator e(find(key)); - if (e == end()) - return dfl; - return Utils::strToU64(e->second.c_str()); + Utils::scopy(_d,sizeof(_d),s); } /** - * @param key Key to get - * @param dfl Default value if not present (default: 0) - * @return Value converted to unsigned 64-bit int or 0 if not found + * Delete all entries */ - inline uint64_t getHexUInt(const std::string &key,uint64_t dfl = 0) const + inline void clear() { - const_iterator e(find(key)); - if (e == end()) - return dfl; - return Utils::hexStrToU64(e->second.c_str()); + _d[0] = (char)0; } /** - * @param key Key to get - * @param dfl Default value if not present (default: 0) - * @return Value converted to signed 64-bit int or 0 if not found + * @return Size of dictionary in bytes not including terminating NULL */ - inline int64_t getInt(const std::string &key,int64_t dfl = 0) const + inline unsigned int sizeBytes() const { - const_iterator e(find(key)); - if (e == end()) - return dfl; - return Utils::strTo64(e->second.c_str()); + for(unsigned int i=0;i= destlen) { + dest[destlen-1] = (char)0; + return (int)(destlen-1); + } + dest[j++] = *p; + } + ++p; + } + } + } else { + ++p; + } + } + } } /** - * @param key Key to set - * @param value String value + * @param key Key to look up + * @param dfl Default value if not found in dictionary (a key with an empty value is considered not found) + * @return Boolean value of key or 'dfl' if not found */ - inline void set(const std::string &key,const std::string &value) + bool getBoolean(const char *key,bool dfl = false) const { - (*this)[key] = value; + char tmp[128]; + if (this->get(key,tmp,sizeof(tmp)) >= 1) { + switch(tmp[0]) { + case '1': + case 't': + case 'T': + case 'y': + case 'Y': + return true; + default: + return false; + } + } + return dfl; } /** - * @param key Key to set - * @param value Boolean value + * @param key Key to look up + * @param dfl Default value or 0 if unspecified + * @return Decoded hex UInt value or 'dfl' if not found */ - inline void set(const std::string &key,bool value) + inline uint64_t getHexUInt(const char *key,uint64_t dfl = 0) const { - (*this)[key] = ((value) ? "1" : "0"); + char tmp[128]; + if (this->get(key,tmp,sizeof(tmp)) >= 1) + return Utils::hexStrToU64(tmp); + return dfl; } /** - * @param key Key to set - * @param value Integer value + * Add a new key=value pair + * + * If the key is already present this will append another, but the first + * will always be returned by get(). There is no erase(). This is designed + * to be generated and shipped, not as an editable data structure. + * + * @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 */ - inline void set(const std::string &key,uint64_t value) + inline bool add(const char *key,const char *value,int vlen = -1) { - char tmp[24]; - Utils::snprintf(tmp,sizeof(tmp),"%llu",(unsigned long long)value); - (*this)[key] = tmp; + for(unsigned int i=0;iadd(key,(value) ? "1" : "0",1); } - /** - * @param key Key to set - * @param value Integer value + /** + * Add a 64-bit integer (unsigned) as a hex value */ - inline void setHex(const std::string &key,uint64_t value) + inline void add(const char *key,uint64_t value) { - char tmp[24]; + char tmp[128]; Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value); - (*this)[key] = tmp; + this->add(key,tmp,-1); } /** * @param key Key to check - * @return True if dictionary contains key - */ - inline bool contains(const std::string &key) const { return (find(key) != end()); } - - /** - * @return String-serialized dictionary - */ - std::string toString() const; - - /** - * Clear and initialize from a string - * - * @param s String-serialized dictionary - * @param maxlen Maximum length of string buffer - */ - void fromString(const char *s,unsigned int maxlen); - inline void fromString(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); } - void updateFromString(const char *s,unsigned int maxlen); - inline void update(const char *s,unsigned int maxlen) { updateFromString(s, maxlen); } - inline void update(const std::string &s) { updateFromString(s.c_str(),(unsigned int)s.length()); } - - /** - * @return True if this dictionary is cryptographically signed - */ - inline bool hasSignature() const { return (find(ZT_DICTIONARY_SIGNATURE) != end()); } - - /** - * @return Signing identity in string-serialized format or empty string if none + * @return True if key is present */ - inline std::string signingIdentity() const { return get(ZT_DICTIONARY_SIGNATURE_IDENTITY,std::string()); } - - /** - * @return Signature timestamp in milliseconds since epoch or 0 if none - */ - uint64_t signatureTimestamp() const; - - /** - * @param key Key to erase - */ - void eraseKey(const std::string &key); - - /** - * Remove any signature from this dictionary - */ - inline void removeSignature() + inline bool contains(const char *key) const { - eraseKey(ZT_DICTIONARY_SIGNATURE); - eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY); - eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP); + char tmp[2]; + return (this->get(key,tmp,2) >= 0); } /** - * Add or update signature fields with a signature of all other keys and values - * - * @param with Identity to sign with (must have secret key) - * @param now Current time - * @return True on success + * @return Dictionary data as a 0-terminated C-string */ - bool sign(const Identity &id,uint64_t now); - - /** - * Verify signature against an identity - * - * @param id Identity to verify against - * @return True if signature verification OK - */ - bool verify(const Identity &id) const; + inline const char *data() const { return _d; } private: - void _mkSigBuf(std::string &buf) const; - static void _appendEsc(const char *data,unsigned int len,std::string &to); + char _d[ZT_DICTIONARY_MAX_SIZE]; }; } // namespace ZeroTier -#endif // ZT_SUPPORT_OLD_STYLE_NETCONF - #endif -- cgit v1.2.3