diff --git a/node/Buffer.hpp b/node/Buffer.hpp index c5d625deb..0b1715925 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -300,6 +300,23 @@ public: append(s.data(),(unsigned int)s.length()); } + /** + * Append a C string including null termination byte + * + * @param s C string + * @throws std::out_of_range Attempt to append beyond capacity + */ + inline void appendCString(const char *s) + throw(std::out_of_range) + { + for(;;) { + if (_l >= C) + throw std::out_of_range("Buffer: append beyond capacity"); + if (!(_b[_l++] = *(s++))) + break; + } + } + /** * Append a buffer * diff --git a/node/Network.cpp b/node/Network.cpp index e7b996618..00ae795a2 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -181,12 +181,23 @@ bool Network::applyConfiguration(const NetworkConfig &conf) int Network::setConfiguration(const void *confBytes,unsigned int confLen,bool saveToDisk) { try { - if (!confLen) + if (confLen <= 1) return 0; NetworkConfig newConfig; - if (reinterpret_cast<const uint8_t *>(confBytes)[0] == ZT_NETWORKCONFIG_V2_MARKER_BYTE) { - Buffer<8194> tmp(confBytes,confLen); + + // Find the length of any string-serialized old-style Dictionary, + // including its terminating NULL (if any). If this is before + // the end of the config, that tells us there is a new-style + // binary config which is preferred. + unsigned int dictLen = 0; + while (dictLen < confLen) { + if (!(reinterpret_cast<const uint8_t *>(confBytes)[dictLen++])) + break; + } + + if (dictLen < (confLen - 2)) { + Buffer<8194> tmp(reinterpret_cast<const uint8_t *>(confBytes) + dictLen,confLen - dictLen); newConfig.deserialize(tmp,0); } else { #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF @@ -422,15 +433,15 @@ public: _now(renv->node->now()), _controller(nw->controller()), _network(nw), + _anchors(nw->config().anchors()), _rootAddresses(renv->topology->rootAddresses()) {} inline void operator()(Topology &t,const SharedPtr<Peer> &p) { - if ( - (_network->_isAllowed(p)) || - (p->address() == _controller) || - (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) - ) { + if ( (_network->_isAllowed(p)) || // FIXME: this causes multicast LIKEs for public networks to get spammed + (p->address() == _controller) || + (std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end()) || + (std::find(_anchors.begin(),_anchors.end(),p->address()) != _anchors.end()) ) { peers.push_back(p); } } @@ -439,14 +450,15 @@ private: const uint64_t _now; const Address _controller; Network *const _network; + const std::vector<Address> _anchors; const std::vector<Address> _rootAddresses; }; void Network::_announceMulticastGroups() { // Assumes _lock is locked + std::vector<MulticastGroup> allMulticastGroups(_allMulticastGroups()); _MulticastAnnounceAll gpfunc(RR,this); RR->topology->eachPeer<_MulticastAnnounceAll &>(gpfunc); - std::vector<MulticastGroup> allMulticastGroups(_allMulticastGroups()); for(std::vector< SharedPtr<Peer> >::const_iterator i(gpfunc.peers.begin());i!=gpfunc.peers.end();++i) _announceMulticastGroupsTo(*i,allMulticastGroups); } diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 15778aede..a03c1faf9 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -25,6 +25,7 @@ #include <vector> #include <stdexcept> +#include <algorithm> #include "../include/ZeroTierOne.h" @@ -40,13 +41,6 @@ #include <string> #endif -/** - * First byte of V2 binary-serialized network configs - * - * This will never begin a Dictionary, so it serves to distinguish. - */ -#define ZT_NETWORKCONFIG_V2_MARKER_BYTE 0x00 - /** * Flag: allow passive bridging (experimental) */ @@ -68,9 +62,9 @@ #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL /** - * This device is allowed to send packets from any Ethernet MAC, including ZeroTier-reserved ones + * An anchor is a device that is willing to be one and has been online/stable for a long time on this network */ -#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_IMPOSTOR 0x0000040000000000ULL +#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL namespace ZeroTier { @@ -302,6 +296,19 @@ public: return r; } + /** + * @return ZeroTier addresses of "anchor" devices on this network + */ + inline std::vector<Address> anchors() const + { + std::vector<Address> r; + for(unsigned int i=0;i<_specialistCount;++i) { + if ((_specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0) + r.push_back(Address(_specialists[i])); + } + return r; + } + /** * Look up a static physical address for a given ZeroTier address * @@ -321,6 +328,8 @@ public: } /** + * This gets network preferred relays with their static physical address if one is defined + * * @return Network-preferred relays for this network (if none, only roots will be used) */ inline std::vector<Relay> relays() const @@ -393,9 +402,7 @@ public: template<unsigned int C> inline void serialize(Buffer<C> &b) const { - b.append((uint8_t)ZT_NETWORKCONFIG_V2_MARKER_BYTE); - - b.append((uint16_t)0); // version + b.append((uint16_t)1); // version b.append((uint64_t)_nwid); b.append((uint64_t)_timestamp); @@ -517,9 +524,7 @@ public: unsigned int p = startAt; - if (b[p++] != ZT_NETWORKCONFIG_V2_MARKER_BYTE) - throw std::invalid_argument("unrecognized format"); - if (b.template at<uint16_t>(p) != 0) + if (b.template at<uint16_t>(p) != 1) throw std::invalid_argument("unrecognized version"); p += 2; @@ -532,9 +537,8 @@ public: _type = (ZT_VirtualNetworkType)b[p++]; unsigned int nl = (unsigned int)b[p++]; - if (nl > ZT_MAX_NETWORK_SHORT_NAME_LENGTH) - nl = ZT_MAX_NETWORK_SHORT_NAME_LENGTH; - memcpy(_name,b.field(p,nl),nl); + memcpy(_name,b.field(p,nl),std::max(nl,(unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH)); + p += nl; // _name will always be null terminated since field size is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1 _specialistCount = (unsigned int)b.template at<uint16_t>(p); p += 2; diff --git a/node/NetworkConfigRequestMetaData.hpp b/node/NetworkConfigRequestMetaData.hpp index 5bf8bac4f..3756d0d86 100644 --- a/node/NetworkConfigRequestMetaData.hpp +++ b/node/NetworkConfigRequestMetaData.hpp @@ -25,6 +25,9 @@ #include "Constants.hpp" #include "NetworkConfig.hpp" +#include "Buffer.hpp" + +#include "../version.h" #ifdef ZT_SUPPORT_OLD_STYLE_NETCONF #include <string> @@ -33,27 +36,122 @@ namespace ZeroTier { +/** + * Network configuration request meta data + */ class NetworkConfigRequestMetaData { public: - NetworkConfigRequestMetaData() : - _vendor(0), - _majorVersion(0), - _minorVersion(0), - _revision(0), - _buildNo(0), - _flags(0) + NetworkConfigRequestMetaData() { + memset(this,0,sizeof(NetworkConfigRequestMetaData)); } -protected: - unsigned int _vendor; - unsigned int _majorVersion; - unsigned int _minorVersion; - unsigned int _revision; - unsigned int _buildNo; - unsigned int _flags; - char _passcode[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1]; + template<unsigned int C> + inline void serialize(Buffer<C> &b) const + { + // Unlike network config we always send the old fields. Newer network + // controllers will detect the presence of the new serialized data by + // detecting extra data after the terminating NULL. But always sending + // these maintains backward compatibility with old controllers. + b.appendCString("majv="ZEROTIER_ONE_VERSION_MAJOR_S"\nminv="ZEROTIER_ONE_VERSION_MINOR_S"\nrevv="ZEROTIER_ONE_VERSION_REVISION_S"\n"); + + b.append((uint16_t)1); // version + + b.append((uint64_t)buildId); + b.append((uint64_t)flags); + b.append((uint16_t)vendor); + b.append((uint16_t)platform); + b.append((uint16_t)architecture); + b.append((uint16_t)majorVersion); + b.append((uint16_t)minorVersion); + b.append((uint16_t)revision); + + unsigned int tl = (unsigned int)strlen(_auth); + if (tl > 255) tl = 255; // sanity check + b.append((uint8_t)tl); + b.append((const void *)auth,tl); + + b.append((uint16_t)0); // extended bytes, currently 0 since unused + } + + template<unsigned int C> + inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0) + { + memset(this,0,sizeof(NetworkConfigRequestMetaData)); + + unsigned int p = startAt; + + // Seek past old style meta-data + while (b[p]) ++p; + + if (b.template at<uint16_t>(p) != 1) + throw std::invalid_argument("unrecognized version"); + p += 2; + + buildId = b.template at<uint64_t>(p); p += 8; + flags = b.template at<uint64_t>(p); p += 8; + vendor = (ZT_Vendor)b.template at<uint16_t>(p); p += 2; + platform = (ZT_Platform)b.template at<uint16_t>(p); p += 2; + architecture = (ZT_Architecture)b.template at<uint16_t>(p); p += 2; + majorVersion = b.template at<uint16_t>(p); p += 2; + minorVersion = b.template at<uint16_t>(p); p += 2; + revision = b.template at<uint16_t>(p); p += 2; + + unsigned int tl = (unsigned int)b[p++]; + memcpy(auth,b.field(p,tl),std::max(tl,(unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH)); + // auth[] is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1 and so will always end up null-terminated since we zeroed the structure + p += tl; + + p += b.template at<uint16_t>(p) + 2; + + return (p - startAt); + } + + /** + * Build ID (currently unused, must be 0) + */ + uint64_t buildId; + + /** + * Flags (currently unused, must be 0) + */ + uint64_t flags; + + /** + * ZeroTier vendor or 0 for unspecified + */ + ZT_Vendor vendor; + + /** + * ZeroTier platform or 0 for unspecified + */ + ZT_Platform platform; + + /** + * ZeroTier architecture or 0 for unspecified + */ + ZT_Architecture architecture; + + /** + * ZeroTier software major version + */ + unsigned int majorVersion; + + /** + * ZeroTier software minor version + */ + unsigned int minorVersion; + + /** + * ZeroTier software revision + */ + unsigned int revision; + + /** + * Authentication data (e.g. bearer=<token>) + */ + char auth[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1]; }; } // namespace ZeroTier diff --git a/version.h b/version.h index 62045ed81..70815daf2 100644 --- a/version.h +++ b/version.h @@ -23,15 +23,18 @@ * Major version */ #define ZEROTIER_ONE_VERSION_MAJOR 1 +#define ZEROTIER_ONE_VERSION_MAJOR_S "1" /** * Minor version */ #define ZEROTIER_ONE_VERSION_MINOR 1 +#define ZEROTIER_ONE_VERSION_MINOR_S "1" /** * Revision */ #define ZEROTIER_ONE_VERSION_REVISION 5 +#define ZEROTIER_ONE_VERSION_REVISION_S "5" #endif