diff --git a/node/Endpoint.hpp b/node/Endpoint.hpp index 1f01c805a..56ad148d3 100644 --- a/node/Endpoint.hpp +++ b/node/Endpoint.hpp @@ -37,34 +37,37 @@ public: enum Type { NIL = 0, // NIL value - SOCKADDR = 1, // InetAddress + INETADDR = 1, // InetAddress (v4 or v6) DNSNAME = 2, // DNS name and port that resolves to InetAddress ZEROTIER = 3, // ZeroTier Address (for relaying and meshy behavior) - URL = 4 // URL for http/https/ws/etc. (not implemented yet) + URL = 4, // URL for http/https/ws/etc. (not implemented yet) + ETHERNET = 5 // 48-bit LAN-local Ethernet address }; inline Endpoint() { memset(reinterpret_cast(this),0,sizeof(Endpoint)); } - inline Endpoint(const InetAddress &sa) : _t(SOCKADDR) { _v.sa = sa; } + inline Endpoint(const InetAddress &sa) : _t(INETADDR) { _v.sa = sa; } inline Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) : _t(ZEROTIER) { _v.zt.a = zt.toInt(); memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE); } inline Endpoint(const char *name,const int port) : _t(DNSNAME) { Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); _v.dns.port = port; } inline Endpoint(const char *url) : _t(URL) { Utils::scopy(_v.url,sizeof(_v.url),url); } - inline const InetAddress *sockaddr() const { return (_t == SOCKADDR) ? reinterpret_cast(&_v.sa) : nullptr; } + inline const InetAddress *sockaddr() const { return (_t == INETADDR) ? reinterpret_cast(&_v.sa) : nullptr; } inline const char *dnsName() const { return (_t == DNSNAME) ? _v.dns.name : nullptr; } inline const int dnsPort() const { return (_t == DNSNAME) ? _v.dns.port : -1; } inline Address ztAddress() const { return (_t == ZEROTIER) ? Address(_v.zt.a) : Address(); } inline const uint8_t *ztIdentityHash() const { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; } inline const char *url() const { return (_t == URL) ? _v.url : nullptr; } + inline MAC ethernet() const { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); } inline Type type() const { return _t; } - inline unsigned int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) + static inline int marshalSizeMax() { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; } + inline int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) { - unsigned int p; + int p; switch(_t) { - case SOCKADDR: - data[0] = (uint8_t)SOCKADDR; + case INETADDR: + data[0] = (uint8_t)INETADDR; return 1 + reinterpret_cast(&_v.sa)->marshal(data+1); case DNSNAME: data[0] = (uint8_t)DNSNAME; @@ -74,7 +77,7 @@ public: break; ++p; if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1)) - return 0; + return -1; } data[p++] = (uint8_t)((_v.dns.port >> 8) & 0xff); data[p++] = (uint8_t)(_v.dns.port & 0xff); @@ -96,45 +99,55 @@ public: break; ++p; if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1)) - return 0; + return -1; } return p; + case ETHERNET: + data[0] = (uint8_t)ETHERNET; + data[1] = (uint8_t)((_v.eth >> 40) & 0xff); + data[2] = (uint8_t)((_v.eth >> 32) & 0xff); + data[3] = (uint8_t)((_v.eth >> 24) & 0xff); + data[4] = (uint8_t)((_v.eth >> 16) & 0xff); + data[5] = (uint8_t)((_v.eth >> 8) & 0xff); + data[6] = (uint8_t)(_v.eth & 0xff); + return 7; default: data[0] = (uint8_t)NIL; return 1; } } - - inline bool unmarshal(const uint8_t *restrict data,const unsigned int len) + inline int unmarshal(const uint8_t *restrict data,const int len) { - if (len == 0) - return false; - unsigned int p; + if (len <= 0) + return -1; + int p; switch((Type)data[0]) { case NIL: _t = NIL; - return true; - case SOCKADDR: - _t = SOCKADDR; + return 1; + case INETADDR: + _t = INETADDR; return reinterpret_cast(&_v.sa)->unmarshal(data+1,len-1); case DNSNAME: if (len < 4) - return false; + return -1; _t = DNSNAME; p = 1; for (;;) { - if ((_v.dns.name[p-1] = (char)data[p]) == 0) + if ((_v.dns.name[p-1] = (char)data[p]) == 0) { + ++p; break; + } ++p; if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2))) return; } _v.dns.port = ((int)data[p++]) << 8; _v.dns.port |= (int)data[p++]; - return true; + return p; case ZEROTIER: - if (len != (ZT_IDENTITY_HASH_SIZE + 6)) - return false; + if (len < (ZT_IDENTITY_HASH_SIZE + 6)) + return -1; _t = ZEROTIER; _v.zt.a = ((uint64_t)data[1]) << 32; _v.zt.a |= ((uint64_t)data[2]) << 24; @@ -142,20 +155,33 @@ public: _v.zt.a |= ((uint64_t)data[4]) << 8; _v.zt.a |= (uint64_t)data[5]; memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE); - return true; + return (ZT_IDENTITY_HASH_SIZE + 6); case URL: if (len < 2) - return false; + return -1; _t = URL; p = 1; for (;;) { - if ((_v.url[p-1] = (char)data[p]) == 0) + if ((_v.url[p-1] = (char)data[p]) == 0) { + ++p; break; + } ++p; if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len)) - return; + return -1; } - return true; + return p; + case ETHERNET: + if (len < 7) + return -1; + _t = ZEROTIER; + _v.eth = ((uint64_t)data[1]) << 40; + _v.eth |= ((uint64_t)data[2]) << 32; + _v.eth |= ((uint64_t)data[3]) << 24; + _v.eth |= ((uint64_t)data[4]) << 16; + _v.eth |= ((uint64_t)data[5]) << 8; + _v.eth |= (uint64_t)data[6]; + return 7; } return false; } @@ -173,6 +199,7 @@ private: uint8_t idh[ZT_IDENTITY_HASH_SIZE]; } zt; char url[ZT_ENDPOINT_MAX_NAME_SIZE]; + uint64_t eth; } _v; }; diff --git a/node/Identity.hpp b/node/Identity.hpp index 9003bd930..8fa905b60 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -27,6 +27,11 @@ #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 + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE) +#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) + +#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) + namespace ZeroTier { /** @@ -241,6 +246,87 @@ public: */ inline const Address &address() const { return _address; } + static inline int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } + inline int marshal(uint8_t restrict data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const + { + _address.copyTo(data,ZT_ADDRESS_LENGTH); + 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); + } + 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 + 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_C25519_PRIVATE_KEY_LEN + ZT_ECC384_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); + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1); + } + 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 + 1] = 0; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2); + + } + return -1; + } + inline int unmarshal(const uint8_t *restrict data,const int len) + { + if (len < (ZT_ADDRESS_LENGTH + 1)) + return -1; + 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); + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN); + } else if (privlen == 0) { + _hasPrivate = false; + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1); + } + break; + + case P384: + if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2)) + 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 + 1)) + 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); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE]; + if (len < (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))) + return -1; + return (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)); + } else if (privlen == 0) { + _hasPrivate = false; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2); + } + break; + + } + return -1; + } + /** * Serialize this identity (binary) * @@ -274,7 +360,7 @@ public: } else { b.append((uint8_t)0); } - b.append((uint16_t)0); // size of additional fields (should have included such a thing in v0!) + b.append((uint8_t)0); // size of additional fields (should have included such a thing in v0!) break; } @@ -332,7 +418,7 @@ public: } else { _hasPrivate = false; } - p += b.template at(p) + 2; + p += b.template at(p) + 2; break; default: diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 9444b1bbe..8330c7844 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -489,7 +489,8 @@ struct InetAddress : public sockaddr_storage */ inline operator bool() const { return (ss_family != 0); } - inline unsigned int marshal(uint8_t restrict data[20]) const + static inline int marshalSizeMax() { return 19; } + inline int marshal(uint8_t restrict data[19]) const { switch(ss_family) { case AF_INET: @@ -515,37 +516,36 @@ struct InetAddress : public sockaddr_storage return 1; } } - - inline bool unmarshal(const uint8_t *restrict data,const unsigned int len) + inline int unmarshal(const uint8_t *restrict data,const int len) { - if (len) { - memset(this,0,sizeof(InetAddress)); - switch(data[0]) { - case 0: - return true; - case 4: - if (len != 7) - return false; - reinterpret_cast(this)->sin_family = AF_INET; - reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[0] = data[1]; - reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[1] = data[2]; - reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[2] = data[3]; - reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[3] = data[4]; - reinterpret_cast(this)->sin_port = Utils::hton((((uint16_t)data[5]) << 8) | (uint16_t)data[6]); - return true; - case 6: - if (len != 19) - return false; - reinterpret_cast(this)->sin6_family = AF_INET6; - for(int i=0;i<16;i++) - (reinterpret_cast(this)->sin6_addr.s6_addr)[i] = data[i+1]; - reinterpret_cast(this)->sin6_port = Utils::hton((((uint16_t)data[17]) << 8) | (uint16_t)data[18]); - return true; - default: - return false; - } + if (len <= 0) + return -1; + switch(data[0]) { + case 0: + return 1; + case 4: + if (len < 7) + return -1; + memset(this,0,sizeof(InetAddress)); + reinterpret_cast(this)->sin_family = AF_INET; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[0] = data[1]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[1] = data[2]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[2] = data[3]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[3] = data[4]; + reinterpret_cast(this)->sin_port = Utils::hton((((uint16_t)data[5]) << 8) | (uint16_t)data[6]); + return 7; + case 6: + if (len < 19) + return -1; + memset(this,0,sizeof(InetAddress)); + reinterpret_cast(this)->sin6_family = AF_INET6; + for(int i=0;i<16;i++) + (reinterpret_cast(this)->sin6_addr.s6_addr)[i] = data[i+1]; + reinterpret_cast(this)->sin6_port = Utils::hton((((uint16_t)data[17]) << 8) | (uint16_t)data[18]); + return 19; + default: + return -1; } - return false; } template diff --git a/node/Locator.hpp b/node/Locator.hpp index 0de1f8af4..676ff3f01 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -19,6 +19,7 @@ #include "Constants.hpp" #include "Endpoint.hpp" +#include "Identity.hpp" #define ZT_LOCATOR_MAX_ENDPOINTS 8 @@ -29,26 +30,178 @@ namespace ZeroTier { /** * Signed information about a node's location on the network * - * A locator is a signed record that contains information about where a node - * may be found. It can contain static physical addresses or virtual ZeroTier - * addresses of nodes that can forward to the target node. Locator records - * can be stored in signed DNS TXT record sets, in LF by roots, in caches, - * etc. + * A locator contains long-lived endpoints for a node such as IP/port pairs, + * URLs, or other nodes, and is signed by the node it describes. */ class Locator { - friend class SharedPtr; - public: - inline Locator() : _ts(0),_signatureLength(0) {} + inline Locator() : _ts(0),_at(nullptr),_signatureLength(0) {} + inline ~Locator() { delete [] _at; } + + inline Locator(const Locator &l) : + _ts(l._ts), + _id(l._id), + _at((l._endpointCount > 0) ? new Endpoint[l._endpointCount] : nullptr), + _endpointCount(l._endpointCount), + _signatureLength(l._signatureLength) + { + for(unsigned int i=0;i<_endpointCount;++i) + _at[i] = l._at[i]; + memcpy(_signature,l._signature,_signatureLength); + } + + inline Locator &operator=(const Locator &l) + { + _ts = l._ts; + _id = l._id; + delete [] _at; + _at = (l._endpointCount > 0) ? new Endpoint[l._endpointCount] : nullptr; + for(unsigned int i=0;i ZT_LOCATOR_MAX_ENDPOINTS)||(!id.hasPrivate())) + return false; + _ts = ts; + _id = id; + if (_at) + delete [] _at; + _at = new Endpoint[endpointCount]; + for(unsigned int i=0;i ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)) + return false; + uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX]; + const unsigned int signLen = marshal(signData,true); + return _id.verify(signData,signLen,_signature,_signatureLength); + } + + inline operator bool() const { return (_ts != 0); } + + static inline int marshalSizeMax() { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } + inline int marshal(uint8_t restrict data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const + { + if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)) + return -1; + + data[0] = (uint8_t)((uint64_t)_ts >> 56); + data[1] = (uint8_t)((uint64_t)_ts >> 48); + data[2] = (uint8_t)((uint64_t)_ts >> 40); + data[3] = (uint8_t)((uint64_t)_ts >> 32); + data[4] = (uint8_t)((uint64_t)_ts >> 24); + data[5] = (uint8_t)((uint64_t)_ts >> 16); + data[6] = (uint8_t)((uint64_t)_ts >> 8); + data[7] = (uint8_t)((uint64_t)_ts); + + int p = _id.marshal(data + 8,false); + if (p <= 0) + return -1; + p += 8; + + data[p++] = (uint8_t)_endpointCount; + for(unsigned int i=0;i<_endpointCount;++i) { + int tmp = _at[i].marshal(data + p); + if (tmp < 0) + return -1; + p += tmp; + } + + if (!excludeSignature) { + data[p++] = (uint8_t)(_signatureLength >> 8); + data[p++] = (uint8_t)_signatureLength; + memcpy(data + p,_signature,_signatureLength); + p += _signatureLength; + } + + return p; + } + inline int unmarshal(const uint8_t *restrict data,const int len) + { + if (len <= 8) + return -1; + + uint64_t ts = ((uint64_t)data[0] << 56); + ts |= ((uint64_t)data[1] << 48); + ts |= ((uint64_t)data[2] << 40); + ts |= ((uint64_t)data[3] << 32); + ts |= ((uint64_t)data[4] << 24); + ts |= ((uint64_t)data[5] << 16); + ts |= ((uint64_t)data[6] << 8); + ts |= (uint64_t)data[7]; + _ts = (int64_t)ts; + + int p = _id.unmarshal(data + 8,len - 8); + if (p <= 0) + return -1; + p += 8; + + if (p >= len) + return -1; + unsigned int ec = (int)data[p++]; + if (ec > ZT_LOCATOR_MAX_ENDPOINTS) + return -1; + if (_at) + delete [] _at; + _at = new Endpoint[ec]; + for(int i=0;i len) + return -1; + unsigned int sl = data[p++]; + sl <<= 8; + sl |= data[p++]; + if (sl > ZT_SIGNATURE_BUFFER_SIZE) + return -1; + _signatureLength = sl; + if ((p + sl) > len) + return -1; + memcpy(_signature,data + p,sl); + p += (int)sl; + + return p; } private: