/* * 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. */ /****/ #include "Locator.hpp" #include "Identity.hpp" #include namespace ZeroTier { Locator::Locator(const char *const str) noexcept { if (!fromString(str)) { m_ts = 0; m_signer.zero(); m_endpoints.clear(); m_signature.clear(); } } bool Locator::add(const Endpoint &ep) { if (m_endpoints.size() < ZT_LOCATOR_MAX_ENDPOINTS) { if (std::find(m_endpoints.begin(), m_endpoints.end(), ep) == m_endpoints.end()) m_endpoints.push_back(ep); return true; } return false; } bool Locator::sign(const int64_t ts, const Identity &id) noexcept { m_ts = ts; m_signer = id.fingerprint(); std::sort(m_endpoints.begin(), m_endpoints.end()); uint8_t signdata[ZT_LOCATOR_MARSHAL_SIZE_MAX]; const unsigned int signlen = marshal(signdata, true); const unsigned int siglen = id.sign(signdata, signlen, m_signature.data(), m_signature.capacity()); if (siglen == 0) return false; m_signature.unsafeSetSize(siglen); return true; } bool Locator::verify(const Identity &id) const noexcept { try { if ((m_ts > 0) && (m_signer == id.fingerprint())) { uint8_t signdata[ZT_LOCATOR_MARSHAL_SIZE_MAX]; const unsigned int signlen = marshal(signdata, true); return id.verify(signdata, signlen, m_signature.data(), m_signature.size()); } } catch (...) {} // fail verify on any unexpected exception return false; } char *Locator::toString(char s[ZT_LOCATOR_STRING_SIZE_MAX]) const noexcept { static_assert(ZT_LOCATOR_STRING_SIZE_MAX > ((((ZT_LOCATOR_MARSHAL_SIZE_MAX / 5) + 1) * 8) + ZT_ADDRESS_LENGTH_HEX + 1), "overflow"); uint8_t bin[ZT_LOCATOR_MARSHAL_SIZE_MAX]; Address(m_signer.address).toString(s); s[ZT_ADDRESS_LENGTH_HEX] = '@'; Utils::b32e(bin, marshal(bin, false), s + (ZT_ADDRESS_LENGTH_HEX + 1), ZT_LOCATOR_STRING_SIZE_MAX - (ZT_ADDRESS_LENGTH_HEX + 1)); return s; } bool Locator::fromString(const char *s) noexcept { if (!s) return false; if (strlen(s) < (ZT_ADDRESS_LENGTH_HEX + 1)) return false; uint8_t bin[ZT_LOCATOR_MARSHAL_SIZE_MAX]; const int bl = Utils::b32d(s + (ZT_ADDRESS_LENGTH_HEX + 1), bin, ZT_LOCATOR_MARSHAL_SIZE_MAX); if ((bl <= 0) || (bl > ZT_LOCATOR_MARSHAL_SIZE_MAX)) return false; return unmarshal(bin, bl) > 0; } int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX], const bool excludeSignature) const noexcept { Utils::storeBigEndian(data, (uint64_t) m_ts); int p = 8; int l = m_signer.marshal(data + p); if (l <= 0) return -1; p += l; Utils::storeBigEndian(data + p, (uint16_t) m_endpoints.size()); p += 2; for (Vector::const_iterator e(m_endpoints.begin());e != m_endpoints.end();++e) { l = e->marshal(data + p); if (l <= 0) return -1; p += l; } Utils::storeAsIsEndian(data + p, 0); // length of meta-data, currently always 0 p += 2; if (!excludeSignature) { Utils::storeBigEndian(data + p, (uint16_t) m_signature.size()); p += 2; Utils::copy(data + p, m_signature.data(), m_signature.size()); p += (int) m_signature.size(); } return p; } int Locator::unmarshal(const uint8_t *data, const int len) noexcept { if (unlikely(len < 8)) return -1; m_ts = (int64_t) Utils::loadBigEndian(data); int p = 8; int l = m_signer.unmarshal(data + p, len - p); if (l <= 0) return -1; p += l; if (unlikely(p + 2) > len) return -1; unsigned int endpointCount = Utils::loadBigEndian(data + p); p += 2; if (unlikely(endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)) return -1; m_endpoints.resize(endpointCount); m_endpoints.shrink_to_fit(); for (unsigned int i = 0;i < endpointCount;++i) { l = m_endpoints[i].unmarshal(data + p, len - p); if (l <= 0) return -1; p += l; } if (unlikely((p + 2) > len)) return -1; p += 2 + (int) Utils::loadBigEndian(data + p); if (unlikely((p + 2) > len)) return -1; const unsigned int siglen = Utils::loadBigEndian(data + p); p += 2; if (unlikely((siglen > ZT_SIGNATURE_BUFFER_SIZE) || ((p + (int) siglen) > len))) return -1; m_signature.unsafeSetSize(siglen); Utils::copy(m_signature.data(), data + p, siglen); p += siglen; if (unlikely(p > len)) return -1; return p; } } // namespace ZeroTier extern "C" { ZT_Locator *ZT_Locator_create( int64_t ts, const ZT_Endpoint *endpoints, unsigned int endpointCount, const ZT_Identity *signer) { try { if ((ts <= 0) || (!endpoints) || (endpointCount == 0) || (!signer)) return nullptr; ZeroTier::Locator *loc = new ZeroTier::Locator(); for (unsigned int i = 0;i < endpointCount;++i) loc->add(reinterpret_cast(endpoints)[i]); if (!loc->sign(ts, *reinterpret_cast(signer))) { delete loc; return nullptr; } return reinterpret_cast(loc); } catch (...) { return nullptr; } } ZT_Locator *ZT_Locator_fromString(const char *str) { try { if (!str) return nullptr; ZeroTier::Locator *loc = new ZeroTier::Locator(); if (!loc->fromString(str)) { delete loc; return nullptr; } return reinterpret_cast(loc); } catch ( ... ) { return nullptr; } } ZT_Locator *ZT_Locator_unmarshal( const void *data, unsigned int len) { try { if ((!data) || (len == 0)) return nullptr; ZeroTier::Locator *loc = new ZeroTier::Locator(); if (loc->unmarshal(reinterpret_cast(data), (int) len) <= 0) { delete loc; return nullptr; } return reinterpret_cast(loc); } catch (...) { return nullptr; } } int ZT_Locator_marshal(const ZT_Locator *loc, void *buf, unsigned int bufSize) { if ((!loc) || (bufSize < ZT_LOCATOR_MARSHAL_SIZE_MAX)) return -1; return reinterpret_cast(loc)->marshal(reinterpret_cast(buf), (int) bufSize); } char *ZT_Locator_toString( const ZT_Locator *loc, char *buf, int capacity) { if ((!loc) || (capacity < ZT_LOCATOR_STRING_SIZE_MAX)) return nullptr; return reinterpret_cast(loc)->toString(buf); } const ZT_Fingerprint *ZT_Locator_fingerprint(const ZT_Locator *loc) { if (!loc) return nullptr; return (ZT_Fingerprint *) (&(reinterpret_cast(loc)->signer())); } int64_t ZT_Locator_timestamp(const ZT_Locator *loc) { if (!loc) return 0; return reinterpret_cast(loc)->timestamp(); } unsigned int ZT_Locator_endpointCount(const ZT_Locator *loc) { return (loc) ? (unsigned int) (reinterpret_cast(loc)->endpoints().size()) : 0; } const ZT_Endpoint *ZT_Locator_endpoint(const ZT_Locator *loc, const unsigned int ep) { if (!loc) return nullptr; if (ep >= (unsigned int) (reinterpret_cast(loc)->endpoints().size())) return nullptr; return reinterpret_cast(&(reinterpret_cast(loc)->endpoints()[ep])); } int ZT_Locator_verify(const ZT_Locator *loc, const ZT_Identity *signer) { if ((!loc) || (!signer)) return 0; return reinterpret_cast(loc)->verify(*reinterpret_cast(signer)) ? 1 : 0; } void ZT_Locator_delete(ZT_Locator *loc) { if (loc) delete reinterpret_cast(loc); } } // C API functions