From 1e0a930d23d49a4e5d861dc2abc99fd87359be1e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 19 Feb 2020 09:28:18 -0800 Subject: [PATCH] Create a container for identity hashes, replace some "volatile" hacky junk with std::atomic, clean up some security checks, rename raw data in Buf to unsafeData to permit search for unsafe. --- node/Buf.hpp | 243 ++++++++++++++++++++++++++++++++++---------- node/CMakeLists.txt | 1 + node/Constants.hpp | 29 ++---- node/Endpoint.cpp | 200 +++++++++++++++++------------------- node/Endpoint.hpp | 77 +++++++------- node/H.hpp | 86 ++++++++++++++++ node/Identity.cpp | 168 ++++++++++++++++++------------ node/Identity.hpp | 29 ++++-- node/Membership.cpp | 19 ++-- node/Network.hpp | 13 ++- node/Peer.cpp | 16 +-- node/Peer.hpp | 42 ++++---- node/Protocol.cpp | 14 +-- node/Protocol.hpp | 22 ++-- node/Topology.cpp | 45 ++++---- node/Topology.hpp | 83 ++++++++------- node/Trace.cpp | 12 +-- node/VL1.cpp | 114 +++++++++++---------- 18 files changed, 743 insertions(+), 470 deletions(-) create mode 100644 node/H.hpp diff --git a/node/Buf.hpp b/node/Buf.hpp index fd8d3c28a..de01e9ae4 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -147,7 +147,7 @@ public: if (l <= ZT_BUF_MEM_SIZE) { r.b.move(s->b); if (s->s > 0) - memmove(r.b->b,r.b->b + s->s,l); + memmove(r.b->unsafeData,r.b->unsafeData + s->s,l); r.e = l; while (++s != fcv.end()) { @@ -157,7 +157,7 @@ public: r.e = 0; break; } - memcpy(r.b->b + r.e,s->b->b + s->s,l); + memcpy(r.b->unsafeData + r.e,s->b->unsafeData + s->s,l); s->b.zero(); // let go of buffer in vector as soon as possible r.e += l; } @@ -171,12 +171,12 @@ public: */ ZT_ALWAYS_INLINE Buf() noexcept {} - ZT_ALWAYS_INLINE Buf(const Buf &b2) noexcept { memcpy(b,b2.b,ZT_BUF_MEM_SIZE); } + ZT_ALWAYS_INLINE Buf(const Buf &b2) noexcept { memcpy(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE); } ZT_ALWAYS_INLINE Buf &operator=(const Buf &b2) noexcept { if (this != &b2) - memcpy(b,b2.b,ZT_BUF_MEM_SIZE); + memcpy(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE); return *this; } @@ -206,29 +206,29 @@ public: /** * Set all memory to zero */ - ZT_ALWAYS_INLINE void clear() noexcept { memset(b,0,ZT_BUF_MEM_SIZE); } + ZT_ALWAYS_INLINE void clear() noexcept { memset(unsafeData,0,ZT_BUF_MEM_SIZE); } /** * Zero security critical data using Utils::burn() to ensure it's never optimized out. */ - ZT_ALWAYS_INLINE void burn() noexcept { Utils::burn(b,ZT_BUF_MEM_SIZE); } + ZT_ALWAYS_INLINE void burn() noexcept { Utils::burn(unsafeData,ZT_BUF_MEM_SIZE); } /** * Read a byte * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by 1) * @return Byte (undefined on overflow) */ ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const noexcept { const int s = ii++; - return b[(unsigned int)s & ZT_BUF_MEM_MASK]; + return unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK]; } /** * Read a 16-bit integer * - * @param ii Integer + * @param ii Index value-result parameter (incremented by 2) * @return Integer (undefined on overflow) */ ZT_ALWAYS_INLINE uint16_t rI16(int &ii) const noexcept @@ -240,14 +240,14 @@ public: ((uint16_t)data.bytes[s] << 8U) | (uint16_t)data.bytes[s + 1]); #else - return Utils::ntoh(*reinterpret_cast(b + s)); + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); #endif } /** * Read a 32-bit integer * - * @param ii Integer + * @param ii Index value-result parameter (incremented by 4) * @return Integer (undefined on overflow) */ ZT_ALWAYS_INLINE uint32_t rI32(int &ii) const noexcept @@ -261,14 +261,14 @@ public: ((uint32_t)data.bytes[s + 2] << 8U) | (uint32_t)data.bytes[s + 3]); #else - return Utils::ntoh(*reinterpret_cast(b + s)); + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); #endif } /** * Read a 64-bit integer * - * @param ii Integer + * @param ii Index value-result parameter (incremented by 8) * @return Integer (undefined on overflow) */ ZT_ALWAYS_INLINE uint64_t rI64(int &ii) const noexcept @@ -286,7 +286,7 @@ public: ((uint64_t)data.bytes[s + 6] << 8U) | (uint64_t)data.bytes[s + 7]); #else - return Utils::ntoh(*reinterpret_cast(b + s)); + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); #endif } @@ -301,7 +301,7 @@ public: * indicates. * * @tparam T Object type - * @param ii Iterator + * @param ii Index value-result parameter (incremented by object's size in bytes) * @param obj Object to read * @return Bytes read or a negative value on unmarshal error (passed from object) or overflow */ @@ -309,7 +309,7 @@ public: ZT_ALWAYS_INLINE int rO(int &ii,T &obj) const noexcept { if (ii < ZT_BUF_MEM_SIZE) { - int ms = obj.unmarshal(b + ii,ZT_BUF_MEM_SIZE - ii); + int ms = obj.unmarshal(unsafeData + ii,ZT_BUF_MEM_SIZE - ii); if (ms > 0) ii += ms; return ms; @@ -323,17 +323,17 @@ public: * Use this if the buffer's memory may get changed between reading and processing * what is read. * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by length of string) * @param buf Buffer to receive string * @param bufSize Capacity of buffer in bytes * @return Pointer to buf or NULL on overflow or error */ ZT_ALWAYS_INLINE char *rS(int &ii,char *const buf,const unsigned int bufSize) const noexcept { - const char *const s = (const char *)(b + ii); + const char *const s = (const char *)(unsafeData + ii); const int sii = ii; while (ii < ZT_BUF_MEM_SIZE) { - if (b[ii++] == 0) { + if (unsafeData[ii++] == 0) { memcpy(buf,s,ii - sii); return buf; } @@ -351,14 +351,14 @@ public: * This version avoids a copy and so is faster if the buffer won't be modified between * reading and processing. * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by length of string) * @return Pointer to null-terminated C-style string or NULL on overflow or error */ ZT_ALWAYS_INLINE const char *rSnc(int &ii) const noexcept { - const char *const s = (const char *)(b + ii); + const char *const s = (const char *)(unsafeData + ii); while (ii < ZT_BUF_MEM_SIZE) { - if (b[ii++] == 0) + if (unsafeData[ii++] == 0) return s; } return nullptr; @@ -370,15 +370,15 @@ public: * Use this if the buffer's memory may get changed between reading and processing * what is read. * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by len) * @param bytes Buffer to contain data to read * @param len Length of buffer * @return Pointer to data or NULL on overflow or error */ - ZT_ALWAYS_INLINE uint8_t *rB(int &ii,void *bytes,unsigned int len) const noexcept + ZT_ALWAYS_INLINE uint8_t *rB(int &ii,void *const bytes,const unsigned int len) const noexcept { if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) { - memcpy(bytes,b + ii,len); + memcpy(bytes,unsafeData + ii,len); return reinterpret_cast(bytes); } return nullptr; @@ -393,7 +393,7 @@ public: * This version avoids a copy and so is faster if the buffer won't be modified between * reading and processing. * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by len) * @param len Length of data field to obtain a pointer to * @return Pointer to field or NULL on overflow */ @@ -403,25 +403,102 @@ public: return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr; } + /** + * Load a value at an index without advancing the index + * + * Note that unlike the rI??() methods this does not increment ii and therefore + * will not necessarily result in a 'true' return from readOverflow(). It does + * however subject 'ii' to soft bounds masking like the gI??() methods. + */ + ZT_ALWAYS_INLINE uint8_t lI8(const int ii) const noexcept + { + return unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK]; + } + + /** + * Load a value at an index without advancing the index + * + * Note that unlike the rI??() methods this does not increment ii and therefore + * will not necessarily result in a 'true' return from readOverflow(). It does + * however subject 'ii' to soft bounds masking like the gI??() methods. + */ + ZT_ALWAYS_INLINE uint16_t lI16(const int ii) const noexcept + { + const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + return ( + ((uint16_t)data.bytes[s] << 8U) | + (uint16_t)data.bytes[s + 1]); +#else + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); +#endif + } + + /** + * Load a value at an index without advancing the index + * + * Note that unlike the rI??() methods this does not increment ii and therefore + * will not necessarily result in a 'true' return from readOverflow(). It does + * however subject 'ii' to soft bounds masking like the gI??() methods. + */ + ZT_ALWAYS_INLINE uint32_t lI32(const int ii) const noexcept + { + const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + return ( + ((uint32_t)data.bytes[s] << 24U) | + ((uint32_t)data.bytes[s + 1] << 16U) | + ((uint32_t)data.bytes[s + 2] << 8U) | + (uint32_t)data.bytes[s + 3]); +#else + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); +#endif + } + + /** + * Load a value at an index without advancing the index + * + * Note that unlike the rI??() methods this does not increment ii and therefore + * will not necessarily result in a 'true' return from readOverflow(). It does + * however subject 'ii' to soft bounds masking like the gI??() methods. + */ + ZT_ALWAYS_INLINE uint8_t lI64(const int ii) const noexcept + { + const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + return ( + ((uint64_t)data.bytes[s] << 56U) | + ((uint64_t)data.bytes[s + 1] << 48U) | + ((uint64_t)data.bytes[s + 2] << 40U) | + ((uint64_t)data.bytes[s + 3] << 32U) | + ((uint64_t)data.bytes[s + 4] << 24U) | + ((uint64_t)data.bytes[s + 5] << 16U) | + ((uint64_t)data.bytes[s + 6] << 8U) | + (uint64_t)data.bytes[s + 7]); +#else + return Utils::ntoh(*reinterpret_cast(unsafeData + s)); +#endif + } + /** * Write a byte * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by 1) * @param n Byte */ - ZT_ALWAYS_INLINE void wI(int &ii,uint8_t n) noexcept + ZT_ALWAYS_INLINE void wI8(int &ii,const uint8_t n) noexcept { const int s = ii++; - b[(unsigned int)s & ZT_BUF_MEM_MASK] = n; + unsafeData[(unsigned int)s & ZT_BUF_MEM_MASK] = n; } /** * Write a 16-bit integer in big-endian byte order * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by 2) * @param n Integer */ - ZT_ALWAYS_INLINE void wI(int &ii,uint16_t n) noexcept + ZT_ALWAYS_INLINE void wI16(int &ii,const uint16_t n) noexcept { const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; ii += 2; @@ -429,17 +506,17 @@ public: b[s] = (uint8_t)(n >> 8U); b[s + 1] = (uint8_t)n; #else - *reinterpret_cast(b + s) = Utils::hton(n); + *reinterpret_cast(unsafeData + s) = Utils::hton(n); #endif } /** * Write a 32-bit integer in big-endian byte order * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by 4) * @param n Integer */ - ZT_ALWAYS_INLINE void wI(int &ii,uint32_t n) noexcept + ZT_ALWAYS_INLINE void wI32(int &ii,const uint32_t n) noexcept { const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; ii += 4; @@ -449,17 +526,17 @@ public: b[s + 2] = (uint8_t)(n >> 8U); b[s + 3] = (uint8_t)n; #else - *reinterpret_cast(b + s) = Utils::hton(n); + *reinterpret_cast(unsafeData + s) = Utils::hton(n); #endif } /** * Write a 64-bit integer in big-endian byte order * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by 8) * @param n Integer */ - ZT_ALWAYS_INLINE void wI(int &ii,uint64_t n) noexcept + ZT_ALWAYS_INLINE void wI64(int &ii,const uint64_t n) noexcept { const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; ii += 8; @@ -473,7 +550,7 @@ public: b[s + 6] = (uint8_t)(n >> 8U); b[s + 7] = (uint8_t)n; #else - *reinterpret_cast(b + s) = Utils::hton(n); + *reinterpret_cast(unsafeData + s) = Utils::hton(n); #endif } @@ -481,7 +558,7 @@ public: * Write an object implementing the marshal interface * * @tparam T Object type - * @param ii Iterator + * @param ii Index value-result parameter (incremented by size of object) * @param t Object to write */ template @@ -489,7 +566,7 @@ public: { const int s = ii; if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) { - int ms = t.marshal(b + s); + int ms = t.marshal(unsafeData + s); if (ms > 0) ii += ms; } else { @@ -500,7 +577,7 @@ public: /** * Write a C-style null-terminated string (including the trailing zero) * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by length of string) * @param s String to write (writes an empty string if this is NULL) */ ZT_ALWAYS_INLINE void wS(int &ii,const char *s) noexcept @@ -509,17 +586,17 @@ public: char c; do { c = *(s++); - wI(ii,(uint8_t)c); + wI8(ii,(uint8_t)c); } while (c); } else { - wI(ii,(uint8_t)0); + wI8(ii,0); } } /** * Write a byte array * - * @param ii Iterator + * @param ii Index value-result parameter (incremented by len) * @param bytes Bytes to write * @param len Size of data in bytes */ @@ -527,7 +604,65 @@ public: { const int s = ii; if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) - memcpy(b + s,bytes,len); + memcpy(unsafeData + s,bytes,len); + } + + /** + * Store a byte without advancing the index + */ + ZT_ALWAYS_INLINE void sI8(const int ii,const uint8_t n) noexcept + { + unsafeData[(unsigned int)ii & ZT_BUF_MEM_MASK] = n; + } + + /** + * Store an integer without advancing the index + */ + ZT_ALWAYS_INLINE void sI16(const int ii,const uint16_t n) noexcept + { + const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + b[s] = (uint8_t)(n >> 8U); + b[s + 1] = (uint8_t)n; +#else + *reinterpret_cast(unsafeData + s) = Utils::hton(n); +#endif + } + + /** + * Store an integer without advancing the index + */ + ZT_ALWAYS_INLINE void sI32(const int ii,const uint32_t n) noexcept + { + const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + b[s] = (uint8_t)(n >> 24U); + b[s + 1] = (uint8_t)(n >> 16U); + b[s + 2] = (uint8_t)(n >> 8U); + b[s + 3] = (uint8_t)n; +#else + *reinterpret_cast(unsafeData + s) = Utils::hton(n); +#endif + } + + /** + * Store an integer without advancing the index + */ + ZT_ALWAYS_INLINE void sI64(const int ii,const uint64_t n) noexcept + { + const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK; +#ifdef ZT_NO_UNALIGNED_ACCESS + b[s] = (uint8_t)(n >> 56U); + b[s + 1] = (uint8_t)(n >> 48U); + b[s + 2] = (uint8_t)(n >> 40U); + b[s + 3] = (uint8_t)(n >> 32U); + b[s + 4] = (uint8_t)(n >> 24U); + b[s + 5] = (uint8_t)(n >> 16U); + b[s + 6] = (uint8_t)(n >> 8U); + b[s + 7] = (uint8_t)n; +#else + *reinterpret_cast(unsafeData + s) = Utils::hton(n); +#endif } /** @@ -547,7 +682,7 @@ public: * @return Reference to 'b' cast to type T */ template - ZT_ALWAYS_INLINE T &as(const unsigned int i = 0) noexcept { return *reinterpret_cast(b + i); } + ZT_ALWAYS_INLINE T &as(const unsigned int i = 0) noexcept { return *reinterpret_cast(unsafeData + i); } /** * Cast data in 'b' to a (usually packed) structure type (const) @@ -561,14 +696,14 @@ public: * @return Reference to 'b' cast to type T */ template - ZT_ALWAYS_INLINE const T &as(const unsigned int i = 0) const noexcept { return *reinterpret_cast(b + i); } + ZT_ALWAYS_INLINE const T &as(const unsigned int i = 0) const noexcept { return *reinterpret_cast(unsafeData + i); } - ZT_ALWAYS_INLINE bool operator==(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) == 0); } - ZT_ALWAYS_INLINE bool operator!=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) != 0); } - ZT_ALWAYS_INLINE bool operator<(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) < 0); } - ZT_ALWAYS_INLINE bool operator<=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) <= 0); } - ZT_ALWAYS_INLINE bool operator>(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) > 0); } - ZT_ALWAYS_INLINE bool operator>=(const Buf &b2) const noexcept { return (memcmp(b,b2.b,ZT_BUF_MEM_SIZE) >= 0); } + ZT_ALWAYS_INLINE bool operator==(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) == 0); } + ZT_ALWAYS_INLINE bool operator!=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) != 0); } + ZT_ALWAYS_INLINE bool operator<(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) < 0); } + ZT_ALWAYS_INLINE bool operator<=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) <= 0); } + ZT_ALWAYS_INLINE bool operator>(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) > 0); } + ZT_ALWAYS_INLINE bool operator>=(const Buf &b2) const noexcept { return (memcmp(unsafeData,b2.unsafeData,ZT_BUF_MEM_SIZE) >= 0); } /** * Raw data held in buffer @@ -577,7 +712,7 @@ public: * They exist to allow reads and writes of integer types to silently overflow if a * read or write is performed at the end of the buffer. */ - uint8_t b[ZT_BUF_MEM_SIZE + 8]; + uint8_t unsafeData[ZT_BUF_MEM_SIZE + 8]; private: // Next item in free buffer pool linked list if Buf is placed in pool, undefined and unused otherwise diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index 2cf3ba3a2..8209a5de3 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -16,6 +16,7 @@ set(core_headers ECC384.hpp Expect.hpp FCV.hpp + H.hpp Hashtable.hpp Identity.hpp InetAddress.hpp diff --git a/node/Constants.hpp b/node/Constants.hpp index 8e3551d06..c7df55c54 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -66,16 +66,6 @@ */ #define ZT_MAX_NETWORK_CONFIG_BYTES 131072 -/** - * Size of RX queue in packets - */ -#define ZT_RX_QUEUE_SIZE 32 - -/** - * Size of TX queue in packets - */ -#define ZT_TX_QUEUE_SIZE 32 - /** * Length of peer shared secrets (256-bit, do not change) */ @@ -109,16 +99,6 @@ */ #define ZT_WHOIS_RETRY_DELAY 500 -/** - * Transmit queue entry timeout - */ -#define ZT_TRANSMIT_QUEUE_TIMEOUT 5000 - -/** - * Receive queue entry timeout - */ -#define ZT_RECEIVE_QUEUE_TIMEOUT 5000 - /** * Maximum number of ZT hops allowed (this is not IP hops/TTL) * @@ -156,6 +136,15 @@ */ #define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_PING_PERIOD + 5000) +/** + * Global timeout for peers in milliseconds + * + * This is global as in "entire world," and this value is 30 days. In this + * code the global timeout is used to determine when to ignore cached + * peers and their identity<>address mappings. + */ +#define ZT_PEER_GLOBAL_TIMEOUT 2592000000LL + /** * Maximum interval between sort/prioritize of paths for a peer */ diff --git a/node/Endpoint.cpp b/node/Endpoint.cpp index 1a9d8b414..e1afc8cc6 100644 --- a/node/Endpoint.cpp +++ b/node/Endpoint.cpp @@ -19,14 +19,13 @@ bool Endpoint::operator==(const Endpoint &ep) const { if (_t == ep._t) { switch(_t) { - default: return true; - case INETADDR_V4: - case INETADDR_V6: return (inetAddr() == ep.inetAddr()); - case DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0)); - case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0)); - case URL: return (strcmp(_v.url,ep._v.url) == 0); - case ETHERNET: return (_v.eth == ep._v.eth); - case WEBRTC: return ((_v.webrtc.offerLen == ep._v.webrtc.offerLen)&&(memcmp(_v.webrtc.offer,ep._v.webrtc.offer,_v.webrtc.offerLen) == 0)); + default: return true; + case TYPE_ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0)); + case TYPE_DNSNAME: return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0)); + case TYPE_URL: return (strcmp(_v.url,ep._v.url) == 0); + case TYPE_ETHERNET: return (_v.eth == ep._v.eth); + case TYPE_INETADDR_V4: + case TYPE_INETADDR_V6: return (inetAddr() == ep.inetAddr()); } } return false; @@ -39,21 +38,15 @@ bool Endpoint::operator<(const Endpoint &ep) const } else if (_t == ep._t) { int ncmp; switch(_t) { - case INETADDR_V4: - case INETADDR_V6: - return (inetAddr() < ep.inetAddr()); - case DNSNAME: + case TYPE_ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0)); + case TYPE_DNSNAME: ncmp = strcmp(_v.dns.name,ep._v.dns.name); - return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port)); - case ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0)); - case URL: return (strcmp(_v.url,ep._v.url) < 0); - case ETHERNET: return (_v.eth < ep._v.eth); - case WEBRTC: - if (_v.webrtc.offerLen < ep._v.webrtc.offerLen) - return true; - else if (_v.webrtc.offerLen == ep._v.webrtc.offerLen) - return memcmp(_v.webrtc.offer,ep._v.webrtc.offer,_v.webrtc.offerLen) < 0; - default: return false; + return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port)); + case TYPE_URL: return (strcmp(_v.url,ep._v.url) < 0); + case TYPE_ETHERNET: return (_v.eth < ep._v.eth); + case TYPE_INETADDR_V4: + case TYPE_INETADDR_V6: return (inetAddr() < ep.inetAddr()); + default: return false; } } return false; @@ -67,10 +60,15 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept Utils::storeBigEndian(data + 3,(int16_t)_l[1]); Utils::storeBigEndian(data + 5,(int16_t)_l[2]); switch(_t) { - case INETADDR_V4: - case INETADDR_V6: - return 7 + reinterpret_cast(&_v.sa)->marshal(data+1); - case DNSNAME: + case TYPE_ZEROTIER: + data[7] = (uint8_t)(_v.zt.a >> 32U); + data[8] = (uint8_t)(_v.zt.a >> 24U); + data[9] = (uint8_t)(_v.zt.a >> 16U); + data[10] = (uint8_t)(_v.zt.a >> 8U); + data[11] = (uint8_t)_v.zt.a; + memcpy(data + 12,_v.zt.idh,ZT_IDENTITY_HASH_SIZE); + return ZT_IDENTITY_HASH_SIZE + 12; + case TYPE_DNSNAME: p = 7; for (;;) { if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0) @@ -82,15 +80,7 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept data[p++] = (uint8_t)(_v.dns.port >> 8U); data[p++] = (uint8_t)_v.dns.port; return p; - case ZEROTIER: - data[7] = (uint8_t)(_v.zt.a >> 32U); - data[8] = (uint8_t)(_v.zt.a >> 24U); - data[9] = (uint8_t)(_v.zt.a >> 16U); - data[10] = (uint8_t)(_v.zt.a >> 8U); - data[11] = (uint8_t)_v.zt.a; - memcpy(data + 12,_v.zt.idh,ZT_IDENTITY_HASH_SIZE); - return ZT_IDENTITY_HASH_SIZE + 12; - case URL: + case TYPE_URL: p = 7; for (;;) { if ((data[p] = (uint8_t)_v.url[p-1]) == 0) @@ -100,7 +90,7 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept return -1; } return p; - case ETHERNET: + case TYPE_ETHERNET: data[7] = (uint8_t)(_v.eth >> 40U); data[8] = (uint8_t)(_v.eth >> 32U); data[9] = (uint8_t)(_v.eth >> 24U); @@ -108,12 +98,14 @@ int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept data[11] = (uint8_t)(_v.eth >> 8U); data[12] = (uint8_t)_v.eth; return 13; - case WEBRTC: - Utils::storeBigEndian(data + 7,(uint16_t)_v.webrtc.offerLen); - memcpy(data + 9,_v.webrtc.offer,_v.webrtc.offerLen); - return 9 + _v.webrtc.offerLen; + case TYPE_INETADDR_V4: + case TYPE_INETADDR_V6: + p = asInetAddress(_v.sa).marshal(data + 7); + if (p < 0) + return p; + return 7 + p; default: - data[0] = (uint8_t)NIL; + data[0] = (uint8_t)TYPE_NIL; return 7; } } @@ -128,75 +120,71 @@ int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept _l[1] = Utils::loadBigEndian(data + 3); _l[2] = Utils::loadBigEndian(data + 5); switch(_t) { - case NIL: + case TYPE_NIL: return 7; - case INETADDR_V4: - case INETADDR_V6: - return 7 + reinterpret_cast(&_v.sa)->unmarshal(data+7,len-7); - case DNSNAME: - if (len < 10) + case TYPE_ZEROTIER: + if (len < (12 + ZT_IDENTITY_HASH_SIZE)) + return -1; + _v.zt.a = ((uint64_t)data[7]) << 32U; + _v.zt.a |= ((uint64_t)data[8]) << 24U; + _v.zt.a |= ((uint64_t)data[9]) << 16U; + _v.zt.a |= ((uint64_t)data[10]) << 8U; + _v.zt.a |= (uint64_t)data[11]; + memcpy(_v.zt.idh,data + 12,ZT_IDENTITY_HASH_SIZE); + return 60; + case TYPE_DNSNAME: + if (len < 10) + return -1; + p = 7; + for (;;) { + if ((_v.dns.name[p-1] = (char)data[p]) == 0) { + ++p; + break; + } + ++p; + if ((p >= (ZT_ENDPOINT_MARSHAL_SIZE_MAX-2))||(p >= (len-2))) + return -1; + } + _v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U); + _v.dns.port |= (uint16_t)data[p++]; + return p; + case TYPE_URL: + if (len < 8) + return -1; + p = 7; + for (;;) { + if ((_v.url[p-1] = (char)data[p]) == 0) { + ++p; + break; + } + ++p; + if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len)) + return -1; + } + return p; + case TYPE_ETHERNET: + if (len < 13) + return -1; + _v.eth = ((uint64_t)data[7]) << 40U; + _v.eth |= ((uint64_t)data[8]) << 32U; + _v.eth |= ((uint64_t)data[9]) << 24U; + _v.eth |= ((uint64_t)data[10]) << 16U; + _v.eth |= ((uint64_t)data[11]) << 8U; + _v.eth |= (uint64_t)data[12]; + return 13; + case TYPE_INETADDR_V4: + case TYPE_INETADDR_V6: + p = asInetAddress(_v.sa).unmarshal(data + 7,len - 7); + if (p <= 0) return -1; - p = 7; - for (;;) { - if ((_v.dns.name[p-1] = (char)data[p]) == 0) { - ++p; - break; - } - ++p; - if ((p >= (ZT_ENDPOINT_MARSHAL_SIZE_MAX-2))||(p >= (len-2))) - return -1; - } - _v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U); - _v.dns.port |= (uint16_t)data[p++]; - return p; - case ZEROTIER: - if (len < 60) - return -1; - _v.zt.a = ((uint64_t)data[7]) << 32U; - _v.zt.a |= ((uint64_t)data[8]) << 24U; - _v.zt.a |= ((uint64_t)data[9]) << 16U; - _v.zt.a |= ((uint64_t)data[10]) << 8U; - _v.zt.a |= (uint64_t)data[11]; - memcpy(_v.zt.idh,data + 12,48); - return 60; - case URL: - if (len < 8) - return -1; - p = 7; - for (;;) { - if ((_v.url[p-1] = (char)data[p]) == 0) { - ++p; - break; - } - ++p; - if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len)) - return -1; - } - return p; - case ETHERNET: - if (len < 13) - return -1; - _v.eth = ((uint64_t)data[7]) << 40U; - _v.eth |= ((uint64_t)data[8]) << 32U; - _v.eth |= ((uint64_t)data[9]) << 24U; - _v.eth |= ((uint64_t)data[10]) << 16U; - _v.eth |= ((uint64_t)data[11]) << 8U; - _v.eth |= (uint64_t)data[12]; - return 13; - case WEBRTC: - if (len < 9) - return -1; - _v.webrtc.offerLen = Utils::loadBigEndian(data + 7); - if ((len < (9 + _v.webrtc.offerLen))||(_v.webrtc.offerLen > ZT_ENDPOINT_MAX_NAME_SIZE)) - return -1; - memcpy(_v.webrtc.offer,data + 9,_v.webrtc.offerLen); - return 9 + _v.webrtc.offerLen; + return 7 + p; default: - // Unrecognized endpoint types not yet specified must start with a byte - // length size so that older versions of ZeroTier can skip them. - if (len < 8) + // Unrecognized endpoint types not yet specified must start with a 16-bit + // length so that older versions of ZeroTier can skip them. + if (len < 9) return -1; - return 8 + (int)data[7]; + p = 9 + (int)Utils::loadBigEndian(data + 7); + return (p > len) ? -1 : p; } } diff --git a/node/Endpoint.hpp b/node/Endpoint.hpp index 4cf7f12b9..a73a7a272 100644 --- a/node/Endpoint.hpp +++ b/node/Endpoint.hpp @@ -39,99 +39,96 @@ namespace ZeroTier { class Endpoint : public TriviallyCopyable { public: + /** + * Endpoint type + */ enum Type { - NIL = 0, // NIL value - INETADDR_V4 = 1, // IPv4 - INETADDR_V6 = 2, // IPv6 - DNSNAME = 3, // DNS name and port that resolves to InetAddress - ZEROTIER = 4, // ZeroTier Address (for relaying and meshy behavior) - URL = 5, // URL for http/https/ws/etc. (not implemented yet) - ETHERNET = 6, // 48-bit LAN-local Ethernet address - WEBRTC = 7, // WebRTC data channels - UNRECOGNIZED = 255 // Unrecognized endpoint type encountered in stream + TYPE_NIL = 0, // NIL value + TYPE_ZEROTIER = 1, // ZeroTier Address (for relaying and meshy behavior) + TYPE_DNSNAME = 2, // DNS name and port that resolves to InetAddress + TYPE_URL = 3, // URL for HTTP or Web Sockets transport + TYPE_INETADDR_V4 = 4, // IPv4 + TYPE_ETHERNET = 5, // 48-bit LAN-local Ethernet address + TYPE_INETADDR_V6 = 6 // IPv6 }; ZT_ALWAYS_INLINE Endpoint() noexcept { memoryZero(this); } - explicit ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa) { *this = sa; } + ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa) + { + switch (sa.ss_family) { + case AF_INET: + _t = TYPE_INETADDR_V4; + break; + case AF_INET6: + _t = TYPE_INETADDR_V6; + default: + _t = TYPE_NIL; + return; + } + asInetAddress(_v.sa) = sa; + } ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) : - _t(ZEROTIER) + _t(TYPE_ZEROTIER) { _v.zt.a = zt.toInt(); memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE); } ZT_ALWAYS_INLINE Endpoint(const char *name,const int port) : - _t(DNSNAME) + _t(TYPE_DNSNAME) { _v.dns.port = port; Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); } explicit ZT_ALWAYS_INLINE Endpoint(const char *url) : - _t(URL) + _t(TYPE_URL) { Utils::scopy(_v.url,sizeof(_v.url),url); } - ZT_ALWAYS_INLINE Endpoint &operator=(const InetAddress &sa) - { - switch(sa.ss_family) { - case AF_INET: - _t = INETADDR_V4; - break; - case AF_INET6: - _t = INETADDR_V6; - break; - default: - _t = NIL; - return *this; - } - _v.sa = sa; - return *this; - } - /** * @return InetAddress or NIL if not of this type */ - ZT_ALWAYS_INLINE const InetAddress &inetAddr() const noexcept { return ((_t == INETADDR_V4)||(_t == INETADDR_V6)) ? *reinterpret_cast(&_v.sa) : InetAddress::NIL; } + ZT_ALWAYS_INLINE const InetAddress &inetAddr() const noexcept { return ((_t == TYPE_INETADDR_V4)||(_t == TYPE_INETADDR_V6)) ? asInetAddress(_v.sa) : InetAddress::NIL; } /** * @return DNS name or empty string if not of this type */ - ZT_ALWAYS_INLINE const char *dnsName() const noexcept { return (_t == DNSNAME) ? _v.dns.name : ""; } + ZT_ALWAYS_INLINE const char *dnsName() const noexcept { return (_t == TYPE_DNSNAME) ? _v.dns.name : ""; } /** * @return Port associated with DNS name or -1 if not of this type */ - ZT_ALWAYS_INLINE int dnsPort() const noexcept { return (_t == DNSNAME) ? _v.dns.port : -1; } + ZT_ALWAYS_INLINE int dnsPort() const noexcept { return (_t == TYPE_DNSNAME) ? _v.dns.port : -1; } /** * @return ZeroTier address or NIL if not of this type */ - ZT_ALWAYS_INLINE Address ztAddress() const noexcept { return Address((_t == ZEROTIER) ? _v.zt.a : (uint64_t)0); } + ZT_ALWAYS_INLINE Address ztAddress() const noexcept { return Address((_t == TYPE_ZEROTIER) ? _v.zt.a : (uint64_t)0); } /** * @return 384-bit hash of identity keys or NULL if not of this type */ - ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const noexcept { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; } + ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const noexcept { return (_t == TYPE_ZEROTIER) ? _v.zt.idh : nullptr; } /** * @return URL or empty string if not of this type */ - ZT_ALWAYS_INLINE const char *url() const noexcept { return (_t == URL) ? _v.url : ""; } + ZT_ALWAYS_INLINE const char *url() const noexcept { return (_t == TYPE_URL) ? _v.url : ""; } /** * @return Ethernet address or NIL if not of this type */ - ZT_ALWAYS_INLINE MAC ethernet() const noexcept { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); } + ZT_ALWAYS_INLINE MAC ethernet() const noexcept { return (_t == TYPE_ETHERNET) ? MAC(_v.eth) : MAC(); } /** * @return Endpoint type or NIL if unset/empty */ ZT_ALWAYS_INLINE Type type() const noexcept { return _t; } - explicit ZT_ALWAYS_INLINE operator bool() const noexcept { return _t != NIL; } + explicit ZT_ALWAYS_INLINE operator bool() const noexcept { return _t != TYPE_NIL; } bool operator==(const Endpoint &ep) const; ZT_ALWAYS_INLINE bool operator!=(const Endpoint &ep) const { return (!(*this == ep)); } @@ -153,10 +150,6 @@ private: uint16_t port; char name[ZT_ENDPOINT_MAX_NAME_SIZE]; } dns; - struct { - uint16_t offerLen; - uint8_t offer[ZT_ENDPOINT_MAX_NAME_SIZE]; - } webrtc; struct { uint64_t a; uint8_t idh[ZT_IDENTITY_HASH_SIZE]; diff --git a/node/H.hpp b/node/H.hpp new file mode 100644 index 000000000..735666c68 --- /dev/null +++ b/node/H.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. + */ +/****/ + +#ifndef ZT_H_HPP +#define ZT_H_HPP + +#include "Constants.hpp" +#include "TriviallyCopyable.hpp" + +namespace ZeroTier { + +/** + * Container for cryptographic hashes + * + * The size of the hash used with this container must be a multiple of 64 bits. + * Currently it's used as H<384> and H<512>. + * + * Warning: the [] operator is not bounds checked. + * + * @tparam BITS Bits in hash, must be a multiple of 64 + */ +template +class H : public TriviallyCopyable +{ +public: + ZT_ALWAYS_INLINE H() noexcept {} + + /** + * @param h Hash value of size BITS / 8 + */ + explicit ZT_ALWAYS_INLINE H(const void *h) noexcept { memcpy(_h,h,BITS / 8); } + + /** + * @param h Hash value of size BITS / 8 + */ + ZT_ALWAYS_INLINE void set(const void *h) noexcept { memcpy(_h,h,BITS / 8); } + + ZT_ALWAYS_INLINE void zero() noexcept + { + for(int i=0;i<(BITS / sizeof(unsigned long));++i) + _h[i] = 0; + } + + ZT_ALWAYS_INLINE uint8_t *data() noexcept { return reinterpret_cast(_h); } + ZT_ALWAYS_INLINE const uint8_t *data() const noexcept { return reinterpret_cast(_h); } + + ZT_ALWAYS_INLINE uint8_t operator[](const unsigned int i) const noexcept { return reinterpret_cast(_h)[i]; } + ZT_ALWAYS_INLINE uint8_t &operator[](const unsigned int i) noexcept { return reinterpret_cast(_h)[i]; } + + static constexpr unsigned int size() noexcept { return BITS / 8; } + + ZT_ALWAYS_INLINE unsigned long hashCode() const noexcept { return _h[0]; } + + ZT_ALWAYS_INLINE operator bool() const noexcept + { + for(int i=0;i<(BITS / sizeof(unsigned long));++i) { + if (_h[i] != 0) + return true; + } + return false; + } + + ZT_ALWAYS_INLINE bool operator==(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) == 0; } + ZT_ALWAYS_INLINE bool operator!=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) != 0; } + ZT_ALWAYS_INLINE bool operator<(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) < 0; } + ZT_ALWAYS_INLINE bool operator>(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) > 0; } + ZT_ALWAYS_INLINE bool operator<=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) <= 0; } + ZT_ALWAYS_INLINE bool operator>=(const H &h) const noexcept { return memcmp(_h,h._h,BITS / 8) >= 0; } + +private: + unsigned long _h[BITS / sizeof(unsigned long)]; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Identity.cpp b/node/Identity.cpp index a03d10232..ea709034e 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -93,7 +93,6 @@ bool Identity::generate(const Type t) _type = t; _hasPrivate = true; - _hash[0] = 0; // force hash recompute switch(t) { case C25519: { @@ -103,32 +102,28 @@ bool Identity::generate(const Type t) _address.setTo(digest + 59); // last 5 bytes are address } while (_address.isReserved()); delete[] genmem; + _computeHash(); } break; case P384: { + AES c; do { C25519::generate(_pub.c25519,_priv.c25519); ECC384GenerateKey(_pub.p384,_priv.p384); - // This is just an intentionally complex hash function for use with a simple hashcash - // design to slow down identity generation as a defense in depth against brute force - // collision searches. V0 used a somewhat more overkill memory intensive design that's - // not really necessary and makes verifications too slow, so V1 uses this instead. - SHA384(digest,&_pub,sizeof(_pub)); - AES c(digest); - SHA384(digest,digest,48); - std::sort(digest,digest + 48); + c.init(digest); c.encrypt(digest,digest); c.encrypt(digest + 16,digest + 16); c.encrypt(digest + 32,digest + 32); - SHA384(digest,digest,48); if (digest[47] != 0) continue; _address.setTo(digest); } while (_address.isReserved()); + + _hash.set(digest); // P384 uses the same hash for hash() and address generation } break; default: @@ -143,6 +138,7 @@ bool Identity::locallyValidate() const if ((_address.isReserved())||(!_address)) return false; switch (_type) { + case C25519: try { uint8_t digest[64]; @@ -153,51 +149,30 @@ bool Identity::locallyValidate() const } catch ( ... ) {} return false; - case P384: { - const uint8_t *hash = this->hash(); - return ((hash[47] == 0)&&(Address(hash) == _address)); - } - - default: - return false; - } -} - -const uint8_t *Identity::hash() const -{ - uint8_t *const hash = const_cast(reinterpret_cast(_hash)); - switch(_type) { - default: - memset(hash,0,48); - break; - - case C25519: - if (_hash[0] == 0) - SHA384(hash,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - break; - case P384: - if (_hash[0] == 0) { - SHA384(hash,&_pub,sizeof(_pub)); - AES c(hash); - std::sort(hash,hash + 48); - c.encrypt(hash,hash); - c.encrypt(hash + 16,hash + 16); - c.encrypt(hash + 32,hash + 32); - SHA384(hash,hash,48); - } - break; + return ((_hash[47] == 0)&&(Address(_hash.data()) == _address)); + } - return hash; + return false; } void Identity::hashWithPrivate(uint8_t h[48]) const { - switch(_type) { - case C25519: SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); break; - case P384: SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv)); break; - default: memset(h,0,48); + if (_hasPrivate) { + switch (_type) { + + case C25519: + SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); + break; + + case P384: + SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv)); + break; + + } + return; } + memset(h,0,48); } unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const @@ -213,9 +188,11 @@ unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned case P384: if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { - // When signing with P-384 we also include the C25519 public key in the hash. + // For P384 we sign SHA384(data | public keys) for added defense against any attack + // that attempted to decouple the two keys in some way. Otherwise this has no impact + // on the security of the signature (unless SHA384 had some serious flaw). uint8_t h[48]; - SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + SHA384(h,data,len,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); return ZT_ECC384_SIGNATURE_SIZE; } @@ -235,7 +212,7 @@ bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned case P384: if (siglen == ZT_ECC384_SIGNATURE_SIZE) { uint8_t h[48]; - SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + SHA384(h,data,len,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig); } break; @@ -263,6 +240,11 @@ bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) } else if (_type == P384) { if (id._type == P384) { + // For another P384 identity we execute DH agreement with BOTH keys and then + // hash the results together. For those (cough FIPS cough) who only consider + // P384 to be kosher, the C25519 secret can be considered a "salt" + // or something. For those who don't trust P384 this means the privacy of + // your traffic is also protected by C25519. C25519::agree(_priv.c25519,id._pub.c25519,rawkey); ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN); SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); @@ -283,13 +265,14 @@ bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const { + char *p = buf; + _address.toString(p); + p += 10; + *(p++) = ':'; + switch(_type) { case C25519: { - char *p = buf; - Utils::hex10(_address.toInt(),p); - p += 10; - *(p++) = ':'; *(p++) = '0'; *(p++) = ':'; Utils::hex(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,p); @@ -304,10 +287,6 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_ } case P384: { - char *p = buf; - Utils::hex10(_address.toInt(),p); - p += 10; - *(p++) = ':'; *(p++) = '1'; *(p++) = ':'; int el = Utils::b32e((const uint8_t *)(&_pub),sizeof(_pub),p,(int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); @@ -324,13 +303,14 @@ char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_ } } + return nullptr; } bool Identity::fromString(const char *str) { + _hash.zero(); _hasPrivate = false; - _hash[0] = 0; // force hash recompute if (!str) { _address.zero(); @@ -421,6 +401,8 @@ bool Identity::fromString(const char *str) return false; } + _computeHash(); + return true; } @@ -430,50 +412,67 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool incl switch(_type) { case C25519: data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519; + memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + if ((includePrivate)&&(_hasPrivate)) { data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN; memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); - return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN); + return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN; + } else { + data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0; + return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1; } - data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0; - return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1; case P384: data[ZT_ADDRESS_LENGTH] = (uint8_t)P384; - memcpy(data + 1 + ZT_ADDRESS_LENGTH,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); + + memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); + if ((includePrivate)&&(_hasPrivate)) { data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; + } else { + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; + return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; } - data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; - return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; + } return -1; } int Identity::unmarshal(const uint8_t *data,const int len) noexcept { + _hash.zero(); + _hasPrivate = false; + if (len < (ZT_ADDRESS_LENGTH + 1)) return -1; - _hash[0] = 0; // force hash recompute + unsigned int privlen; switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) { case C25519: if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1)) return -1; + memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN]; if (privlen == ZT_C25519_PRIVATE_KEY_LEN) { if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)) return -1; + _hasPrivate = true; memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN); + + _computeHash(); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN; } else if (privlen == 0) { _hasPrivate = false; + + _computeHash(); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1; } break; @@ -481,26 +480,61 @@ int Identity::unmarshal(const uint8_t *data,const int len) noexcept case P384: if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1)) return -1; + memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) { if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)) return -1; + _hasPrivate = true; memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); + _computeHash(); if (!this->locallyValidate()) // for P384 we do this always return -1; + return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; } else if (privlen == 0) { _hasPrivate = false; + + _computeHash(); + if (!this->locallyValidate()) // for P384 we do this always + return -1; + return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1; } break; } + return -1; } +void Identity::_computeHash() +{ + switch(_type) { + default: + _hash.zero(); + break; + + case C25519: + SHA384(_hash.data(),_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + break; + + case P384: + if (!_hash) { + uint8_t *const h = _hash.data(); + SHA384(h,&_pub,sizeof(_pub)); + AES c(h); + c.encrypt(h,h); + c.encrypt(h + 16,h + 16); + c.encrypt(h + 32,h + 32); + } + break; + } +} + } // namespace ZeroTier extern "C" { diff --git a/node/Identity.hpp b/node/Identity.hpp index 09e6348a6..7bf95f505 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -24,6 +24,7 @@ #include "SHA512.hpp" #include "ECC384.hpp" #include "TriviallyCopyable.hpp" +#include "H.hpp" #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 #define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE) @@ -96,7 +97,11 @@ public: bool generate(Type t); /** - * Check the validity of this identity's pairing of key to address + * Check the validity of this identity's address + * + * For type 0 identities this is slightly time consuming. For type 1 identities it's + * instantaneous. It should be done when a new identity is accepted for the very first + * time. * * @return True if validation check passes */ @@ -108,7 +113,7 @@ public: ZT_ALWAYS_INLINE bool hasPrivate() const noexcept { return _hasPrivate; } /** - * This gets (computing if needed) a hash of this identity's public key(s). + * Get hash of this identity's public key(s) * * The hash returned by this function differs by identity type. For C25519 (type 0) * identities this returns a simple SHA384 of the public key, which is NOT the same @@ -117,12 +122,18 @@ public: * and address computation. This difference is because the v0 hash is expensive while * the v1 hash is fast. * - * @return 384-bit/48-byte hash (pointer remains valid as long as Identity object exists) + * While addresses can technically collide (though this is rare and hard to create), + * the full hash of an identity's keys is unique to within cryptographic strength + * bounds of the keys themselves. + * + * @return 384-bit/48-byte hash */ - const uint8_t *hash() const; + ZT_ALWAYS_INLINE const H<384> &hash() const noexcept { return _hash; } /** - * Compute a hash of this identity's public and private keys + * Compute a hash of this identity's public and private keys. + * + * If there is no private key or the identity is NIL the buffer is filled with zero. * * @param h Buffer to store SHA384 hash */ @@ -234,10 +245,10 @@ public: int unmarshal(const uint8_t *data,int len) noexcept; private: + void _computeHash(); + Address _address; - uint64_t _hash[6]; // hash of public key memo-ized for performance, recalculated when _hash[0] == 0 - Type _type; // _type determines which fields in _priv and _pub are used - bool _hasPrivate; + H<384> _hash; ZT_PACKED_STRUCT(struct { // don't re-order these uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN]; uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE]; @@ -246,6 +257,8 @@ private: uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; // NIST P-384 public key }) _pub; + Type _type; // _type determines which fields in _priv and _pub are used + bool _hasPrivate; }; } // namespace ZeroTier diff --git a/node/Membership.cpp b/node/Membership.cpp index 1aeb6524f..20b894383 100644 --- a/node/Membership.cpp +++ b/node/Membership.cpp @@ -60,10 +60,10 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i sendCom = false; outp->wO(outl,nconf.com); } - outp->wI(outl,(uint8_t)0); + outp->wI8(outl,0); if ((outl + ZT_CAPABILITY_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) { - void *const capCountAt = outp->b + outl; + void *const capCountAt = outp->unsafeData + outl; outl += 2; unsigned int capCount = 0; while (capPtr < nconf.capabilityCount) { @@ -75,7 +75,7 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i Utils::storeBigEndian(capCountAt,(uint16_t)capCount); if ((outl + ZT_TAG_MARSHAL_SIZE_MAX + 4) < ZT_PROTO_MAX_PACKET_LENGTH) { - void *const tagCountAt = outp->b + outl; + void *const tagCountAt = outp->unsafeData + outl; outl += 2; unsigned int tagCount = 0; while (tagPtr < nconf.tagCount) { @@ -86,10 +86,10 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i } Utils::storeBigEndian(tagCountAt,(uint16_t)tagCount); - outp->wI(outl,(uint16_t)0); // no revocations sent here as these propagate differently + outp->wI16(outl,0); // no revocations sent here as these propagate differently if ((outl + ZT_CERTIFICATEOFOWNERSHIP_MARSHAL_SIZE_MAX + 2) < ZT_PROTO_MAX_PACKET_LENGTH) { - void *const cooCountAt = outp->b + outl; + void *const cooCountAt = outp->unsafeData + outl; outl += 2; unsigned int cooCount = 0; while (cooPtr < nconf.certificateOfOwnershipCount) { @@ -102,15 +102,14 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const i complete = true; } else { - outp->wI(outl,(uint16_t)0); + outp->wI16(outl,0); } } else { - outp->wI(outl,(uint16_t)0); - outp->wI(outl,(uint16_t)0); - outp->wI(outl,(uint16_t)0); + outp->wI32(outl,0); + outp->wI16(outl,0); // three zero 16-bit integers } } else { - outp->wI(outl,(uint64_t)0); // four zero 16-bit integers + outp->wI64(outl,0); // four zero 16-bit integers } if (outl > sizeof(Protocol::Header)) { diff --git a/node/Network.hpp b/node/Network.hpp index df05e9d69..758368ce4 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -14,13 +14,6 @@ #ifndef ZT_NETWORK_HPP #define ZT_NETWORK_HPP -#include -#include -#include -#include -#include -#include - #include "Constants.hpp" #include "Hashtable.hpp" #include "Address.hpp" @@ -34,6 +27,12 @@ #include "NetworkConfig.hpp" #include "CertificateOfMembership.hpp" +#include +#include +#include +#include +#include + #define ZT_NETWORK_MAX_INCOMING_UPDATES 3 namespace ZeroTier { diff --git a/node/Peer.cpp b/node/Peer.cpp index 569fd1490..9caa9a13a 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -122,7 +122,7 @@ void Peer::received( RR->t->learnedNewPath(tPtr,0x582fabdd,packetId,_id,path->address(),old); } else { if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id,path->localSocket(),path->address())) { - RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id.address(),_id.hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH); + RR->t->tryingNewPath(tPtr,0xb7747ddd,_id,path->address(),path->address(),packetId,(uint8_t)verb,_id.address(),_id.hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH); sendHELLO(tPtr,path->localSocket(),path->address(),now); path->sent(now); } @@ -241,7 +241,7 @@ void Peer::sendNOP(void *tPtr,const int64_t localSocket,const InetAddress &atAdd ph.flags = 0; ph.verb = Protocol::VERB_NOP; Protocol::armor(outp,sizeof(Protocol::Header),_key,this->cipher()); - RR->node->putPacket(tPtr,localSocket,atAddress,outp.b,sizeof(Protocol::Header)); + RR->node->putPacket(tPtr,localSocket,atAddress,outp.unsafeData,sizeof(Protocol::Header)); } void Peer::ping(void *tPtr,int64_t now,const bool pingAllAddressTypes) @@ -337,24 +337,26 @@ void Peer::getAllPaths(std::vector< SharedPtr > &paths) void Peer::save(void *tPtr) const { - uint8_t *const buf = (uint8_t *)malloc(ZT_PEER_MARSHAL_SIZE_MAX); + uint8_t *const buf = (uint8_t *)malloc(8 + ZT_PEER_MARSHAL_SIZE_MAX); if (!buf) return; + Utils::storeBigEndian(buf,(uint64_t)RR->node->now()); + _lock.rlock(); - const int len = marshal(buf); + const int len = marshal(buf + 8); _lock.runlock(); if (len > 0) { uint64_t id[2]; id[0] = _id.address().toInt(); id[1] = 0; - RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,id,buf,(unsigned int)len); + RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,id,buf,(unsigned int)len + 8); } free(buf); } -void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool behindSymmetric,const bool bfg1024) +void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool bfg1024) { static uint8_t junk = 0; @@ -376,7 +378,7 @@ void Peer::contact(void *tPtr,const Endpoint &ep,const int64_t now,const bool be // If the peer indicates that they may be behind a symmetric NAT and there are no // living direct paths, try a few more aggressive things. - if ((behindSymmetric) && (phyAddr.ss_family == AF_INET) && (!direct(now))) { + if ((phyAddr.ss_family == AF_INET) && (!direct(now))) { unsigned int port = phyAddr.port(); if ((bfg1024)&&(port < 1024)&&(RR->node->natMustDie())) { // If the other side is using a low-numbered port and has elected to diff --git a/node/Peer.hpp b/node/Peer.hpp index 9692b23a1..481c91a84 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -170,7 +170,11 @@ public: /** * @return Bootstrap address or NULL if none */ - ZT_ALWAYS_INLINE const Endpoint &bootstrap() const noexcept { return _bootstrap; } + ZT_ALWAYS_INLINE const Endpoint &bootstrap() const noexcept + { + RWMutex::RLock l(_lock); + return _bootstrap; + } /** * Set bootstrap endpoint @@ -179,9 +183,8 @@ public: */ ZT_ALWAYS_INLINE void setBootstrap(const Endpoint &ep) noexcept { - _lock.lock(); + RWMutex::Lock l(_lock); _bootstrap = ep; - _lock.unlock(); } /** @@ -315,17 +318,14 @@ public: void save(void *tPtr) const; /** - * Attempt to contact this peer at a physical address - * - * This checks rate limits, path usability, sometimes deploys advanced NAT-t techniques, etc. + * Attempt to contact this peer at a physical address, subject to internal checks * * @param tPtr External user pointer we pass around * @param ep Endpoint to attempt to contact * @param now Current time - * @param behindSymmetric This peer may be behind a symmetric NAT (only meaningful for IPv4) * @param bfg1024 Use BFG1024 brute force symmetric NAT busting algorithm if applicable */ - void contact(void *tPtr,const Endpoint &ep,int64_t now,bool behindSymmetric,bool bfg1024); + void contact(void *tPtr,const Endpoint &ep,int64_t now,bool bfg1024); /** * Called by Node when an alarm set by this peer goes off @@ -348,20 +348,24 @@ private: const RuntimeEnvironment *RR; - volatile int64_t _lastReceive; - volatile int64_t _lastWhoisRequestReceived; - volatile int64_t _lastEchoRequestReceived; - volatile int64_t _lastPushDirectPathsReceived; - volatile int64_t _lastProbeReceived; - volatile int64_t _lastAttemptedP2PInit; - volatile int64_t _lastTriedStaticPath; - volatile int64_t _lastPrioritizedPaths; - volatile int64_t _lastAttemptedAggressiveNATTraversal; - volatile unsigned int _latency; + // The last time various things happened, for rate limiting and periodic events. + std::atomic _lastReceive; + std::atomic _lastWhoisRequestReceived; + std::atomic _lastEchoRequestReceived; + std::atomic _lastPushDirectPathsReceived; + std::atomic _lastProbeReceived; + std::atomic _lastAttemptedP2PInit; + std::atomic _lastTriedStaticPath; + std::atomic _lastPrioritizedPaths; + std::atomic _lastAttemptedAggressiveNATTraversal; + // Latency in milliseconds + std::atomic _latency; + + // For SharedPtr<> std::atomic __refCount; - // Lock for non-volatile read/write fields + // Read/write mutex for non-atomic non-const fields. RWMutex _lock; // Number of paths current alive as of last _prioritizePaths diff --git a/node/Protocol.cpp b/node/Protocol.cpp index ca1b8391a..271d1523a 100644 --- a/node/Protocol.cpp +++ b/node/Protocol.cpp @@ -65,8 +65,8 @@ volatile uintptr_t _checkSizesIMeanIt = _checkSizes(); uint64_t createProbe(const Identity &sender,const Identity &recipient,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) noexcept { uint8_t tmp[ZT_IDENTITY_HASH_SIZE + ZT_IDENTITY_HASH_SIZE]; - memcpy(tmp,sender.hash(),ZT_IDENTITY_HASH_SIZE); - memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.hash(),ZT_IDENTITY_HASH_SIZE); + memcpy(tmp,sender.hash().data(),ZT_IDENTITY_HASH_SIZE); + memcpy(tmp + ZT_IDENTITY_HASH_SIZE,recipient.hash().data(),ZT_IDENTITY_HASH_SIZE); uint64_t hash[6]; SHA384(hash,tmp,sizeof(tmp),key,ZT_PEER_SECRET_KEY_LENGTH); return hash[0]; @@ -98,7 +98,7 @@ void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH], // only difference here is that we don't encrypt the payload uint64_t mac[2]; - poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); + poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); ph.mac = mac[0]; } break; @@ -111,10 +111,10 @@ void armor(Buf &pkt,int packetSize,const uint8_t key[ZT_PEER_SECRET_KEY_LENGTH], s20.crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN); const unsigned int encLen = packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START; - s20.crypt12(pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen); + s20.crypt12(pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen); uint64_t mac[2]; - poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen,macKey); + poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,encLen,macKey); ph.mac = mac[0]; } break; @@ -132,9 +132,9 @@ int compress(SharedPtr &pkt,int packetSize) noexcept if (!pkt2) return packetSize; const int uncompressedLen = packetSize - ZT_PROTO_PACKET_PAYLOAD_START; - const int compressedLen = LZ4_compress_fast(reinterpret_cast(pkt->b + ZT_PROTO_PACKET_PAYLOAD_START),reinterpret_cast(pkt2->b + ZT_PROTO_PACKET_PAYLOAD_START),uncompressedLen,ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START); + const int compressedLen = LZ4_compress_fast(reinterpret_cast(pkt->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),reinterpret_cast(pkt2->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),uncompressedLen,ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START); if ((compressedLen > 0)&&(compressedLen < uncompressedLen)) { - memcpy(pkt2->b,pkt->b,ZT_PROTO_PACKET_PAYLOAD_START); + memcpy(pkt2->unsafeData,pkt->unsafeData,ZT_PROTO_PACKET_PAYLOAD_START); pkt.swap(pkt2); pkt->as().verb |= ZT_PROTO_VERB_FLAG_COMPRESSED; return compressedLen + ZT_PROTO_PACKET_PAYLOAD_START; diff --git a/node/Protocol.hpp b/node/Protocol.hpp index 54abb24f4..45bd21e7a 100644 --- a/node/Protocol.hpp +++ b/node/Protocol.hpp @@ -640,13 +640,17 @@ enum Verb * <[...] paths> * * Path record format: - * <[1] 8-bit path flags (always 0, currently unused)> + * <[1] 8-bit path flags> * <[2] length of extended path characteristics or 0 for none> * <[...] extended path characteristics> * <[1] address type> - * <[1] address length in bytes> + * <[1] address record length in bytes> * <[...] address> * + * Path flags: + * 0x01 - Sender is likely behind a symmetric NAT + * 0x02 - Use BFG1024 algorithm for symmetric NAT-t if conditions met + * * The receiver may, upon receiving a push, attempt to establish a * direct link to one or more of the indicated addresses. It is the * responsibility of the sender to limit which peers it pushes direct @@ -965,21 +969,21 @@ ZT_PACKED_STRUCT(struct UNSUPPORTED_OPERATION__NETWORK_CONFIG_REQUEST * @param packetSize Packet's actual size in bytes * @return Packet ID or 0 if packet size is less than 8 */ -ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian(pkt.b) : 0ULL; } +ZT_ALWAYS_INLINE uint64_t packetId(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= 8) ? Utils::loadBigEndian(pkt.unsafeData) : 0ULL; } /** * @param Packet to extract hops from * @param packetSize Packet's actual size in bytes * @return 3-bit hops field embedded in packet flags field */ -ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; } +ZT_ALWAYS_INLINE uint8_t packetHops(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? (pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK) : 0; } /** * @param Packet to extract cipher ID from * @param packetSize Packet's actual size in bytes * @return 3-bit cipher field embedded in packet flags field */ -ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.b[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; } +ZT_ALWAYS_INLINE uint8_t packetCipher(const Buf &pkt,const unsigned int packetSize) noexcept { return (packetSize >= ZT_PROTO_PACKET_FLAGS_INDEX) ? ((pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] >> 3U) & 0x07U) : 0; } /** * @return 3-bit hops field embedded in packet flags field @@ -1009,14 +1013,14 @@ ZT_ALWAYS_INLINE void salsa2012DeriveKey(const uint8_t *const in,uint8_t *const for(int i=0;i<18;++i) out[i] = in[i] ^ packet.b[i]; #else - *reinterpret_cast(out) = *reinterpret_cast(in) ^ *reinterpret_cast(packet.b); - *reinterpret_cast(out + 8) = *reinterpret_cast(in + 8) ^ *reinterpret_cast(packet.b + 8); - *reinterpret_cast(out + 16) = *reinterpret_cast(in + 16) ^ *reinterpret_cast(packet.b + 16); + *reinterpret_cast(out) = *reinterpret_cast(in) ^ *reinterpret_cast(packet.unsafeData); + *reinterpret_cast(out + 8) = *reinterpret_cast(in + 8) ^ *reinterpret_cast(packet.unsafeData + 8); + *reinterpret_cast(out + 16) = *reinterpret_cast(in + 16) ^ *reinterpret_cast(packet.unsafeData + 16); #endif // Flags, but with hop count masked off. Hop count is altered by forwarding // nodes and is the only field that is mutable by unauthenticated third parties. - out[18] = in[18] ^ (packet.b[18] & 0xf8U); + out[18] = in[18] ^ (packet.unsafeData[18] & 0xf8U); // Raw packet size in bytes -- thus each packet size defines a new key space. out[19] = in[19] ^ (uint8_t)packetSize; diff --git a/node/Topology.cpp b/node/Topology.cpp index 8941f64de..7dd3989d6 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -38,8 +38,10 @@ Topology::Topology(const RuntimeEnvironment *renv,const Identity &myId,void *tPt RR(renv), _myIdentity(myId), _numConfiguredPhysicalPaths(0), - _peers(128), - _paths(256) + _peers(256), + _peersByIncomingProbe(256), + _peersByIdentityHash(256), + _paths(1024) { uint64_t idtmp[2]; idtmp[0] = 0; idtmp[1] = 0; std::vector data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_ROOTS,idtmp)); @@ -65,6 +67,9 @@ Topology::Topology(const RuntimeEnvironment *renv,const Identity &myId,void *tPt p->init(*r); } _rootPeers.push_back(p); + _peers[p->address()] = p; + _peersByIncomingProbe[p->incomingProbe()] = p; + _peersByIdentityHash[p->identity().hash()] = p; } } @@ -83,11 +88,13 @@ SharedPtr Topology::add(void *tPtr,const SharedPtr &peer) _loadCached(tPtr,peer->address(),hp); if (hp) { _peersByIncomingProbe[peer->incomingProbe()] = hp; + _peersByIdentityHash[peer->identity().hash()] = hp; return hp; } hp = peer; _peersByIncomingProbe[peer->incomingProbe()] = peer; + _peersByIdentityHash[peer->identity().hash()] = peer; return peer; } @@ -150,6 +157,8 @@ void Topology::addRoot(void *tPtr,const Identity &id,const InetAddress &bootstra p->init(id); if (bootstrap) p->setBootstrap(Endpoint(bootstrap)); + _peersByIncomingProbe[p->incomingProbe()] = p; + _peersByIdentityHash[p->identity().hash()] = p; } _rootPeers.push_back(p); @@ -204,6 +213,7 @@ void Topology::doPeriodicTasks(void *tPtr,const int64_t now) if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) { (*p)->save(tPtr); _peersByIncomingProbe.erase((*p)->incomingProbe()); + _peersByIdentityHash.erase((*p)->identity().hash()); _peers.erase(*a); } } @@ -226,11 +236,8 @@ void Topology::saveAll(void *tPtr) Hashtable< Address,SharedPtr >::Iterator i(_peers); Address *a = nullptr; SharedPtr *p = nullptr; - while (i.next(a,p)) { - if ( (!(*p)->alive(RR->node->now())) && (_roots.count((*p)->identity()) == 0) ) { - (*p)->save((void *)0); - } - } + while (i.next(a,p)) + (*p)->save(tPtr); } void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr &peer) @@ -240,19 +247,21 @@ void Topology::_loadCached(void *tPtr,const Address &zta,SharedPtr &peer) id[0] = zta.toInt(); id[1] = 0; std::vector data(RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,id)); - if (!data.empty()) { + if (data.size() > 8) { const uint8_t *d = data.data(); int dl = (int)data.size(); - for (;;) { - Peer *const p = new Peer(RR); - int n = p->unmarshal(d,dl); - if (n > 0) { - // TODO: will eventually handle multiple peers - peer.set(p); - return; - } else { - delete p; - } + + const int64_t ts = (int64_t)Utils::loadBigEndian(d); + Peer *const p = new Peer(RR); + int n = p->unmarshal(d + 8,dl - 8); + if (n < 0) { + delete p; + return; + } + if ((RR->node->now() - ts) < ZT_PEER_GLOBAL_TIMEOUT) { + // TODO: handle many peers, same address (?) + peer.set(p); + return; } } } catch ( ... ) { diff --git a/node/Topology.hpp b/node/Topology.hpp index f4b7f8d3e..14d94142c 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -30,6 +30,7 @@ #include "Hashtable.hpp" #include "SharedPtr.hpp" #include "ScopedPtr.hpp" +#include "H.hpp" namespace ZeroTier { @@ -66,24 +67,40 @@ public: ZT_ALWAYS_INLINE SharedPtr peer(void *tPtr,const Address &zta,const bool loadFromCached = true) { { - RWMutex::RLock _l(_peers_l); + RWMutex::RLock l(_peers_l); const SharedPtr *const ap = _peers.get(zta); if (ap) return *ap; } - - SharedPtr p; - if (loadFromCached) { - _loadCached(tPtr,zta,p); - if (p) { - RWMutex::Lock _l(_peers_l); - SharedPtr &hp = _peers[zta]; - if (!hp) + { + SharedPtr p; + if (loadFromCached) { + _loadCached(tPtr,zta,p); + if (p) { + RWMutex::Lock l(_peers_l); + SharedPtr &hp = _peers[zta]; + if (hp) + return hp; hp = p; + } } + return p; } + } - return p; + /** + * Get a peer by its 384-bit identity public key hash + * + * @param hash Identity hash + * @return Peer or NULL if no peer is currently in memory for this hash (cache is not checked in this case) + */ + ZT_ALWAYS_INLINE SharedPtr peerByHash(const H<384> &hash) + { + RWMutex::RLock _l(_peers_l); + const SharedPtr *const ap = _peersByIdentityHash.get(hash); + if (ap) + return *ap; + return SharedPtr(); } /** @@ -111,29 +128,21 @@ public: ZT_ALWAYS_INLINE SharedPtr path(const int64_t l,const InetAddress &r) { const uint64_t k = _pathHash(l,r); - - _paths_l.rlock(); - SharedPtr p(_paths[k]); - _paths_l.runlock(); - if (p) - return p; - - _paths_l.lock(); - SharedPtr &p2 = _paths[k]; - if (p2) { - p = p2; - } else { - try { - p.set(new Path(l,r)); - } catch ( ... ) { - _paths_l.unlock(); - return SharedPtr(); - } - p2 = p; + { + RWMutex::RLock lck(_paths_l); + SharedPtr *const p = _paths.get(k); + if (p) + return *p; + } + { + SharedPtr p(new Path(l,r)); + RWMutex::Lock lck(_paths_l); + SharedPtr &p2 = _paths[k]; + if (p2) + return p2; + p2 = p; + return p; } - _paths_l.unlock(); - - return p; } /** @@ -173,9 +182,8 @@ public: Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); Address *a = nullptr; SharedPtr *p = nullptr; - while (i.next(a,p)) { + while (i.next(a,p)) f(*((const SharedPtr *)p)); - } } /** @@ -202,9 +210,8 @@ public: Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); Address *a = nullptr; SharedPtr *p = nullptr; - while (i.next(a,p)) { + while (i.next(a,p)) f(*((const SharedPtr *)p),std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)p->ptr())); - } } catch ( ... ) {} // should not throw } @@ -221,9 +228,8 @@ public: Hashtable< uint64_t,SharedPtr >::Iterator i(const_cast(this)->_paths); uint64_t *k = nullptr; SharedPtr *p = nullptr; - while (i.next(k,p)) { + while (i.next(k,p)) f(*((const SharedPtr *)p)); - } } /** @@ -359,6 +365,7 @@ private: Hashtable< Address,SharedPtr > _peers; Hashtable< uint64_t,SharedPtr > _peersByIncomingProbe; + Hashtable< H<384>,SharedPtr > _peersByIdentityHash; Hashtable< uint64_t,SharedPtr > _paths; std::set< Identity > _roots; // locked by _peers_l std::vector< SharedPtr > _rootPeers; // locked by _peers_l diff --git a/node/Trace.cpp b/node/Trace.cpp index 9e0905f18..bdd0a6c01 100644 --- a/node/Trace.cpp +++ b/node/Trace.cpp @@ -118,15 +118,15 @@ void Trace::_tryingNewPath( ev.evType = ZT_CONST_TO_BE_UINT16(ZT_TRACE_VL1_TRYING_NEW_PATH); ev.codeLocation = Utils::hton(codeLocation); ev.address = Utils::hton(trying.address().toInt()); - memcpy(ev.identityHash,trying.hash(),48); + memcpy(ev.identityHash,trying.hash().data(),48); physicalAddress.forTrace(ev.physicalAddress); triggerAddress.forTrace(ev.triggerAddress); ev.triggeringPacketId = triggeringPacketId; ev.triggeringPacketVerb = triggeringPacketVerb; ev.triggeredByAddress = Utils::hton(triggeredByAddress); if (triggeredByIdentityHash) - memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,48); - else memset(ev.triggeredByIdentityHash,0,48); + memcpy(ev.triggeredByIdentityHash,triggeredByIdentityHash,ZT_IDENTITY_HASH_SIZE); + else memset(ev.triggeredByIdentityHash,0,ZT_IDENTITY_HASH_SIZE); ev.reason = (uint8_t)reason; RR->node->postEvent(tPtr,ZT_EVENT_TRACE,&ev); } @@ -145,7 +145,7 @@ void Trace::_learnedNewPath( ev.codeLocation = Utils::hton(codeLocation); ev.packetId = packetId; // packet IDs are kept in big-endian ev.address = Utils::hton(peerIdentity.address().toInt()); - memcpy(ev.identityHash,peerIdentity.hash(),48); + memcpy(ev.identityHash,peerIdentity.hash().data(),ZT_IDENTITY_HASH_SIZE); physicalAddress.forTrace(ev.physicalAddress); replaced.forTrace(ev.replaced); @@ -171,10 +171,10 @@ void Trace::_incomingPacketDropped( ev.networkId = Utils::hton(networkId); if (peerIdentity) { ev.address = Utils::hton(peerIdentity.address().toInt()); - memcpy(ev.identityHash,peerIdentity.hash(),48); + memcpy(ev.identityHash,peerIdentity.hash().data(),ZT_IDENTITY_HASH_SIZE); } else { ev.address = 0; - memset(ev.identityHash,0,48); + memset(ev.identityHash,0,ZT_IDENTITY_HASH_SIZE); } physicalAddress.forTrace(ev.physicalAddress); ev.hops = hops; diff --git a/node/VL1.cpp b/node/VL1.cpp index f88f68404..bafb94ff7 100644 --- a/node/VL1.cpp +++ b/node/VL1.cpp @@ -68,7 +68,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd // but we might as well avoid it. When the peer receives NOP on a path that hasn't been handshaked yet // it will send its own HELLO to which we will respond with a fully encrypted OK(HELLO). if (len == ZT_PROTO_PROBE_LENGTH) { - const SharedPtr peer(RR->topology->peerByProbe(Utils::loadAsIsEndian(data->b))); + const SharedPtr peer(RR->topology->peerByProbe(data->lI64(0))); if ((peer)&&(peer->rateGateInboundProbe(now))) { peer->sendNOP(tPtr,path->localSocket(),path->address(),now); path->sent(now); @@ -90,7 +90,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd // Destination address of packet (filled below) Address destination; - if (data->b[ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX] == ZT_PROTO_PACKET_FRAGMENT_INDICATOR) { + if (data->lI8(ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX) == ZT_PROTO_PACKET_FRAGMENT_INDICATOR) { // Fragment ----------------------------------------------------------------------------------------------------- const Protocol::FragmentHeader &fragmentHeader = data->as(); @@ -234,7 +234,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd // Verify packet MAC. uint64_t mac[2]; - poly1305(mac,pkt.b->b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); + poly1305(mac,pkt.b->unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); if (ph->mac != mac[0]) { RR->t->incomingPacketDropped(tPtr,0xcc89c812,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); return; @@ -268,7 +268,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd for(i=0;is >= s->e) goto next_slice; - ps->b->b[ps->e++] = s->b->b[s->s++]; // move from head of current to end of previous + ps->b->unsafeData[ps->e++] = s->b->unsafeData[s->s++]; // move from head of current to end of previous } next_slice: ps = s++; } @@ -277,18 +277,18 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd // Simultaneously decrypt and assemble packet into a contiguous buffer. // Since we moved data around above all slices will have sizes that are // multiples of 64. - memcpy(pkt.b->b,ph,sizeof(Protocol::Header)); + memcpy(pkt.b->unsafeData,ph,sizeof(Protocol::Header)); pkt.e = sizeof(Protocol::Header); for(FCV::iterator s(pktv.begin());s!=pktv.end();++s) { const unsigned int sliceSize = s->e - s->s; - s20.crypt12(s->b->b + s->s,pkt.b->b + pkt.e,sliceSize); + s20.crypt12(s->b->unsafeData + s->s,pkt.b->unsafeData + pkt.e,sliceSize); pkt.e += sliceSize; } ph = &(pkt.b->as()); // Verify packet MAC. uint64_t mac[2]; - poly1305(mac,pkt.b->b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); + poly1305(mac,pkt.b->unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); if (ph->mac != mac[0]) { RR->t->incomingPacketDropped(tPtr,0xbc881231,ph->packetId,0,peer->identity(),path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); return; @@ -353,8 +353,8 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd SharedPtr nb(new Buf()); const int uncompressedLen = LZ4_decompress_safe( - reinterpret_cast(pkt.b->b + ZT_PROTO_PACKET_PAYLOAD_START), - reinterpret_cast(nb->b), + reinterpret_cast(pkt.b->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START), + reinterpret_cast(nb->unsafeData), (int)(packetSize - ZT_PROTO_PACKET_PAYLOAD_START), ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START); @@ -402,7 +402,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd case Protocol::VERB_MULTICAST: ok = RR->vl2->_MULTICAST(tPtr,path,peer,*pkt.b,(int)packetSize); break; case Protocol::VERB_ENCAP: ok = _ENCAP(tPtr,path,peer,*pkt.b,(int)packetSize); break; default: - RR->t->incomingPacketDropped(tPtr,0xdeadeff0,ph->packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB); + RR->t->incomingPacketDropped(tPtr,0xeeeeeff0,ph->packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB); break; } if (ok) @@ -414,10 +414,10 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd void VL1::_relay(void *tPtr,const SharedPtr &path,const Address &destination,SharedPtr &data,unsigned int len) { - const uint8_t newHopCount = (data->b[ZT_PROTO_PACKET_FLAGS_INDEX] & 7U) + 1; + const uint8_t newHopCount = (data->lI8(ZT_PROTO_PACKET_FLAGS_INDEX) & 7U) + 1; if (newHopCount >= ZT_RELAY_MAX_HOPS) return; - data->b[ZT_PROTO_PACKET_FLAGS_INDEX] = (data->b[ZT_PROTO_PACKET_FLAGS_INDEX] & 0xf8U) | newHopCount; + data->sI8(ZT_PROTO_PACKET_FLAGS_INDEX,(data->lI8(ZT_PROTO_PACKET_FLAGS_INDEX) & 0xf8U) | newHopCount); const SharedPtr toPeer(RR->topology->peer(tPtr,destination,false)); if (!toPeer) @@ -427,7 +427,7 @@ void VL1::_relay(void *tPtr,const SharedPtr &path,const Address &destinati if (!toPath) return; - toPath->send(RR,tPtr,data->b,len,now); + toPath->send(RR,tPtr,data->unsafeData,len,now); } void VL1::_sendPendingWhois(void *const tPtr,const int64_t now) @@ -467,15 +467,15 @@ void VL1::_sendPendingWhois(void *const tPtr,const int64_t now) int outl = sizeof(Protocol::Header); while ((a != toSend.end())&&(outl < ZT_PROTO_MAX_PACKET_LENGTH)) { - a->copyTo(outp.b + outl); + a->copyTo(outp.unsafeData + outl); ++a; outl += ZT_ADDRESS_LENGTH; } if (outl > sizeof(Protocol::Header)) { - Protocol::armor(outp,outl,root->key(),peer->cipher()); + Protocol::armor(outp,outl,root->key(),root->cipher()); RR->expect->sending(ph.packetId,now); - rootPath->send(RR,tPtr,outp.b,outl,now); + rootPath->send(RR,tPtr,outp.unsafeData,outl,now); } } } @@ -525,7 +525,7 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr &path,SharedPtr &peer,Bu Protocol::salsa2012DeriveKey(peer->key(),perPacketKey,pkt,packetSize); Salsa20(perPacketKey,&p.h.packetId).crypt12(Utils::ZERO256,macKey,ZT_POLY1305_KEY_LEN); uint64_t mac[2]; - poly1305(mac,pkt.b + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); + poly1305(mac,pkt.unsafeData + ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,packetSize - ZT_PROTO_PACKET_ENCRYPTED_SECTION_START,macKey); if (p.h.mac != mac[0]) { RR->t->incomingPacketDropped(tPtr,0x11bfff81,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); return false; @@ -542,8 +542,8 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr &path,SharedPtr &peer,Bu } packetSize -= ZT_HMACSHA384_LEN; KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,0,hmacKey); // iter == 0 for HELLO, 1 for OK(HELLO) - HMACSHA384(hmacKey,pkt.b,packetSize,hmac); - if (!Utils::secureEq(pkt.b + packetSize,hmac,ZT_HMACSHA384_LEN)) { + HMACSHA384(hmacKey,pkt.unsafeData,packetSize,hmac); + if (!Utils::secureEq(pkt.unsafeData + packetSize,hmac,ZT_HMACSHA384_LEN)) { RR->t->incomingPacketDropped(tPtr,0x1000662a,p.h.packetId,0,id,path->address(),hops,Protocol::VERB_NOP,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED); return false; } @@ -568,10 +568,10 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr &path,SharedPtr &peer,Bu // can't even get ephemeral public keys without first knowing the long term secret key, // adding a little defense in depth. uint8_t iv[8]; - for (int i = 0; i < 8; ++i) iv[i] = pkt.b[i]; + for (int i = 0; i < 8; ++i) iv[i] = pkt.unsafeData[i]; iv[7] &= 0xf8U; // this exists for pure legacy reasons, meh... Salsa20 s20(key,iv); - s20.crypt12(pkt.b + ptr,pkt.b + ptr,packetSize - ptr); + s20.crypt12(pkt.unsafeData + ptr,pkt.unsafeData + ptr,packetSize - ptr); ptr += pkt.rI16(ptr); // skip length field which currently is always zero in v2.0+ @@ -654,23 +654,23 @@ bool VL1::_HELLO(void *tPtr,const SharedPtr &path,SharedPtr &peer,Bu int outl = sizeof(Protocol::OK::HELLO); outp.wO(outl,path->address()); - outp.wI(outl,(uint16_t)0); // legacy field, always 0 + outp.wI16(outl,0); // legacy field, always 0 if (p.versionProtocol >= 11) { - outp.wI(outl,(uint16_t)myNodeMetaDataBin.size()); + outp.wI16(outl,(uint16_t)myNodeMetaDataBin.size()); outp.wB(outl,myNodeMetaDataBin.data(),(unsigned int)myNodeMetaDataBin.size()); - outp.wI(outl,(uint16_t)0); // length of additional fields, currently 0 + outp.wI16(outl,0); // length of additional fields, currently 0 if ((outl + ZT_HMACSHA384_LEN) > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check, shouldn't be possible return false; KBKDFHMACSHA384(key,ZT_PROTO_KDF_KEY_LABEL_HELLO_HMAC,0,1,hmacKey); // iter == 1 for OK - HMACSHA384(hmacKey,outp.b + sizeof(ok.h),outl - sizeof(ok.h),outp.b + outl); + HMACSHA384(hmacKey,outp.unsafeData + sizeof(ok.h),outl - sizeof(ok.h),outp.unsafeData + outl); outl += ZT_HMACSHA384_LEN; } Protocol::armor(outp,outl,peer->key(),peer->cipher()); - path->send(RR,tPtr,outp.b,outl,now); + path->send(RR,tPtr,outp.unsafeData,outl,now); return true; } @@ -781,7 +781,7 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr &path,const SharedPtr &p int outl = sizeof(Protocol::OK::WHOIS); while ( ((ptr + ZT_ADDRESS_LENGTH) <= packetSize) && ((outl + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX) < ZT_PROTO_MAX_PACKET_LENGTH) ) { - const SharedPtr &wp(RR->topology->peer(tPtr,Address(pkt.b + ptr))); + const SharedPtr &wp(RR->topology->peer(tPtr,Address(pkt.unsafeData + ptr))); if (wp) { outp.wO(outl,wp->identity()); if (peer->remoteVersionProtocol() >= 11) { // older versions don't know what a locator is @@ -798,7 +798,7 @@ bool VL1::_WHOIS(void *tPtr,const SharedPtr &path,const SharedPtr &p if (outl > sizeof(Protocol::OK::WHOIS)) { Protocol::armor(outp,outl,peer->key(),peer->cipher()); - path->send(RR,tPtr,outp.b,outl,RR->node->now()); + path->send(RR,tPtr,outp.unsafeData,outl,RR->node->now()); } } @@ -823,28 +823,27 @@ bool VL1::_RENDEZVOUS(void *tPtr,const SharedPtr &path,const SharedPtrcontact(tPtr,Endpoint(atAddr),now,false,false); - RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); + const InetAddress atAddr(pkt.unsafeData + sizeof(Protocol::RENDEZVOUS),rdv.addressLength,port); + peer->contact(tPtr,Endpoint(atAddr),now,false); + RR->t->tryingNewPath(tPtr,0x55a19aaa,with->identity(),atAddr,path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); } break; - case 255: - if ((sizeof(Protocol::RENDEZVOUS) + 1) <= packetSize) { - Endpoint ep; - int epl = ep.unmarshal(pkt.b + sizeof(Protocol::RENDEZVOUS),packetSize - (int)sizeof(Protocol::RENDEZVOUS)); - if ((epl > 0) && (ep)) { - switch (ep.type()) { - case Endpoint::INETADDR_V4: - case Endpoint::INETADDR_V6: - peer->contact(tPtr,ep,now,false,false); - RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); - break; - default: - break; - } + case 255: { + Endpoint ep; + int p = sizeof(Protocol::RENDEZVOUS); + int epl = pkt.rO(p,ep); + if ((epl > 0) && (ep) && (!Buf::readOverflow(p,packetSize))) { + switch (ep.type()) { + case Endpoint::TYPE_INETADDR_V4: + case Endpoint::TYPE_INETADDR_V6: + peer->contact(tPtr,ep,now,false); + RR->t->tryingNewPath(tPtr,0x55a19aab,with->identity(),ep.inetAddr(),path->address(),Protocol::packetId(pkt,packetSize),Protocol::VERB_RENDEZVOUS,peer->address(),peer->identity().hash().data(),ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS); + break; + default: + break; } } - break; + } break; } } } @@ -872,7 +871,7 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr &path,const SharedPtr &pe outh.h.inReVerb = Protocol::VERB_ECHO; outh.h.inRePacketId = packetId; int outl = sizeof(Protocol::OK::ECHO); - outp.wB(outl,pkt.b + sizeof(Protocol::Header),packetSize - sizeof(Protocol::Header)); + outp.wB(outl,pkt.unsafeData + sizeof(Protocol::Header),packetSize - sizeof(Protocol::Header)); if (Buf::writeOverflow(outl)) { RR->t->incomingPacketDropped(tPtr,0x14d70bb0,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); @@ -880,7 +879,7 @@ bool VL1::_ECHO(void *tPtr,const SharedPtr &path,const SharedPtr &pe } Protocol::armor(outp,outl,peer->key(),peer->cipher()); - path->send(RR,tPtr,outp.b,outl,now); + path->send(RR,tPtr,outp.unsafeData,outl,now); } else { RR->t->incomingPacketDropped(tPtr,0x27878bc1,packetId,0,peer->identity(),path->address(),Protocol::packetHops(pkt,packetSize),Protocol::VERB_ECHO,ZT_TRACE_PACKET_DROP_REASON_RATE_LIMIT_EXCEEDED); } @@ -908,13 +907,21 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr &path,const Shared Endpoint ep; for(unsigned int pi=0;pit->incomingPacketDropped(tPtr,0xaed00118,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); return false; } + if (Buf::readOverflow(ptr,packetSize)) { + RR->t->incomingPacketDropped(tPtr,0xb450e10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); + return false; + } const void *addrBytes = nullptr; unsigned int addrLen = 0; @@ -934,11 +941,14 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr &path,const Shared addrLen = 16; addrPort = pkt.rI16(ptr); break; + //case 200: + // TODO: this would be a WebRTC SDP offer contained in the extended attrs field + //break; default: break; } if (Buf::readOverflow(ptr,packetSize)) { - RR->t->incomingPacketDropped(tPtr,0xbad0f10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); + RR->t->incomingPacketDropped(tPtr,0xb4d0f10f,pdp.h.packetId,0,peer->identity(),path->address(),Protocol::packetHops(pdp.h),Protocol::VERB_PUSH_DIRECT_PATHS,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET); return false; } @@ -951,8 +961,8 @@ bool VL1::_PUSH_DIRECT_PATHS(void *tPtr,const SharedPtr &path,const Shared } switch(ep.type()) { - case Endpoint::INETADDR_V4: - case Endpoint::INETADDR_V6: + case Endpoint::TYPE_INETADDR_V4: + case Endpoint::TYPE_INETADDR_V6: a = ep.inetAddr(); break; default: // other types are not supported yet