/* * 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 "Endpoint.hpp" namespace ZeroTier { Endpoint::Endpoint(const InetAddress &sa,const Protocol proto) noexcept { switch (sa.family()) { case AF_INET: _t = TYPE_INETADDR_V4; break; case AF_INET6: _t = TYPE_INETADDR_V6; default: _t = TYPE_NIL; return; } asInetAddress(_v.in.sa) = sa; _v.in.proto = (uint8_t)proto; } bool Endpoint::operator==(const Endpoint &ep) const noexcept { if (_t == ep._t) { switch(_t) { default: return true; case TYPE_ZEROTIER: return ((_v.zt.address == ep._v.zt.address)&&(memcmp(_v.zt.hash,ep._v.zt.hash,sizeof(_v.zt.hash)) == 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 ((asInetAddress(_v.in.sa) == asInetAddress(ep._v.in.sa))&&(_v.in.proto == ep._v.in.proto)); } } return false; } bool Endpoint::operator<(const Endpoint &ep) const noexcept { if ((int)_t < (int)ep._t) { return true; } else if (_t == ep._t) { int ncmp; switch(_t) { case TYPE_ZEROTIER: return (_v.zt.address < ep._v.zt.address) ? true : ((_v.zt.address == ep._v.zt.address)&&(memcmp(_v.zt.hash,ep._v.zt.hash,sizeof(_v.zt.hash)) < 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 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 ((_v.in.proto < ep._v.in.proto)||((_v.in.proto == ep._v.in.proto)&&(asInetAddress(_v.in.sa) < asInetAddress(ep._v.in.sa)))); default: return false; } } return false; } int Endpoint::marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const noexcept { int p; data[0] = (uint8_t)_t; Utils::storeBigEndian(data + 1,(uint16_t)_l[0]); Utils::storeBigEndian(data + 3,(uint16_t)_l[1]); Utils::storeBigEndian(data + 5,(uint16_t)_l[2]); switch(_t) { case TYPE_ZEROTIER: data[7] = (uint8_t)(_v.zt.address >> 32U); data[8] = (uint8_t)(_v.zt.address >> 24U); data[9] = (uint8_t)(_v.zt.address >> 16U); data[10] = (uint8_t)(_v.zt.address >> 8U); data[11] = (uint8_t)_v.zt.address; memcpy(data + 12,_v.zt.hash,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) break; ++p; if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1)) return -1; } data[p++] = (uint8_t)(_v.dns.port >> 8U); data[p++] = (uint8_t)_v.dns.port; return p; case TYPE_URL: p = 7; for (;;) { if ((data[p] = (uint8_t)_v.url[p-1]) == 0) break; ++p; if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1)) return -1; } return p; case TYPE_ETHERNET: data[7] = (uint8_t)(_v.eth >> 40U); data[8] = (uint8_t)(_v.eth >> 32U); data[9] = (uint8_t)(_v.eth >> 24U); data[10] = (uint8_t)(_v.eth >> 16U); data[11] = (uint8_t)(_v.eth >> 8U); data[12] = (uint8_t)_v.eth; return 13; case TYPE_INETADDR_V4: case TYPE_INETADDR_V6: p = 7 + asInetAddress(_v.in.sa).marshal(data + 7); if (p <= 7) return -1; data[p++] = _v.in.proto; return p; default: data[0] = (uint8_t)TYPE_NIL; return 7; } } int Endpoint::unmarshal(const uint8_t *restrict data,const int len) noexcept { if (len < 7) return -1; int p; _t = (Type)data[0]; _l[0] = (int)Utils::loadBigEndian(data + 1); _l[1] = (int)Utils::loadBigEndian(data + 3); _l[2] = (int)Utils::loadBigEndian(data + 5); switch(_t) { case TYPE_NIL: return 7; case TYPE_ZEROTIER: if (len < (12 + ZT_IDENTITY_HASH_SIZE)) return -1; _v.zt.address = ((uint64_t)data[7]) << 32U; _v.zt.address |= ((uint64_t)data[8]) << 24U; _v.zt.address |= ((uint64_t)data[9]) << 16U; _v.zt.address |= ((uint64_t)data[10]) << 8U; _v.zt.address |= (uint64_t)data[11]; memcpy(_v.zt.hash,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 = 7 + asInetAddress(_v.in.sa).unmarshal(data + 7,len - 7); if ((p <= 7)||(p >= len)) return -1; _v.in.proto = data[p++]; return p; default: // 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; p = 9 + (int)Utils::loadBigEndian(data + 7); return (p > len) ? -1 : p; } } } // namespace ZeroTier