Some cleanup, revision to Endpoint string format, stub out HTTP API callbacks.

This commit is contained in:
Adam Ierymenko 2020-06-08 14:41:13 -07:00
parent 016d85b169
commit 40d48c969c
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 243 additions and 128 deletions

View file

@ -30,14 +30,14 @@ char *Endpoint::toString(char s[ZT_ENDPOINT_STRING_SIZE_MAX]) const noexcept
break; break;
case ZT_ENDPOINT_TYPE_ZEROTIER: case ZT_ENDPOINT_TYPE_ZEROTIER:
s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER]; s[0] = s_endpointTypeChars[ZT_ENDPOINT_TYPE_ZEROTIER];
s[1] = '='; s[1] = '/';
zt().toString(s + 2); zt().toString(s + 2);
break; break;
case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_ETHERNET:
case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_WIFI_DIRECT:
case ZT_ENDPOINT_TYPE_BLUETOOTH: case ZT_ENDPOINT_TYPE_BLUETOOTH:
s[0] = s_endpointTypeChars[this->type]; s[0] = s_endpointTypeChars[this->type];
s[1] = '='; s[1] = '/';
eth().toString(s + 2); eth().toString(s + 2);
break; break;
case ZT_ENDPOINT_TYPE_IP: 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_TCP:
case ZT_ENDPOINT_TYPE_IP_HTTP: case ZT_ENDPOINT_TYPE_IP_HTTP:
s[0] = s_endpointTypeChars[this->type]; s[0] = s_endpointTypeChars[this->type];
s[1] = '='; s[1] = '/';
ip().toString(s + 2); ip().toString(s + 2);
break; break;
} }
@ -59,13 +59,32 @@ bool Endpoint::fromString(const char *s) noexcept
if ((!s) || (!*s)) if ((!s) || (!*s))
return true; return true;
const char *start = strchr(s, '='); // Locate first slash, colon, and dot to help classify input.
if (start++ != nullptr) { const char *slash = nullptr, *colon = nullptr, *dot = nullptr;
// Parse a fully qualified type-address format Endpoint. 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]; char tmp[16];
for (unsigned int i=0;i<16;++i) { for (unsigned int i=0;i<16;++i) {
char ss = s[i]; const char ss = s[i];
if (ss == '-') { if (ss == '/') {
tmp[i] = 0; tmp[i] = 0;
break; break;
} }
@ -74,32 +93,33 @@ bool Endpoint::fromString(const char *s) noexcept
tmp[15] = 0; tmp[15] = 0;
this->type = (ZT_EndpointType)Utils::strToUInt(tmp); this->type = (ZT_EndpointType)Utils::strToUInt(tmp);
Fingerprint tmpfp; ++slash;
MAC tmpmac;
switch (this->type) { switch (this->type) {
case ZT_ENDPOINT_TYPE_NIL: case ZT_ENDPOINT_TYPE_NIL:
break; break;
case ZT_ENDPOINT_TYPE_ZEROTIER: case ZT_ENDPOINT_TYPE_ZEROTIER: {
if (!tmpfp.fromString(start)) Fingerprint tmpfp;
if (!tmpfp.fromString(slash))
return false; return false;
this->value.fp = tmpfp; this->value.fp = tmpfp;
break; } break;
case ZT_ENDPOINT_TYPE_ETHERNET: case ZT_ENDPOINT_TYPE_ETHERNET:
case ZT_ENDPOINT_TYPE_WIFI_DIRECT: case ZT_ENDPOINT_TYPE_WIFI_DIRECT:
case ZT_ENDPOINT_TYPE_BLUETOOTH: case ZT_ENDPOINT_TYPE_BLUETOOTH: {
tmpmac.fromString(start); MAC tmpmac;
tmpmac.fromString(slash);
this->value.mac = tmpmac.toInt(); this->value.mac = tmpmac.toInt();
break; } break;
case ZT_ENDPOINT_TYPE_IP: case ZT_ENDPOINT_TYPE_IP:
case ZT_ENDPOINT_TYPE_IP_UDP: case ZT_ENDPOINT_TYPE_IP_UDP:
case ZT_ENDPOINT_TYPE_IP_TCP: case ZT_ENDPOINT_TYPE_IP_TCP:
case ZT_ENDPOINT_TYPE_IP_HTTP: case ZT_ENDPOINT_TYPE_IP_HTTP:
if (!asInetAddress(this->value.ss).fromString(start)) if (!asInetAddress(this->value.ss).fromString(slash))
return false; return false;
default: default:
return false; 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. // Parse raw IP/port strings as IP_UDP endpoints.
this->type = ZT_ENDPOINT_TYPE_IP_UDP; this->type = ZT_ENDPOINT_TYPE_IP_UDP;
if (!asInetAddress(this->value.ss).fromString(s)) if (!asInetAddress(this->value.ss).fromString(s))

View file

@ -37,29 +37,29 @@ void identityV0ProofOfWorkFrankenhash(const void *const publicKey, unsigned int
// Initialize genmem[] using Salsa20 in a CBC-like configuration since // Initialize genmem[] using Salsa20 in a CBC-like configuration since
// ordinary Salsa20 is randomly seek-able. This is good for a cipher // ordinary Salsa20 is randomly seek-able. This is good for a cipher
// but is not what we want for sequential memory-hardness. // but is not what we want for sequential memory-hardness.
Utils::zero<ZT_V0_IDENTITY_GEN_MEMORY>(genmem); Utils::zero< ZT_V0_IDENTITY_GEN_MEMORY >(genmem);
Salsa20 s20(digest, (char *) digest + 32); Salsa20 s20(digest, (char *)digest + 32);
s20.crypt20((char *) genmem, (char *) genmem, 64); s20.crypt20((char *)genmem, (char *)genmem, 64);
for (unsigned long i = 64;i < ZT_V0_IDENTITY_GEN_MEMORY;i += 64) { for (unsigned long i = 64; i < ZT_V0_IDENTITY_GEN_MEMORY; i += 64) {
unsigned long k = i - 64; unsigned long k = i - 64;
*((uint64_t * )((char *) genmem + i)) = *((uint64_t * )((char *) genmem + k)); *((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 + 8)) = *((uint64_t *)((char *)genmem + k + 8));
*((uint64_t * )((char *) genmem + i + 16)) = *((uint64_t * )((char *) genmem + k + 16)); *((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 + 24)) = *((uint64_t *)((char *)genmem + k + 24));
*((uint64_t * )((char *) genmem + i + 32)) = *((uint64_t * )((char *) genmem + k + 32)); *((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 + 40)) = *((uint64_t *)((char *)genmem + k + 40));
*((uint64_t * )((char *) genmem + i + 48)) = *((uint64_t * )((char *) genmem + k + 48)); *((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
*((uint64_t * )((char *) genmem + i + 56)) = *((uint64_t * )((char *) genmem + k + 56)); *((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
s20.crypt20((char *) genmem + i, (char *) genmem + i, 64); s20.crypt20((char *)genmem + i, (char *)genmem + i, 64);
} }
// Render final digest using genmem as a lookup table // Render final digest using genmem as a lookup table
for (unsigned long i = 0;i < (ZT_V0_IDENTITY_GEN_MEMORY / sizeof(uint64_t));) { 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 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) 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 tmp = ((uint64_t *)genmem)[idx2];
((uint64_t *) genmem)[idx2] = ((uint64_t *) digest)[idx1]; ((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
((uint64_t *) digest)[idx1] = tmp; ((uint64_t *)digest)[idx1] = tmp;
s20.crypt20(digest, digest, 64); s20.crypt20(digest, digest, 64);
} }
} }
@ -86,7 +86,9 @@ struct p_CompareLittleEndian
#if __BYTE_ORDER == __BIG_ENDIAN #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); } ZT_INLINE bool operator()(const uint64_t a,const uint64_t b) const noexcept { return Utils::swapBytes(a) < Utils::swapBytes(b); }
#else #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 #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 // 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. // construction should require a GPU to do ~3X the work of a CPU per iteration.
SHA512(w, in, len); 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; uint64_t *const ww = w + i;
const uint64_t *const wp = w + j; const uint64_t *const wp = w + j;
i += 8; i += 8;
@ -159,7 +161,8 @@ bool Identity::generate(const Type t)
delete[] genmem; delete[] genmem;
m_fp.address = address; // address comes from PoW hash for type 0 identities m_fp.address = address; // address comes from PoW hash for type 0 identities
m_computeHash(); m_computeHash();
} break; }
break;
case P384: { case P384: {
for (;;) { for (;;) {
@ -185,7 +188,8 @@ bool Identity::generate(const Type t)
break; break;
} }
} }
} break; }
break;
default: default:
return false; return false;
@ -201,7 +205,7 @@ bool Identity::locallyValidate() const noexcept
switch (m_type) { switch (m_type) {
case C25519: { case C25519: {
uint8_t digest[64]; 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) if (!genmem)
return false; return false;
identityV0ProofOfWorkFrankenhash(m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, digest, genmem); 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) { switch (m_type) {
case C25519: case C25519:
SHA384(h, m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE); SHA384(h, m_pub, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE);
break; return;
case P384: case P384:
SHA384(h, m_pub, sizeof(m_pub), m_priv, sizeof(m_priv)); 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 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!"); static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE, "weird!");
uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE];
SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_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; 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) { if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE]; uint8_t h[ZT_ECC384_SIGNATURE_HASH_SIZE];
SHA384(h, data, len, m_pub, ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_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; break;
} }
@ -276,20 +279,16 @@ 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 bool Identity::agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const
{ {
uint8_t rawkey[128]; uint8_t rawkey[128], h[64];
uint8_t h[64]; if (likely(m_hasPrivate)) {
if (m_hasPrivate) { if ((m_type == C25519) || (id.m_type == C25519)) {
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 // 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 portion of a type 1 P-384 key.
C25519::agree(m_priv, id.m_pub, rawkey); C25519::agree(m_priv, id.m_pub, rawkey);
SHA512(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE); SHA512(h, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE);
Utils::copy<ZT_SYMMETRIC_KEY_SIZE>(key, h); Utils::copy< ZT_SYMMETRIC_KEY_SIZE >(key, h);
return true; return true;
} } else if ((m_type == P384) && (id.m_type == P384)) {
} else if (m_type == P384) {
if (id.m_type == P384) {
// For another P384 identity we execute DH agreement with BOTH keys and then // 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 // hash the results together. For those (cough FIPS cough) who only consider
// P384 to be kosher, the C25519 secret can be considered a "salt" // P384 to be kosher, the C25519 secret can be considered a "salt"
@ -297,16 +296,8 @@ bool Identity::agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) con
// your traffic is also protected by C25519. // your traffic is also protected by C25519.
C25519::agree(m_priv, id.m_pub, rawkey); 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); 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); SHA384(key, rawkey, ZT_C25519_ECDH_SHARED_SECRET_SIZE + ZT_ECC384_SHARED_SECRET_SIZE);
Utils::copy<ZT_SYMMETRIC_KEY_SIZE>(key, h);
return true; 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<ZT_SYMMETRIC_KEY_SIZE>(key, h);
return true;
}
} }
} }
return false; 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); Utils::hex(m_priv, ZT_C25519_COMBINED_PRIVATE_KEY_SIZE, p);
p += ZT_C25519_COMBINED_PRIVATE_KEY_SIZE * 2; p += ZT_C25519_COMBINED_PRIVATE_KEY_SIZE * 2;
} }
*p = (char) 0; *p = (char)0;
return buf; return buf;
} }
case P384: { case P384: {
*(p++) = '1'; *(p++) = '1';
*(p++) = ':'; *(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; if (el <= 0) return nullptr;
p += el; p += el;
if ((m_hasPrivate) && (includePrivate)) { if ((m_hasPrivate) && (includePrivate)) {
*(p++) = ':'; *(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; if (el <= 0) return nullptr;
p += el; p += el;
} }
*p = (char) 0; *p = (char)0;
return buf; return buf;
} }
} }
@ -362,7 +353,7 @@ bool Identity::fromString(const char *str)
int fno = 0; int fno = 0;
char *saveptr = nullptr; 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++) { switch (fno++) {
case 0: case 0:
@ -437,11 +428,11 @@ int Identity::marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], const bool inc
switch (m_type) { switch (m_type) {
case C25519: case C25519:
data[ZT_ADDRESS_LENGTH] = (uint8_t) C25519; data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
Utils::copy<ZT_C25519_COMBINED_PUBLIC_KEY_SIZE>(data + ZT_ADDRESS_LENGTH + 1, m_pub); Utils::copy< ZT_C25519_COMBINED_PUBLIC_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1, m_pub);
if ((includePrivate) && (m_hasPrivate)) { if ((includePrivate) && (m_hasPrivate)) {
data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = ZT_C25519_COMBINED_PRIVATE_KEY_SIZE; data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = ZT_C25519_COMBINED_PRIVATE_KEY_SIZE;
Utils::copy<ZT_C25519_COMBINED_PRIVATE_KEY_SIZE>(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; return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE;
} else { } else {
data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE] = 0; 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: case P384:
data[ZT_ADDRESS_LENGTH] = (uint8_t) P384; data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
Utils::copy<ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE>(data + ZT_ADDRESS_LENGTH + 1, m_pub); Utils::copy< ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE >(data + ZT_ADDRESS_LENGTH + 1, m_pub);
if ((includePrivate) && (m_hasPrivate)) { if ((includePrivate) && (m_hasPrivate)) {
data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE; data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
Utils::copy<ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE>(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; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
} else { } else {
data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; 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); m_fp.address = Address(data);
unsigned int privlen; unsigned int privlen;
switch ((m_type = (Type) data[ZT_ADDRESS_LENGTH])) { switch ((m_type = (Type)data[ZT_ADDRESS_LENGTH])) {
case C25519: case C25519:
if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1))
return -1; return -1;
Utils::copy<ZT_C25519_COMBINED_PUBLIC_KEY_SIZE>(m_pub, data + ZT_ADDRESS_LENGTH + 1); Utils::copy< ZT_C25519_COMBINED_PUBLIC_KEY_SIZE >(m_pub, data + ZT_ADDRESS_LENGTH + 1);
m_computeHash(); m_computeHash();
privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE]; 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)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE))
return -1; return -1;
m_hasPrivate = true; m_hasPrivate = true;
Utils::copy<ZT_C25519_COMBINED_PRIVATE_KEY_SIZE>(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; return ZT_ADDRESS_LENGTH + 1 + ZT_C25519_COMBINED_PUBLIC_KEY_SIZE + 1 + ZT_C25519_COMBINED_PRIVATE_KEY_SIZE;
} else if (privlen == 0) { } else if (privlen == 0) {
m_hasPrivate = false; 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)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1))
return -1; return -1;
Utils::copy<ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE>(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 m_computeHash(); // this sets the address for P384
if (Address(m_fp.hash) != m_fp.address) // this sanity check is possible with V1 identities if (Address(m_fp.hash) != m_fp.address) // this sanity check is possible with V1 identities
return -1; 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)) if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE))
return -1; return -1;
m_hasPrivate = true; m_hasPrivate = true;
Utils::copy<ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE>(&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; return ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE;
} else if (privlen == 0) { } else if (privlen == 0) {
m_hasPrivate = false; m_hasPrivate = false;
@ -547,7 +538,7 @@ ZT_Identity *ZT_Identity_new(enum ZT_IdentityType type)
return nullptr; return nullptr;
try { try {
ZeroTier::Identity *const id = new ZeroTier::Identity(); ZeroTier::Identity *const id = new ZeroTier::Identity();
id->generate((ZeroTier::Identity::Type) type); id->generate((ZeroTier::Identity::Type)type);
return reinterpret_cast<ZT_Identity *>(id); return reinterpret_cast<ZT_Identity *>(id);
} catch (...) { } catch (...) {
return nullptr; 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) enum ZT_IdentityType ZT_Identity_type(const ZT_Identity *id)
{ {
if (!id) if (!id)
return (ZT_IdentityType) 0; return (ZT_IdentityType)0;
return (enum ZT_IdentityType) reinterpret_cast<const ZeroTier::Identity *>(id)->type(); return (enum ZT_IdentityType)reinterpret_cast<const ZeroTier::Identity *>(id)->type();
} }
char *ZT_Identity_toString(const ZT_Identity *id, char *buf, int capacity, int includePrivate) char *ZT_Identity_toString(const ZT_Identity *id, char *buf, int capacity, int includePrivate)

View file

@ -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: 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: explicit ZT_INLINE MAC(const uint64_t m) noexcept:
@ -63,7 +63,7 @@ public:
*/ */
ZT_INLINE void setTo(const uint8_t b[6]) noexcept 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 ZT_INLINE void copyTo(uint8_t b[6]) const noexcept
{ {
b[0] = (uint8_t) (m_mac >> 40U); b[0] = (uint8_t)(m_mac >> 40U);
b[1] = (uint8_t) (m_mac >> 32U); b[1] = (uint8_t)(m_mac >> 32U);
b[2] = (uint8_t) (m_mac >> 24U); b[2] = (uint8_t)(m_mac >> 24U);
b[3] = (uint8_t) (m_mac >> 16U); b[3] = (uint8_t)(m_mac >> 16U);
b[4] = (uint8_t) (m_mac >> 8U); b[4] = (uint8_t)(m_mac >> 8U);
b[5] = (uint8_t) m_mac; b[5] = (uint8_t)m_mac;
} }
/** /**
@ -100,7 +100,7 @@ public:
*/ */
ZT_INLINE void fromAddress(const Address &ztaddr, uint64_t nwid) noexcept 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 |= ztaddr.toInt(); // a is 40 bits
m ^= ((nwid >> 8U) & 0xffU) << 32U; m ^= ((nwid >> 8U) & 0xffU) << 32U;
m ^= ((nwid >> 16U) & 0xffU) << 24U; m ^= ((nwid >> 16U) & 0xffU) << 24U;
@ -134,7 +134,7 @@ public:
*/ */
static ZT_INLINE unsigned char firstOctetForNetwork(uint64_t nwid) noexcept 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 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) * @return Byte at said position (address interpreted in big-endian order)
*/ */
ZT_INLINE uint8_t operator[](unsigned int i) const noexcept 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 * @return 6, which is the number of bytes in a MAC, for container compliance
@ -152,7 +152,7 @@ public:
{ return 6; } { return 6; }
ZT_INLINE unsigned long hashCode() const noexcept 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 ZT_INLINE operator bool() const noexcept
{ return (m_mac != 0ULL); } { return (m_mac != 0ULL); }
@ -185,12 +185,15 @@ public:
buf[14] = ':'; buf[14] = ':';
buf[15] = Utils::HEXCHARS[(m_mac >> 4U) & 0xfU]; buf[15] = Utils::HEXCHARS[(m_mac >> 4U) & 0xfU];
buf[16] = Utils::HEXCHARS[m_mac & 0xfU]; buf[16] = Utils::HEXCHARS[m_mac & 0xfU];
buf[17] = (char) 0; buf[17] = (char)0;
return buf; return buf;
} }
ZT_INLINE String toString() const 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. * Parse a MAC address in hex format with or without : separators and ignoring non-hex characters.
@ -205,11 +208,11 @@ public:
uint64_t c; uint64_t c;
const char hc = *s++; const char hc = *s++;
if ((hc >= 48) && (hc <= 57)) if ((hc >= 48) && (hc <= 57))
c = (uint64_t) hc - 48; c = (uint64_t)hc - 48;
else if ((hc >= 97) && (hc <= 102)) else if ((hc >= 97) && (hc <= 102))
c = (uint64_t) hc - 87; c = (uint64_t)hc - 87;
else if ((hc >= 65) && (hc <= 70)) else if ((hc >= 65) && (hc <= 70))
c = (uint64_t) hc - 55; c = (uint64_t)hc - 55;
else continue; else continue;
m_mac = (m_mac << 4U) | c; m_mac = (m_mac << 4U) | c;
} }

View file

@ -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( ZT_ResultCode Node::processBackgroundTasks(
void *tPtr, void *tPtr,
int64_t now, 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<ZeroTier::Node *>(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) enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node, void *tptr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline)
{ {
try { try {

View file

@ -71,6 +71,17 @@ public:
unsigned int frameLength, unsigned int frameLength,
volatile int64_t *nextBackgroundTaskDeadline); 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( ZT_ResultCode processBackgroundTasks(
void *tPtr, void *tPtr,
int64_t now, int64_t now,

View file

@ -53,11 +53,9 @@ namespace Utils {
#define ZT_ROL32(x, r) (((x) << (r)) | ((x) >> (32 - (r)))) #define ZT_ROL32(x, r) (((x) << (r)) | ((x) >> (32 - (r))))
#ifdef ZT_ARCH_X64 #ifdef ZT_ARCH_X64
struct CPUIDRegisters struct CPUIDRegisters
{ {
CPUIDRegisters() noexcept; CPUIDRegisters() noexcept;
bool rdrand; bool rdrand;
bool aes; bool aes;
bool avx; bool avx;
@ -68,7 +66,6 @@ struct CPUIDRegisters
bool sha; bool sha;
bool fsrm; bool fsrm;
}; };
extern const CPUIDRegisters CPUID; extern const CPUIDRegisters CPUID;
#endif #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 static ZT_INLINE bool allZero(const void *const b, unsigned int l) noexcept
{ {
const uint8_t *p = reinterpret_cast<const uint8_t *>(b);
#ifndef ZT_NO_UNALIGNED_ACCESS
while (l >= 8) {
if (*reinterpret_cast<const uint64_t *>(p) != 0)
return false;
p += 8;
l -= 8;
}
#endif
for (unsigned int i = 0; i < l; ++i) { for (unsigned int i = 0; i < l; ++i) {
if (reinterpret_cast<const uint8_t *>(b)[i] != 0) if (reinterpret_cast<const uint8_t *>(p)[i] != 0)
return false; return false;
} }
return true; return true;
} }

View file

@ -1459,15 +1459,6 @@ typedef int (*ZT_StateGetFunction)(
/** /**
* Function to send a ZeroTier packet out over the physical wire (L2/L3) * 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 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 * If the local socket is -1, the packet should be sent out from all
* bound local sockets or a random bound local socket. * bound local sockets or a random bound local socket.
@ -1490,6 +1481,29 @@ typedef int (*ZT_WirePacketSendFunction)(
unsigned int, /* Packet length */ unsigned int, /* Packet length */
unsigned int); /* TTL or 0 to use default */ 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 * Function to check whether a path should be used for ZeroTier traffic
* *
@ -1568,6 +1582,11 @@ struct ZT_Node_Callbacks
*/ */
ZT_WirePacketSendFunction wirePacketSendFunction; ZT_WirePacketSendFunction wirePacketSendFunction;
/**
* RECOMMENDED: Function to initiate HTTP requests
*/
ZT_HTTPRequestFunction httpRequestFunction;
/** /**
* REQUIRED: Function to inject frames into a virtual network's TAP * 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, int isZtBuffer,
volatile int64_t *nextBackgroundTaskDeadline); 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 * Perform periodic background operations
* *