diff --git a/core/Endpoint.cpp b/core/Endpoint.cpp index 19accfae9..7d08dde36 100644 --- a/core/Endpoint.cpp +++ b/core/Endpoint.cpp @@ -30,14 +30,14 @@ char *Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept break; case ZT_ENDPOINT_TYPE_ZEROTIER: s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER]; - s[1] = '='; + s[1] = '/'; zt().toString(s + 2); break; case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_BLUETOOTH: s[0] = s_endpointTypeChars[this->type]; - s[1] = '='; + s[1] = '/'; eth().toString(s + 2); break; case ZT_ENDPOINT_TYPE_IP: @@ -45,7 +45,7 @@ char *Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_HTTP: s[0] = s_endpointTypeChars[this->type]; - s[1] = '='; + s[1] = '/'; ip().toString(s + 2); break; } @@ -59,13 +59,32 @@ bool Endpoint::fromString(const char *s) noexcept if ((!s) || (!*s)) return true; - const char *start = strchr(s, '='); - if (start++ != nullptr) { - // Parse a fully qualified type-address format Endpoint. + // Locate first slash, colon, and dot to help classify input. + const char *slash = nullptr, *colon = nullptr, *dot = nullptr; + for(const char *p=s;;++p) { + const char c = *p; + if (c != 0) { + switch (c) { + case '/': + slash = p; + break; + case ':': + colon = p; + break; + case '.': + dot = p; + break; + } + } else break; + } + + if ((slash != nullptr) && (((colon == nullptr) && (dot == nullptr)) || (colon > slash) || (dot > slash))) { + // Detect a fully specified endpoint of the form type/ip/port or type/other, + // but don't detect ip/port as a fully specified endpoint. char tmp[16]; for (unsigned int i=0;i<16;++i) { - char ss = s[i]; - if (ss == '-') { + const char ss = s[i]; + if (ss == '/') { tmp[i] = 0; break; } @@ -74,32 +93,33 @@ bool Endpoint::fromString(const char *s) noexcept tmp[15] = 0; this->type = (ZT_EndpointType)Utils::strToUInt(tmp); - Fingerprint tmpfp; - MAC tmpmac; + ++slash; switch (this->type) { case ZT_ENDPOINT_TYPE_NIL: break; - case ZT_ENDPOINT_TYPE_ZEROTIER: - if (!tmpfp.fromString(start)) + case ZT_ENDPOINT_TYPE_ZEROTIER: { + Fingerprint tmpfp; + if (!tmpfp.fromString(slash)) return false; this->value.fp = tmpfp; - break; + } break; case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_WIFI_DIRECT: - case ZT_ENDPOINT_TYPE_BLUETOOTH: - tmpmac.fromString(start); + case ZT_ENDPOINT_TYPE_BLUETOOTH: { + MAC tmpmac; + tmpmac.fromString(slash); this->value.mac = tmpmac.toInt(); - break; + } break; case ZT_ENDPOINT_TYPE_IP: case ZT_ENDPOINT_TYPE_IP_UDP: case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_HTTP: - if (!asInetAddress(this->value.ss).fromString(start)) + if (!asInetAddress(this->value.ss).fromString(slash)) return false; default: return false; } - } else if ((strchr(s, ':')) || (strchr(s, '.'))) { + } else if (((colon != nullptr) || (dot != nullptr)) && (slash != nullptr)) { // Parse raw IP/port strings as IP_UDP endpoints. this->type = ZT_ENDPOINT_TYPE_IP_UDP; if (!asInetAddress(this->value.ss).fromString(s)) diff --git a/core/Identity.cpp b/core/Identity.cpp index f82b0789e..bb3c96aea 100644 --- a/core/Identity.cpp +++ b/core/Identity.cpp @@ -37,29 +37,29 @@ void identityV0ProofOfWorkFrankenhash(const void *const publicKey, unsigned int // Initialize genmem[] using Salsa20 in a CBC-like configuration since // ordinary Salsa20 is randomly seek-able. This is good for a cipher // but is not what we want for sequential memory-hardness. - Utils::zero(genmem); - Salsa20 s20(digest, (char *) digest + 32); - s20.crypt20((char *) genmem, (char *) genmem, 64); - for (unsigned long i = 64;i < ZT_V0_IDENTITY_GEN_MEMORY;i += 64) { + Utils::zero< ZT_V0_IDENTITY_GEN_MEMORY >(genmem); + Salsa20 s20(digest, (char *)digest + 32); + s20.crypt20((char *)genmem, (char *)genmem, 64); + for (unsigned long i = 64; i < ZT_V0_IDENTITY_GEN_MEMORY; i += 64) { unsigned long k = i - 64; - *((uint64_t * )((char *) genmem + i)) = *((uint64_t * )((char *) genmem + k)); - *((uint64_t * )((char *) genmem + i + 8)) = *((uint64_t * )((char *) genmem + k + 8)); - *((uint64_t * )((char *) genmem + i + 16)) = *((uint64_t * )((char *) genmem + k + 16)); - *((uint64_t * )((char *) genmem + i + 24)) = *((uint64_t * )((char *) genmem + k + 24)); - *((uint64_t * )((char *) genmem + i + 32)) = *((uint64_t * )((char *) genmem + k + 32)); - *((uint64_t * )((char *) genmem + i + 40)) = *((uint64_t * )((char *) genmem + k + 40)); - *((uint64_t * )((char *) genmem + i + 48)) = *((uint64_t * )((char *) genmem + k + 48)); - *((uint64_t * )((char *) genmem + i + 56)) = *((uint64_t * )((char *) genmem + k + 56)); - s20.crypt20((char *) genmem + i, (char *) genmem + i, 64); + *((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k)); + *((uint64_t *)((char *)genmem + i + 8)) = *((uint64_t *)((char *)genmem + k + 8)); + *((uint64_t *)((char *)genmem + i + 16)) = *((uint64_t *)((char *)genmem + k + 16)); + *((uint64_t *)((char *)genmem + i + 24)) = *((uint64_t *)((char *)genmem + k + 24)); + *((uint64_t *)((char *)genmem + i + 32)) = *((uint64_t *)((char *)genmem + k + 32)); + *((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40)); + *((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48)); + *((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56)); + s20.crypt20((char *)genmem + i, (char *)genmem + i, 64); } // Render final digest using genmem as a lookup table - for (unsigned long i = 0;i < (ZT_V0_IDENTITY_GEN_MEMORY / sizeof(uint64_t));) { - unsigned long idx1 = (unsigned long) (Utils::ntoh(((uint64_t *) genmem)[i++]) % (64 / sizeof(uint64_t))); // NOLINT(hicpp-use-auto,modernize-use-auto) - unsigned long idx2 = (unsigned long) (Utils::ntoh(((uint64_t *) genmem)[i++]) % (ZT_V0_IDENTITY_GEN_MEMORY / sizeof(uint64_t))); // NOLINT(hicpp-use-auto,modernize-use-auto) - uint64_t tmp = ((uint64_t *) genmem)[idx2]; - ((uint64_t *) genmem)[idx2] = ((uint64_t *) digest)[idx1]; - ((uint64_t *) digest)[idx1] = tmp; + for (unsigned long i = 0; i < (ZT_V0_IDENTITY_GEN_MEMORY / sizeof(uint64_t));) { + unsigned long idx1 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (64 / sizeof(uint64_t))); // NOLINT(hicpp-use-auto,modernize-use-auto) + unsigned long idx2 = (unsigned long)(Utils::ntoh(((uint64_t *)genmem)[i++]) % (ZT_V0_IDENTITY_GEN_MEMORY / sizeof(uint64_t))); // NOLINT(hicpp-use-auto,modernize-use-auto) + uint64_t tmp = ((uint64_t *)genmem)[idx2]; + ((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1]; + ((uint64_t *)digest)[idx1] = tmp; s20.crypt20(digest, digest, 64); } } @@ -86,7 +86,9 @@ struct p_CompareLittleEndian #if __BYTE_ORDER == __BIG_ENDIAN ZT_INLINE bool operator()(const uint64_t a,const uint64_t b) const noexcept { return Utils::swapBytes(a) < Utils::swapBytes(b); } #else - ZT_INLINE bool operator()(const uint64_t a,const uint64_t b) const noexcept { return a < b; } + ZT_INLINE bool operator()(const uint64_t a, const uint64_t b) const noexcept + { return a < b; } + #endif }; @@ -100,7 +102,7 @@ bool identityV1ProofOfWorkCriteria(const void *in, const unsigned int len) // executing all branches and then selecting the answer, which means this // construction should require a GPU to do ~3X the work of a CPU per iteration. SHA512(w, in, len); - for (unsigned int i = 8, j = 0;i < (ZT_IDENTITY_V1_POW_MEMORY_SIZE / 8);) { + for (unsigned int i = 8, j = 0; i < (ZT_IDENTITY_V1_POW_MEMORY_SIZE / 8);) { uint64_t *const ww = w + i; const uint64_t *const wp = w + j; i += 8; @@ -159,7 +161,8 @@ bool Identity::generate(const Type t) delete[] genmem; m_fp.address = address; // address comes from PoW hash for type 0 identities m_computeHash(); - } break; + } + break; case P384: { for (;;) { @@ -185,7 +188,8 @@ bool Identity::generate(const Type t) break; } } - } break; + } + break; default: return false; @@ -201,7 +205,7 @@ bool Identity::locallyValidate() const noexcept switch (m_type) { case C25519: { uint8_t digest[64]; - char *const genmem = (char *) malloc(ZT_V0_IDENTITY_GEN_MEMORY); + char *const genmem = (char *)malloc(ZT_V0_IDENTITY_GEN_MEMORY); if (!genmem) return false; identityV0ProofOfWorkFrankenhash(m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, digest, genmem); @@ -225,14 +229,13 @@ void Identity::hashWithPrivate(uint8_t h[ZT_FINGERPRINT_HASH_SIZE]) const switch (m_type) { case C25519: SHA384(h, m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); - break; + return; case P384: SHA384(h, m_pub, sizeof(m_pub), m_priv, sizeof(m_priv)); - break; + return; } - return; } - Utils::zero<48>(h); + Utils::zero< ZT_FINGERPRINT_HASH_SIZE >(h); } unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsigned int siglen) const @@ -250,7 +253,7 @@ unsigned int Identity::sign(const void *data, unsigned int len, void *sig, unsig static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE, "weird!"); uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); - ECC384ECDSASign(m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, h, (uint8_t *) sig); + ECC384ECDSASign(m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, h, (uint8_t *)sig); return ZT_ECC384_SIGNATURE_SIZE; } } @@ -267,7 +270,7 @@ bool Identity::verify(const void *data, unsigned int len, const void *sig, unsig if (siglen == ZT_ECC384_SIGNATURE_SIZE) { uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); - return ECC384ECDSAVerify(m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, h, (const uint8_t *) sig); + return ECC384ECDSAVerify(m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, h, (const uint8_t *)sig); } break; } @@ -276,37 +279,25 @@ bool Identity::verify(const void *data, unsigned int len, const void *sig, unsig bool Identity::agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const { - uint8_t rawkey[128]; - uint8_t h[64]; - if (m_hasPrivate) { - if (m_type == C25519) { - if ((id.m_type == C25519) || (id.m_type == P384)) { - // If we are a C25519 key we can agree with another C25519 key or with only the - // C25519 portion of a type 1 P-384 key. - C25519::agree(m_priv, id.m_pub, rawkey); - SHA512(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE); - Utils::copy(key, h); - return true; - } - } else if (m_type == P384) { - if (id.m_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(m_priv, id.m_pub, rawkey); - ECC384ECDH(id.m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, rawkey + ZT_C25519_ECDH_SHARED_SECRET_SIZE); - SHA384(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE + ZT_ECC384_SHARED_SECRET_SIZE); - Utils::copy(key, h); - return true; - } else if (id.m_type == C25519) { - // If the other identity is a C25519 identity we can agree using only that type. - C25519::agree(m_priv, id.m_pub, rawkey); - SHA512(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE); - Utils::copy(key, h); - return true; - } + uint8_t rawkey[128], h[64]; + if (likely(m_hasPrivate)) { + if ((m_type == C25519) || (id.m_type == C25519)) { + // If we are a C25519 key we can agree with another C25519 key or with only the + // C25519 portion of a type 1 P-384 key. + C25519::agree(m_priv, id.m_pub, rawkey); + SHA512(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE); + Utils::copy< ZT_SYMMETRIC_KEY_SIZE >(key, h); + return true; + } else if ((m_type == P384) && (id.m_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(m_priv, id.m_pub, rawkey); + ECC384ECDH(id.m_pub + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, rawkey + ZT_C25519_ECDH_SHARED_SECRET_SIZE); + SHA384(key, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE + ZT_ECC384_SHARED_SECRET_SIZE); + return true; } } return false; @@ -330,22 +321,22 @@ char *Identity::toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER Utils::hex(m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, p); p += ZT_C25519_COMBINED_PRIVATE_KEY_SIZE * 2; } - *p = (char) 0; + *p = (char)0; return buf; } case P384: { *(p++) = '1'; *(p++) = ':'; - int el = Utils::b32e(m_pub, sizeof(m_pub), p, (int) (ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t) (p - buf))); + int el = Utils::b32e(m_pub, sizeof(m_pub), p, (int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); if (el <= 0) return nullptr; p += el; if ((m_hasPrivate) && (includePrivate)) { *(p++) = ':'; - el = Utils::b32e(m_priv, sizeof(m_priv), p, (int) (ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t) (p - buf))); + el = Utils::b32e(m_priv, sizeof(m_priv), p, (int)(ZT_IDENTITY_STRING_BUFFER_LENGTH - (uintptr_t)(p - buf))); if (el <= 0) return nullptr; p += el; } - *p = (char) 0; + *p = (char)0; return buf; } } @@ -362,7 +353,7 @@ bool Identity::fromString(const char *str) int fno = 0; char *saveptr = nullptr; - for (char *f = Utils::stok(tmp, ":", &saveptr);((f) && (fno < 4));f = Utils::stok(nullptr, ":", &saveptr)) { + for (char *f = Utils::stok(tmp, ":", &saveptr); ((f) && (fno < 4)); f = Utils::stok(nullptr, ":", &saveptr)) { switch (fno++) { case 0: @@ -437,11 +428,11 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool inc switch (m_type) { case C25519: - data[ZT_ADDRESS_LENGTH] = (uint8_t) C25519; - Utils::copy(data + ZT_ADDRESS_LENGTH + 1, m_pub); + data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519; + Utils::copy< ZT_C25519_COMBINED_PUBLIC_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1, m_pub); if ((includePrivate) && (m_hasPrivate)) { data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; - Utils::copy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1, m_priv); + Utils::copy< ZT_C25519_COMBINED_PRIVATE_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1, m_priv); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; } else { data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = 0; @@ -449,11 +440,11 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool inc } case P384: - data[ZT_ADDRESS_LENGTH] = (uint8_t) P384; - Utils::copy(data + ZT_ADDRESS_LENGTH + 1, m_pub); + data[ZT_ADDRESS_LENGTH] = (uint8_t)P384; + Utils::copy< ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1, m_pub); if ((includePrivate) && (m_hasPrivate)) { data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; - Utils::copy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1, m_priv); + Utils::copy< ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1, m_priv); 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; @@ -473,13 +464,13 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept m_fp.address = Address(data); unsigned int privlen; - switch ((m_type = (Type) data[ZT_ADDRESS_LENGTH])) { + switch ((m_type = (Type)data[ZT_ADDRESS_LENGTH])) { case C25519: if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1)) return -1; - Utils::copy(m_pub, data + ZT_ADDRESS_LENGTH + 1); + Utils::copy< ZT_C25519_COMBINED_PUBLIC_KEY_SIZE >(m_pub, data + ZT_ADDRESS_LENGTH + 1); m_computeHash(); privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE]; @@ -487,7 +478,7 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE)) return -1; m_hasPrivate = true; - Utils::copy(m_priv, data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1); + Utils::copy< ZT_C25519_COMBINED_PRIVATE_KEY_SIZE >(m_priv, data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1); return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; } else if (privlen == 0) { m_hasPrivate = false; @@ -499,7 +490,7 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1)) return -1; - Utils::copy(m_pub, data + ZT_ADDRESS_LENGTH + 1); + Utils::copy< ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE >(m_pub, data + ZT_ADDRESS_LENGTH + 1); m_computeHash(); // this sets the address for P384 if (Address(m_fp.hash) != m_fp.address) // this sanity check is possible with V1 identities return -1; @@ -509,7 +500,7 @@ int Identity::unmarshal(const uint8_t *data, const int len) noexcept if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)) return -1; m_hasPrivate = true; - Utils::copy(&m_priv, data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1); + Utils::copy< ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE >(&m_priv, data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 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) { m_hasPrivate = false; @@ -547,7 +538,7 @@ ZT_Identity *ZT_Identity_new(enum ZT_IdentityType type) return nullptr; try { ZeroTier::Identity *const id = new ZeroTier::Identity(); - id->generate((ZeroTier::Identity::Type) type); + id->generate((ZeroTier::Identity::Type)type); return reinterpret_cast(id); } catch (...) { return nullptr; @@ -596,8 +587,8 @@ int ZT_Identity_verify(const ZT_Identity *id, const void *data, unsigned int len enum ZT_IdentityType ZT_Identity_type(const ZT_Identity *id) { if (!id) - return (ZT_IdentityType) 0; - return (enum ZT_IdentityType) reinterpret_cast(id)->type(); + return (ZT_IdentityType)0; + return (enum ZT_IdentityType)reinterpret_cast(id)->type(); } char *ZT_Identity_toString(const ZT_Identity *id, char *buf, int capacity, int includePrivate) diff --git a/core/MAC.hpp b/core/MAC.hpp index 543a9ec00..b2090d407 100644 --- a/core/MAC.hpp +++ b/core/MAC.hpp @@ -32,7 +32,7 @@ public: {} ZT_INLINE MAC(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d, const uint8_t e, const uint8_t f) noexcept: - m_mac((((uint64_t) a) << 40U) | (((uint64_t) b) << 32U) | (((uint64_t) c) << 24U) | (((uint64_t) d) << 16U) | (((uint64_t) e) << 8U) | ((uint64_t) f)) + m_mac((((uint64_t)a) << 40U) | (((uint64_t)b) << 32U) | (((uint64_t)c) << 24U) | (((uint64_t)d) << 16U) | (((uint64_t)e) << 8U) | ((uint64_t)f)) {} explicit ZT_INLINE MAC(const uint64_t m) noexcept: @@ -63,7 +63,7 @@ public: */ ZT_INLINE void setTo(const uint8_t b[6]) noexcept { - m_mac = ((uint64_t) b[0] << 40U) | ((uint64_t) b[1] << 32U) | ((uint64_t) b[2] << 24U) | ((uint64_t) b[3] << 16U) | ((uint64_t) b[4] << 8U) | (uint64_t) b[5]; + m_mac = ((uint64_t)b[0] << 40U) | ((uint64_t)b[1] << 32U) | ((uint64_t)b[2] << 24U) | ((uint64_t)b[3] << 16U) | ((uint64_t)b[4] << 8U) | (uint64_t)b[5]; } /** @@ -72,12 +72,12 @@ public: */ ZT_INLINE void copyTo(uint8_t b[6]) const noexcept { - b[0] = (uint8_t) (m_mac >> 40U); - b[1] = (uint8_t) (m_mac >> 32U); - b[2] = (uint8_t) (m_mac >> 24U); - b[3] = (uint8_t) (m_mac >> 16U); - b[4] = (uint8_t) (m_mac >> 8U); - b[5] = (uint8_t) m_mac; + b[0] = (uint8_t)(m_mac >> 40U); + b[1] = (uint8_t)(m_mac >> 32U); + b[2] = (uint8_t)(m_mac >> 24U); + b[3] = (uint8_t)(m_mac >> 16U); + b[4] = (uint8_t)(m_mac >> 8U); + b[5] = (uint8_t)m_mac; } /** @@ -100,7 +100,7 @@ public: */ ZT_INLINE void fromAddress(const Address &ztaddr, uint64_t nwid) noexcept { - uint64_t m = ((uint64_t) firstOctetForNetwork(nwid)) << 40U; + uint64_t m = ((uint64_t)firstOctetForNetwork(nwid)) << 40U; m |= ztaddr.toInt(); // a is 40 bits m ^= ((nwid >> 8U) & 0xffU) << 32U; m ^= ((nwid >> 16U) & 0xffU) << 24U; @@ -134,7 +134,7 @@ public: */ static ZT_INLINE unsigned char firstOctetForNetwork(uint64_t nwid) noexcept { - const uint8_t a = ((uint8_t) (nwid & 0xfeU) | 0x02U); // locally administered, not multicast, from LSB of network ID + const uint8_t a = ((uint8_t)(nwid & 0xfeU) | 0x02U); // locally administered, not multicast, from LSB of network ID return ((a == 0x52) ? 0x32 : a); // blacklist 0x52 since it's used by KVM, libvirt, and other popular virtualization engines... seems de-facto standard on Linux } @@ -143,7 +143,7 @@ public: * @return Byte at said position (address interpreted in big-endian order) */ ZT_INLINE uint8_t operator[](unsigned int i) const noexcept - { return (uint8_t)(m_mac >> (unsigned int) (40 - (i * 8))); } + { return (uint8_t)(m_mac >> (unsigned int)(40 - (i * 8))); } /** * @return 6, which is the number of bytes in a MAC, for container compliance @@ -152,7 +152,7 @@ public: { return 6; } ZT_INLINE unsigned long hashCode() const noexcept - { return (unsigned long) Utils::hash64(m_mac); } + { return (unsigned long)Utils::hash64(m_mac); } ZT_INLINE operator bool() const noexcept { return (m_mac != 0ULL); } @@ -185,12 +185,15 @@ public: buf[14] = ':'; buf[15] = Utils::HEXCHARS[(m_mac >> 4U) & 0xfU]; buf[16] = Utils::HEXCHARS[m_mac & 0xfU]; - buf[17] = (char) 0; + buf[17] = (char)0; return buf; } ZT_INLINE String toString() const - { char tmp[18]; return String(toString(tmp)); } + { + char tmp[18]; + return String(toString(tmp)); + } /** * Parse a MAC address in hex format with or without : separators and ignoring non-hex characters. @@ -205,11 +208,11 @@ public: uint64_t c; const char hc = *s++; if ((hc >= 48) && (hc <= 57)) - c = (uint64_t) hc - 48; + c = (uint64_t)hc - 48; else if ((hc >= 97) && (hc <= 102)) - c = (uint64_t) hc - 87; + c = (uint64_t)hc - 87; else if ((hc >= 65) && (hc <= 70)) - c = (uint64_t) hc - 55; + c = (uint64_t)hc - 55; else continue; m_mac = (m_mac << 4U) | c; } diff --git a/core/Node.cpp b/core/Node.cpp index 0b6797538..124c7387d 100644 --- a/core/Node.cpp +++ b/core/Node.cpp @@ -214,6 +214,20 @@ ZT_ResultCode Node::processVirtualNetworkFrame( } } +ZT_ResultCode Node::processHTTPResponse( + void *tptr, + int64_t now, + void *requestId, + int responseCode, + const char **headerNames, + const char **headerValues, + const void *body, + unsigned int bodySize, + unsigned int flags) +{ + return ZT_RESULT_OK; +} + ZT_ResultCode Node::processBackgroundTasks( void *tPtr, int64_t now, @@ -899,6 +913,27 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame( } } +enum ZT_ResultCode ZT_Node_processHTTPResponse( + ZT_Node *node, + void *tptr, + int64_t now, + void *requestId, + int responseCode, + const char **headerNames, + const char **headerValues, + const void *body, + unsigned int bodySize, + unsigned int flags) +{ + try { + return reinterpret_cast(node)->processHTTPResponse(tptr, now, requestId, responseCode, headerNames, headerValues, body, bodySize, flags); + } catch (std::bad_alloc &exc) { + return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; + } catch (...) { + return ZT_RESULT_ERROR_INTERNAL; + } +} + enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node, void *tptr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline) { try { diff --git a/core/Node.hpp b/core/Node.hpp index 3227b78e2..f0361ed04 100644 --- a/core/Node.hpp +++ b/core/Node.hpp @@ -71,6 +71,17 @@ public: unsigned int frameLength, volatile int64_t *nextBackgroundTaskDeadline); + ZT_ResultCode processHTTPResponse( + void *tptr, + int64_t now, + void *requestId, + int responseCode, + const char **headerNames, + const char **headerValues, + const void *body, + unsigned int bodySize, + unsigned int flags); + ZT_ResultCode processBackgroundTasks( void *tPtr, int64_t now, diff --git a/core/Utils.hpp b/core/Utils.hpp index 0cf002e13..e4293267c 100644 --- a/core/Utils.hpp +++ b/core/Utils.hpp @@ -53,11 +53,9 @@ namespace Utils { #define ZT_ROL32(x, r) (((x) << (r)) | ((x) >> (32 - (r)))) #ifdef ZT_ARCH_X64 - struct CPUIDRegisters { CPUIDRegisters() noexcept; - bool rdrand; bool aes; bool avx; @@ -68,7 +66,6 @@ struct CPUIDRegisters bool sha; bool fsrm; }; - extern const CPUIDRegisters CPUID; #endif @@ -285,10 +282,22 @@ static ZT_INLINE uint32_t hash32(uint32_t x) noexcept */ static ZT_INLINE bool allZero(const void *const b, unsigned int l) noexcept { + const uint8_t *p = reinterpret_cast(b); + +#ifndef ZT_NO_UNALIGNED_ACCESS + while (l >= 8) { + if (*reinterpret_cast(p) != 0) + return false; + p += 8; + l -= 8; + } +#endif + for (unsigned int i = 0; i < l; ++i) { - if (reinterpret_cast(b)[i] != 0) + if (reinterpret_cast(p)[i] != 0) return false; } + return true; } diff --git a/core/zerotier.h b/core/zerotier.h index e88f47757..3ba9780ce 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -1459,15 +1459,6 @@ typedef int (*ZT_StateGetFunction)( /** * Function to send a ZeroTier packet out over the physical wire (L2/L3) * - * Parameters: - * (1) Node - * (2) User pointer - * (3) Local socket or -1 for "all" or "any" - * (4) Remote address - * (5) Packet data - * (6) Packet length - * (7) Desired IP TTL or 0 to use default - * * If there is only one local socket, the local socket can be ignored. * If the local socket is -1, the packet should be sent out from all * bound local sockets or a random bound local socket. @@ -1490,6 +1481,29 @@ typedef int (*ZT_WirePacketSendFunction)( unsigned int, /* Packet length */ unsigned int); /* TTL or 0 to use default */ +/** + * Function to initiate HTTP requests + * + * The supplied HTTP request identifier is an opaque pointer that must + * be returned via ZT_Node_processHttpResponse(). If this handler is + * implemented then ZT_Node_processHttpResponse() must be called for + * each call made by the core to this. This function itself does not + * return any error code; use processHttpResponse() for that. It may + * be called directly from inside the implementation of this. + */ +typedef void (*ZT_HTTPRequestFunction)( + ZT_Node *, /* Node */ + void *, /* User ptr */ + void *, /* Thread ptr */ + void *, /* HTTP request identifier */ + const char *, /* HTTP method (GET, HEAD, etc.) */ + const char *, /* URL */ + const char **, /* Header names, NULL terminated */ + const char **, /* Header values, NULL terminated */ + const void *, /* Request body or NULL if none */ + unsigned int, /* Length of request body in bytes */ + unsigned int); /* Flags */ + /** * Function to check whether a path should be used for ZeroTier traffic * @@ -1568,6 +1582,11 @@ struct ZT_Node_Callbacks */ ZT_WirePacketSendFunction wirePacketSendFunction; + /** + * RECOMMENDED: Function to initiate HTTP requests + */ + ZT_HTTPRequestFunction httpRequestFunction; + /** * REQUIRED: Function to inject frames into a virtual network's TAP */ @@ -1718,6 +1737,33 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame( int isZtBuffer, volatile int64_t *nextBackgroundTaskDeadline); +/** + * Process a response from HTTP requests initiated via API callback + * + * @param node Node instance + * @param tptr Thread pointer to pass to functions/callbacks resulting from this call + * @param now Current clock in milliseconds + * @param requestId Opaque pointer provided via the requesting callback + * @param responseCode HTTP response code (e.g. 200, 500) + * @param headerNames HTTP header names, terminated by a NULL pointer + * @param headerValues HTTP header values corresponding with each name + * @param body Response body or NULL if none + * @param bodySize Size of response body in bytes + * @param flags Response flags + * @return OK (0) or error code if a fatal error condition has occurred + */ +ZT_SDK_API enum ZT_ResultCode ZT_Node_processHTTPResponse( + ZT_Node *node, + void *tptr, + int64_t now, + void *requestId, + int responseCode, + const char **headerNames, + const char **headerValues, + const void *body, + unsigned int bodySize, + unsigned int flags); + /** * Perform periodic background operations *