diff options
-rw-r--r-- | attic/DBM.cpp | 243 | ||||
-rw-r--r-- | attic/DBM.hpp | 168 | ||||
-rw-r--r-- | attic/kubernetes_docs/.zerotierCliSettings (renamed from doc/ext/kubernetes/.zerotierCliSettings) | 0 | ||||
-rw-r--r-- | attic/kubernetes_docs/Dockerfile (renamed from doc/ext/kubernetes/Dockerfile) | 0 | ||||
-rw-r--r-- | attic/kubernetes_docs/README.md (renamed from doc/ext/kubernetes/README.md) | 0 | ||||
-rw-r--r-- | attic/kubernetes_docs/entrypoint.sh (renamed from doc/ext/kubernetes/entrypoint.sh) | 0 | ||||
-rw-r--r-- | attic/kubernetes_docs/server.js (renamed from doc/ext/kubernetes/server.js) | 0 | ||||
-rw-r--r-- | attic/root-watcher/README.md (renamed from root-watcher/README.md) | 0 | ||||
-rw-r--r-- | attic/root-watcher/config.json.example (renamed from root-watcher/config.json.example) | 0 | ||||
-rw-r--r-- | attic/root-watcher/package.json (renamed from root-watcher/package.json) | 0 | ||||
-rw-r--r-- | attic/root-watcher/schema.sql (renamed from root-watcher/schema.sql) | 0 | ||||
-rw-r--r-- | attic/root-watcher/zerotier-root-watcher.js (renamed from root-watcher/zerotier-root-watcher.js) | 0 | ||||
-rw-r--r-- | attic/tcp-proxy/Makefile (renamed from tcp-proxy/Makefile) | 0 | ||||
-rw-r--r-- | attic/tcp-proxy/README.md (renamed from tcp-proxy/README.md) | 0 | ||||
-rw-r--r-- | attic/tcp-proxy/tcp-proxy.cpp (renamed from tcp-proxy/tcp-proxy.cpp) | 0 | ||||
-rw-r--r-- | include/ZeroTierOne.h | 419 | ||||
-rw-r--r-- | make-mac.mk | 2 | ||||
-rw-r--r-- | node/Buffer.hpp | 53 | ||||
-rw-r--r-- | node/Constants.hpp | 6 | ||||
-rw-r--r-- | node/Hashtable.hpp | 7 | ||||
-rw-r--r-- | node/Network.cpp | 54 | ||||
-rw-r--r-- | node/Network.hpp | 3 | ||||
-rw-r--r-- | node/Node.cpp | 292 | ||||
-rw-r--r-- | node/Node.hpp | 64 | ||||
-rw-r--r-- | node/Topology.cpp | 70 | ||||
-rw-r--r-- | osdep/Binder.hpp | 205 | ||||
-rw-r--r-- | osdep/OSUtils.cpp | 2 | ||||
-rw-r--r-- | service/OneService.cpp | 1300 | ||||
-rw-r--r-- | service/OneService.hpp | 45 |
29 files changed, 1798 insertions, 1135 deletions
diff --git a/attic/DBM.cpp b/attic/DBM.cpp new file mode 100644 index 00000000..54f017e0 --- /dev/null +++ b/attic/DBM.cpp @@ -0,0 +1,243 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#include "DBM.hpp" + +#include "../version.h" + +#include "../node/Salsa20.hpp" +#include "../node/Poly1305.hpp" +#include "../node/SHA512.hpp" + +#include "../osdep/OSUtils.hpp" + +#define ZT_STORED_OBJECT_TYPE__CLUSTER_NODE_STATUS (ZT_STORED_OBJECT__MAX_TYPE_ID + 1) +#define ZT_STORED_OBJECT_TYPE__CLUSTER_DEFINITION (ZT_STORED_OBJECT__MAX_TYPE_ID + 2) + +namespace ZeroTier { + +// We generate the cluster ID from our address and version info since this is +// not at all designed to allow interoperation between versions (or endians) +// in the same cluster. +static inline uint64_t _mkClusterId(const Address &myAddress) +{ + uint64_t x = ZEROTIER_ONE_VERSION_MAJOR; + x <<= 8; + x += ZEROTIER_ONE_VERSION_MINOR; + x <<= 8; + x += ZEROTIER_ONE_VERSION_REVISION; + x <<= 40; + x ^= myAddress.toInt(); +#if __BYTE_ORDER == __BIG_ENDIAN + ++x; +#endif; + return x; +} + +void DBM::onUpdate(uint64_t from,const _MapKey &k,const _MapValue &v,uint64_t rev) +{ + char p[4096]; + char tmp[ZT_DBM_MAX_VALUE_SIZE]; + if (_persistentPath((ZT_StoredObjectType)k.type,k.key,p,sizeof(p))) { + // Reduce unnecessary disk writes + FILE *f = fopen(p,"r"); + if (f) { + long n = (long)fread(tmp,1,sizeof(tmp),f); + fclose(f); + if ((n == (long)v.len)&&(!memcmp(v.data,tmp,n))) + return; + } + + // Write to disk if file has changed or was not already present + f = fopen(p,"w"); + if (f) { + if (fwrite(data,len,1,f) != 1) + fprintf(stderr,"WARNING: error writing to %s (I/O error)" ZT_EOL_S,p); + fclose(f); + if (type == ZT_STORED_OBJECT_IDENTITY_SECRET) + OSUtils::lockDownFile(p,false); + } else { + fprintf(stderr,"WARNING: error writing to %s (cannot open)" ZT_EOL_S,p); + } + } +} + +void DBM::onDelete(uint64_t from,const _MapKey &k) +{ + char p[4096]; + if (_persistentPath((ZT_StoredObjectType)k.type,k.key,p,sizeof(p))) + OSUtils::rm(p); +} + +DBM::_vsdm_cryptor::_vsdm_cryptor(const Identity &secretIdentity) +{ + uint8_t s512[64]; + SHA512::hash(h512,secretIdentity.privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN); + memcpy(_key,s512,sizeof(_key)); +} + +void DBM::_vsdm_cryptor::encrypt(void *d,unsigned long l) +{ + if (l >= 24) { // sanity check + uint8_t key[32]; + uint8_t authKey[32]; + uint8_t auth[16]; + + uint8_t *const iv = reinterpret_cast<uint8_t *>(d) + (l - 16); + Utils::getSecureRandom(iv,16); + memcpy(key,_key,32); + for(unsigned long i=0;i<8;++i) + _key[i] ^= iv[i]; + + Salsa20 s20(key,iv + 8); + memset(authKey,0,32); + s20.crypt12(authKey,authKey,32); + s20.crypt12(d,d,l - 24); + + Poly1305::compute(auth,d,l - 24,authKey); + memcpy(reinterpret_cast<uint8_t *>(d) + (l - 24),auth,8); + } +} + +bool DBM::_vsdm_cryptor::decrypt(void *d,unsigned long l) +{ + if (l >= 24) { // sanity check + uint8_t key[32]; + uint8_t authKey[32]; + uint8_t auth[16]; + + uint8_t *const iv = reinterpret_cast<uint8_t *>(d) + (l - 16); + memcpy(key,_key,32); + for(unsigned long i=0;i<8;++i) + _key[i] ^= iv[i]; + + Salsa20 s20(key,iv + 8); + memset(authKey,0,32); + s20.crypt12(authKey,authKey,32); + + Poly1305::compute(auth,d,l - 24,authKey); + if (!Utils::secureEq(reinterpret_cast<uint8_t *>(d) + (l - 24),auth,8)) + return false; + + s20.crypt12(d,d,l - 24); + + return true; + } + return false; +} + +DBM::DBM(const Identity &secretIdentity,uint64_t clusterMemberId,const std::string &basePath,Node *node) : + _basePath(basePath), + _node(node), + _startTime(OSUtils::now()), + _m(_mkClusterId(secretIdentity.address()),clusterMemberId,false,_vsdm_cryptor(secretIdentity),_vsdm_watcher(this)) +{ +} + +DBM::~DBM() +{ +} + +void DBM::put(const ZT_StoredObjectType type,const uint64_t key,const void *data,unsigned int len) +{ + char p[4096]; + if (_m.put(_MapKey(key,(uint16_t)type),Value(OSUtils::now(),(uint16_t)len,data))) { + if (_persistentPath(type,key,p,sizeof(p))) { + FILE *f = fopen(p,"w"); + if (f) { + if (fwrite(data,len,1,f) != 1) + fprintf(stderr,"WARNING: error writing to %s (I/O error)" ZT_EOL_S,p); + fclose(f); + if (type == ZT_STORED_OBJECT_IDENTITY_SECRET) + OSUtils::lockDownFile(p,false); + } else { + fprintf(stderr,"WARNING: error writing to %s (cannot open)" ZT_EOL_S,p); + } + } + } +} + +bool DBM::get(const ZT_StoredObjectType type,const uint64_t key,Value &value) +{ + char p[4096]; + if (_m.get(_MapKey(key,(uint16_t)type),value)) + return true; + if (_persistentPath(type,key,p,sizeof(p))) { + FILE *f = fopen(p,"r"); + if (f) { + long n = (long)fread(value.data,1,sizeof(value.data),f); + value.len = (n > 0) ? (uint16_t)n : (uint16_t)0; + fclose(f); + value.ts = OSUtils::getLastModified(p); + _m.put(_MapKey(key,(uint16_t)type),value); + return true; + } + } + return false; +} + +void DBM::del(const ZT_StoredObjectType type,const uint64_t key) +{ + char p[4096]; + _m.del(_MapKey(key,(uint16_t)type)); + if (_persistentPath(type,key,p,sizeof(p))) + OSUtils::rm(p); +} + +void DBM::clean() +{ +} + +bool DBM::_persistentPath(const ZT_StoredObjectType type,const uint64_t key,char *p,unsigned int maxlen) +{ + switch(type) { + case ZT_STORED_OBJECT_IDENTITY_PUBLIC: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "identity.public",_basePath.c_str()); + return true; + case ZT_STORED_OBJECT_IDENTITY_SECRET: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "identity.secret",_basePath.c_str()); + return true; + case ZT_STORED_OBJECT_IDENTITY: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "iddb.d" ZT_PATH_SEPARATOR_S "%.10llx",_basePath.c_str(),key); + return true; + case ZT_STORED_OBJECT_NETWORK_CONFIG: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_basePath.c_str(),key); + return true; + case ZT_STORED_OBJECT_PLANET: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "planet",_basePath.c_str()); + return true; + case ZT_STORED_OBJECT_MOON: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "moons.d" ZT_PATH_SEPARATOR_S "%.16llx.moon",_basePath.c_str(),key); + return true; + case (ZT_StoredObjectType)ZT_STORED_OBJECT_TYPE__CLUSTER_DEFINITION: + Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "cluster",_basePath.c_str()); + return true; + default: + return false; + } +} + +} // namespace ZeroTier diff --git a/attic/DBM.hpp b/attic/DBM.hpp new file mode 100644 index 00000000..c6d5b8c0 --- /dev/null +++ b/attic/DBM.hpp @@ -0,0 +1,168 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_DBM_HPP___ +#define ZT_DBM_HPP___ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#include <stdexcept> + +#include "../node/Constants.hpp" +#include "../node/Mutex.hpp" +#include "../node/Utils.hpp" +#include "../node/Identity.hpp" +#include "../node/Peer.hpp" + +#include "../ext/vsdm/vsdm.hpp" + +// The Peer is the largest structure we persist here +#define ZT_DBM_MAX_VALUE_SIZE sizeof(Peer) + +namespace ZeroTier { + +class Node; +class DBM; + +class DBM +{ +public: + ZT_PACKED_STRUCT(struct Value + { + Value(const uint64_t t,const uint16_t l,const void *d) : + ts(t), + l(l) + { + memcpy(data,d,l); + } + uint64_t ts; + uint16_t len; + uint8_t data[ZT_DBM_MAX_VALUE_SIZE]; + }); + +private: + ZT_PACKED_STRUCT(struct _MapKey + { + _MapKey() : obj(0),type(0) {} + _MapKey(const uint16_t t,const uint64_t o) : obj(o),type(t) {} + uint64_t obj; + uint16_t type; + inline bool operator==(const _MapKey &k) const { return ((obj == k.obj)&&(type == k.type)); } + }); + struct _MapHasher + { + inline std::size_t operator()(const _MapKey &k) const { return (std::size_t)((k.obj ^ (k.obj >> 32)) + (uint64_t)k.type); } + }; + + void onUpdate(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev); + void onDelete(uint64_t from,const _MapKey &k); + + class _vsdm_watcher + { + public: + _vsdm_watcher(DBM *p) : _parent(p) {} + inline void add(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev) { _parent->onUpdate(from,k,v,rev); } + inline void update(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev) { _parent->onUpdate(from,k,v,rev); } + inline void del(uint64_t from,const _MapKey &k) { _parent->onDelete(from,k); } + private: + DBM *_parent; + }; + class _vsdm_serializer + { + public: + static inline unsigned long objectSize(const _MapKey &k) { return 10; } + static inline unsigned long objectSize(const Value &v) { return (10 + v.len); } + static inline const char *objectData(const _MapKey &k) { return reinterpret_cast<const char *>(&k); } + static inline const char *objectData(const Value &v) { return reinterpret_cast<const char *>(&v); } + static inline bool objectDeserialize(const char *d,unsigned long l,_MapKey &k) + { + if (l == 10) { + memcpy(&k,d,10); + return true; + } + return false; + } + static inline bool objectDeserialize(const char *d,unsigned long l,Value &v) + { + if ((l >= 10)&&(l <= (10 + ZT_DBM_MAX_VALUE_SIZE))) { + memcpy(&v,d,l); + return true; + } + return false; + } + }; + class _vsdm_cryptor + { + public: + _vsdm_cryptor(const Identity &secretIdentity); + static inline unsigned long overhead() { return 24; } + void encrypt(void *d,unsigned long l); + bool decrypt(void *d,unsigned long l); + uint8_t _key[32]; + }; + + typedef vsdm< _MapKey,Value,16384,_vsdm_watcher,_vsdm_serializer,_vsdm_cryptor,_MapHasher > _Map; + + friend class _Map; + +public: + ZT_PACKED_STRUCT(struct ClusterPeerStatus + { + uint64_t startTime; + uint64_t currentTime; + uint64_t clusterPeersConnected; + uint64_t ztPeersConnected; + uint16_t platform; + uint16_t arch; + }); + + DBM(const Identity &secretIdentity,uint64_t clusterMemberId,const std::string &basePath,Node *node); + + ~DBM(); + + void put(const ZT_StoredObjectType type,const uint64_t key,const void *data,unsigned int len); + + bool get(const ZT_StoredObjectType type,const uint64_t key,Value &value); + + void del(const ZT_StoredObjectType type,const uint64_t key); + + void clean(); + +private: + bool DBM::_persistentPath(const ZT_StoredObjectType type,const uint64_t key,char *p,unsigned int maxlen); + + const std::string _basePath; + Node *const _node; + uint64_t _startTime; + _Map _m; +}; + +} // namespace ZeroTier + +#endif diff --git a/doc/ext/kubernetes/.zerotierCliSettings b/attic/kubernetes_docs/.zerotierCliSettings index 0e7df9b6..0e7df9b6 100644 --- a/doc/ext/kubernetes/.zerotierCliSettings +++ b/attic/kubernetes_docs/.zerotierCliSettings diff --git a/doc/ext/kubernetes/Dockerfile b/attic/kubernetes_docs/Dockerfile index 6437a2bb..6437a2bb 100644 --- a/doc/ext/kubernetes/Dockerfile +++ b/attic/kubernetes_docs/Dockerfile diff --git a/doc/ext/kubernetes/README.md b/attic/kubernetes_docs/README.md index 482e77e5..482e77e5 100644 --- a/doc/ext/kubernetes/README.md +++ b/attic/kubernetes_docs/README.md diff --git a/doc/ext/kubernetes/entrypoint.sh b/attic/kubernetes_docs/entrypoint.sh index 80cd278e..80cd278e 100644 --- a/doc/ext/kubernetes/entrypoint.sh +++ b/attic/kubernetes_docs/entrypoint.sh diff --git a/doc/ext/kubernetes/server.js b/attic/kubernetes_docs/server.js index a4b08bb8..a4b08bb8 100644 --- a/doc/ext/kubernetes/server.js +++ b/attic/kubernetes_docs/server.js diff --git a/root-watcher/README.md b/attic/root-watcher/README.md index ded6a63f..ded6a63f 100644 --- a/root-watcher/README.md +++ b/attic/root-watcher/README.md diff --git a/root-watcher/config.json.example b/attic/root-watcher/config.json.example index 0ad1bbe1..0ad1bbe1 100644 --- a/root-watcher/config.json.example +++ b/attic/root-watcher/config.json.example diff --git a/root-watcher/package.json b/attic/root-watcher/package.json index d6e86d78..d6e86d78 100644 --- a/root-watcher/package.json +++ b/attic/root-watcher/package.json diff --git a/root-watcher/schema.sql b/attic/root-watcher/schema.sql index ade0fa3e..ade0fa3e 100644 --- a/root-watcher/schema.sql +++ b/attic/root-watcher/schema.sql diff --git a/root-watcher/zerotier-root-watcher.js b/attic/root-watcher/zerotier-root-watcher.js index d4607fc2..d4607fc2 100644 --- a/root-watcher/zerotier-root-watcher.js +++ b/attic/root-watcher/zerotier-root-watcher.js diff --git a/tcp-proxy/Makefile b/attic/tcp-proxy/Makefile index af4e71e3..af4e71e3 100644 --- a/tcp-proxy/Makefile +++ b/attic/tcp-proxy/Makefile diff --git a/tcp-proxy/README.md b/attic/tcp-proxy/README.md index 6f347d64..6f347d64 100644 --- a/tcp-proxy/README.md +++ b/attic/tcp-proxy/README.md diff --git a/tcp-proxy/tcp-proxy.cpp b/attic/tcp-proxy/tcp-proxy.cpp index a7906aae..a7906aae 100644 --- a/tcp-proxy/tcp-proxy.cpp +++ b/attic/tcp-proxy/tcp-proxy.cpp diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 5126c5a2..9c295cee 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -140,39 +140,6 @@ extern "C" { #define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7 /** - * Maximum number of hops in a ZeroTier circuit test - * - * This is more or less the max that can be fit in a given packet (with - * fragmentation) and only one address per hop. - */ -#define ZT_CIRCUIT_TEST_MAX_HOPS 256 - -/** - * Maximum number of addresses per hop in a circuit test - */ -#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8 - -/** - * Circuit test report flag: upstream peer authorized in path (e.g. by network COM) - */ -#define ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH 0x0000000000000001ULL - -/** - * Maximum number of cluster members (and max member ID plus one) - */ -#define ZT_CLUSTER_MAX_MEMBERS 128 - -/** - * Maximum number of physical ZeroTier addresses a cluster member can report - */ -#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16 - -/** - * Maximum allowed cluster message length in bytes - */ -#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48) - -/** * Maximum value for link quality (min is 0) */ #define ZT_PATH_LINK_QUALITY_MAX 0xff @@ -286,22 +253,27 @@ enum ZT_ResultCode */ ZT_RESULT_OK = 0, - // Fatal errors (>0, <1000) + /** + * Call produced no error but no action was taken + */ + ZT_RESULT_OK_IGNORED = 1, + + // Fatal errors (>100, <1000) /** * Ran out of memory */ - ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 1, + ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 100, /** * Data store is not writable or has failed */ - ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 2, + ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 101, /** * Internal error (e.g. unexpected exception indicating bug or build problem) */ - ZT_RESULT_FATAL_ERROR_INTERNAL = 3, + ZT_RESULT_FATAL_ERROR_INTERNAL = 102, // Non-fatal errors (>1000) @@ -325,7 +297,7 @@ enum ZT_ResultCode * @param x Result code * @return True if result code indicates a fatal error */ -#define ZT_ResultCode_isFatal(x) ((((int)(x)) > 0)&&(((int)(x)) < 1000)) +#define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000)) /** * Status codes sent to status update callback when things happen @@ -421,6 +393,13 @@ enum ZT_Event /** * User message used with ZT_EVENT_USER_MESSAGE + * + * These are direct VL1 P2P messages for application use. Encryption and + * authentication in the ZeroTier protocol will guarantee the origin + * address and message content, but you are responsible for any other + * levels of authentication or access control that are required. Any node + * in the world can send you a user message! (Unless your network is air + * gapped.) */ typedef struct { @@ -748,24 +727,6 @@ typedef struct } v; } ZT_VirtualNetworkRule; -typedef struct -{ - /** - * 128-bit ID (GUID) of this capability - */ - uint64_t id[2]; - - /** - * Expiration time (measured vs. network config timestamp issued by controller) - */ - uint64_t expiration; - - struct { - uint64_t from; - uint64_t to; - } custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH]; -} ZT_VirtualNetworkCapability; - /** * A route to be pushed on a virtual network */ @@ -1105,76 +1066,98 @@ typedef struct } ZT_PeerList; /** - * A cluster member's status + * ZeroTier core state objects + * + * All of these objects can be persisted if desired. To preserve the + * identity of a node and its address, the identity (public and secret) + * must be saved at a minimum. + * + * The reference service implementation currently persists identity, + * peer identities (for a period of time), planet, moons, and network + * configurations. Other state is treated as ephemeral. + * + * All state objects should be replicated in cluster mode. The reference + * clustering implementation uses a rumor mill algorithm in which state + * updates that are accepted with RESULT_OK (but not RESULT_OK_IGNORED) + * are flooded to all connected cluster peers. This results in updates + * being flooded across the cluster until all cluster members have the + * latest. */ -typedef struct { - /** - * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS) - */ - unsigned int id; - - /** - * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address - */ - unsigned int msSinceLastHeartbeat; - +enum ZT_StateObjectType +{ /** - * Non-zero if cluster member is alive + * Null object -- ignored */ - int alive; + ZT_STATE_OBJECT_NULL = 0, /** - * X, Y, and Z coordinates of this member (if specified, otherwise zero) + * Public address and public key * - * What these mean depends on the location scheme being used for - * location-aware clustering. At present this is GeoIP and these - * will be the X, Y, and Z coordinates of the location on a spherical - * approximation of Earth where Earth's core is the origin (in km). - * They don't have to be perfect and need only be comparable with others - * to find shortest path via the standard vector distance formula. + * Object ID: this node's address if known, or 0 if unknown (first query) + * Canonical path: <HOME>/identity.public + * Persistence: required */ - int x,y,z; + ZT_STATE_OBJECT_IDENTITY_PUBLIC = 1, /** - * Cluster member's last reported load + * Full identity with secret key + * + * Object ID: this node's address if known, or 0 if unknown (first query) + * Canonical path: <HOME>/identity.secret + * Persistence: required, should be stored with restricted permissions e.g. mode 0600 on *nix */ - uint64_t load; + ZT_STATE_OBJECT_IDENTITY_SECRET = 2, /** - * Number of peers + * A peer to which this node is communicating + * + * Object ID: peer address + * Canonical path: <HOME>/peers.d/<ADDRESS> (10-digit hex address) + * Persistence: optional, can be purged at any time */ - uint64_t peers; + ZT_STATE_OBJECT_PEER = 3, /** - * Physical ZeroTier endpoints for this member (where peers are sent when directed here) + * The identity of a known peer + * + * Object ID: peer address + * Canonical path: <HOME>/iddb.d/<ADDRESS> (10-digit hex address) + * Persistence: optional, can be purged at any time, recommended ttl 30-60 days */ - struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES]; + ZT_STATE_OBJECT_PEER_IDENTITY = 4, /** - * Number of physical ZeroTier endpoints this member is announcing + * Network configuration + * + * Object ID: peer address + * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID) + * Persistence: required if network memberships should persist */ - unsigned int numZeroTierPhysicalEndpoints; -} ZT_ClusterMemberStatus; + ZT_STATE_OBJECT_NETWORK_CONFIG = 5, -/** - * ZeroTier cluster status - */ -typedef struct { /** - * My cluster member ID (a record for 'self' is included in member[]) + * The planet (there is only one per... well... planet!) + * + * Object ID: world ID of planet, or 0 if unknown (first query) + * Canonical path: <HOME>/planet + * Persistence: recommended */ - unsigned int myId; + ZT_STATE_OBJECT_PLANET = 6, /** - * Number of cluster members + * A moon (federated root set) + * + * Object ID: world ID of moon + * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID) + * Persistence: required if moon memberships should persist */ - unsigned int clusterSize; + ZT_STATE_OBJECT_MOON = 7, /** - * Cluster member statuses + * IDs above this value will not be used by the core (and could be used as implementation-specific IDs) */ - ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS]; -} ZT_ClusterStatus; + ZT_STATE_OBJECT__MAX_ID = 255 +}; /** * An instance of a ZeroTier One node (opaque) @@ -1252,62 +1235,41 @@ typedef void (*ZT_EventCallback)( const void *); /* Event payload (if applicable) */ /** - * Function to get an object from the data store - * - * Parameters: (1) object name, (2) buffer to fill, (3) size of buffer, (4) - * index in object to start reading, (5) result parameter that must be set - * to the actual size of the object if it exists. + * Callback for storing and/or publishing state information * - * Object names can contain forward slash (/) path separators. They will - * never contain .. or backslash (\), so this is safe to map as a Unix-style - * path if the underlying storage permits. For security reasons we recommend - * returning errors if .. or \ are used. + * See ZT_StateObjectType docs for information about each state object type + * and when and if it needs to be persisted. * - * The function must return the actual number of bytes read. If the object - * doesn't exist, it should return -1. -2 should be returned on other errors - * such as errors accessing underlying storage. - * - * If the read doesn't fit in the buffer, the max number of bytes should be - * read. The caller may call the function multiple times to read the whole - * object. + * An object of length -1 is sent to indicate that an object should be + * deleted. */ -typedef long (*ZT_DataStoreGetFunction)( +typedef void (*ZT_StatePutFunction)( ZT_Node *, /* Node */ void *, /* User ptr */ void *, /* Thread ptr */ - const char *, - void *, - unsigned long, - unsigned long, - unsigned long *); + enum ZT_StateObjectType, /* State object type */ + uint64_t, /* State object ID (if applicable) */ + const void *, /* State object data */ + int); /* Length of data or -1 to delete */ /** - * Function to store an object in the data store - * - * Parameters: (1) node, (2) user ptr, (3) object name, (4) object data, - * (5) object size, (6) secure? (bool). - * - * If secure is true, the file should be set readable and writable only - * to the user running ZeroTier One. What this means is platform-specific. + * Callback for retrieving stored state information * - * Name semantics are the same as the get function. This must return zero on - * success. You can return any OS-specific error code on failure, as these - * may be visible in logs or error messages and might aid in debugging. - * - * If the data pointer is null, this must be interpreted as a delete - * operation. + * This function should return the number of bytes actually stored to the + * buffer or -1 if the state object was not found or the buffer was too + * small to store it. */ -typedef int (*ZT_DataStorePutFunction)( - ZT_Node *, - void *, +typedef int (*ZT_StateGetFunction)( + ZT_Node *, /* Node */ + void *, /* User ptr */ void *, /* Thread ptr */ - const char *, - const void *, - unsigned long, - int); + enum ZT_StateObjectType, /* State object type */ + uint64_t, /* State object ID (if applicable) */ + void *, /* Buffer to store state object data */ + unsigned int); /* Length of data buffer in bytes */ /** - * Function to send a ZeroTier packet out over the wire + * Function to send a ZeroTier packet out over the physical wire (L2/L3) * * Parameters: * (1) Node @@ -1362,9 +1324,6 @@ typedef int (*ZT_WirePacketSendFunction)( * all configured ZeroTier interfaces and check to ensure that the supplied * addresses will not result in ZeroTier traffic being sent over a ZeroTier * interface (recursion). - * - * Obviously this is not required in configurations where this can't happen, - * such as network containers or embedded. */ typedef int (*ZT_PathCheckFunction)( ZT_Node *, /* Node */ @@ -1412,14 +1371,14 @@ struct ZT_Node_Callbacks long version; /** - * REQUIRED: Function to get objects from persistent storage + * REQUIRED: Function to store and/or replicate state objects */ - ZT_DataStoreGetFunction dataStoreGetFunction; + ZT_StatePutFunction statePutFunction; /** - * REQUIRED: Function to store objects in persistent storage + * REQUIRED: Function to retrieve state objects from an object store */ - ZT_DataStorePutFunction dataStorePutFunction; + ZT_StateGetFunction stateGetFunction; /** * REQUIRED: Function to send packets over the physical wire @@ -1453,13 +1412,12 @@ struct ZT_Node_Callbacks }; /** - * Create a new ZeroTier One node + * Create a new ZeroTier node * - * Note that this can take a few seconds the first time it's called, as it - * will generate an identity. - * - * TODO: should consolidate function pointers into versioned structure for - * better API stability. + * This will attempt to load its identity via the state get function in the + * callback struct. If that fails it will generate a new identity and store + * it. Identity generation can take anywhere from a few hundred milliseconds + * to a few seconds depending on your CPU speed. * * @param node Result: pointer is set to new node instance on success * @param uptr User pointer to pass to functions/callbacks @@ -1481,6 +1439,49 @@ enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct void ZT_Node_delete(ZT_Node *node); /** + * Notify node of an update to a state object + * + * This can be called after node startup to restore cached state objects such + * as network configurations for joined networks, planet, moons, etc. See + * the documentation of ZT_StateObjectType for more information. It's okay + * to call this for everything in the object store, but note that the node + * will automatically query for some core objects like identities so supplying + * these via this function is not necessary. + * + * Unless clustering is being implemented this function doesn't need to be + * used after startup. It could be called in response to filesystem changes + * to allow some degree of live configurability by filesystem observation. + * + * The return value of this function indicates whether the update was accepted + * as new. A return value of ZT_RESULT_OK indicates that the node gleaned new + * information from this update and that therefore (in cluster rumor mill mode) + * this update should be distributed to other members of a cluster. A return + * value of ZT_RESULT_OK_IGNORED indicates that the object did not provide any + * new information and therefore should not be propagated in a cluster. + * + * If clustering isn't being implemented the return value of this function can + * generally be ignored. + * + * ZT_RESULT_ERROR_BAD_PARAMETER can be returned if the parameter was invalid + * or not applicable. Object stores may delete the object in this case. + * + * @param node Node instance + * @param tptr Thread pointer to pass to functions/callbacks resulting from this call + * @param type State object type + * @param id State object ID + * @param data State object data + * @param len Length of state object data in bytes + * @return ZT_RESULT_OK if object was accepted or ZT_RESULT_OK_IGNORED if non-informative, error if object was invalid + */ +enum ZT_ResultCode ZT_Node_processStateUpdate( + ZT_Node *node, + void *tptr, + ZT_StateObjectType type, + uint64_t id, + const void *data, + unsigned int len); + +/** * Process a packet received from the physical wire * * @param node Node instance @@ -1766,116 +1767,6 @@ int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t type void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance); /** - * Initialize cluster operation - * - * This initializes the internal structures and state for cluster operation. - * It takes two function pointers. The first is to a function that can be - * used to send data to cluster peers (mechanism is not defined by Node), - * and the second is to a function that can be used to get the location of - * a physical address in X,Y,Z coordinate space (e.g. as cartesian coordinates - * projected from the center of the Earth). - * - * Send function takes an arbitrary pointer followed by the cluster member ID - * to send data to, a pointer to the data, and the length of the data. The - * maximum message length is ZT_CLUSTER_MAX_MESSAGE_LENGTH (65535). Messages - * must be delivered whole and may be dropped or transposed, though high - * failure rates are undesirable and can cause problems. Validity checking or - * CRC is also not required since the Node validates the authenticity of - * cluster messages using cryptogrphic methods and will silently drop invalid - * messages. - * - * Address to location function is optional and if NULL geo-handoff is not - * enabled (in this case x, y, and z in clusterInit are also unused). It - * takes an arbitrary pointer followed by a physical address and three result - * parameters for x, y, and z. It returns zero on failure or nonzero if these - * three coordinates have been set. Coordinate space is arbitrary and can be - * e.g. coordinates on Earth relative to Earth's center. These can be obtained - * from latitutde and longitude with versions of the Haversine formula. - * - * See: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates - * - * Neither the send nor the address to location function should block. If the - * address to location function does not have a location for an address, it - * should return zero and then look up the address for future use since it - * will be called again in (typically) 1-3 minutes. - * - * Note that both functions can be called from any thread from which the - * various Node functions are called, and so must be thread safe if multiple - * threads are being used. - * - * @param node Node instance - * @param myId My cluster member ID (less than or equal to ZT_CLUSTER_MAX_MEMBERS) - * @param zeroTierPhysicalEndpoints Preferred physical address(es) for ZeroTier clients to contact this cluster member (for peer redirect) - * @param numZeroTierPhysicalEndpoints Number of physical endpoints in zeroTierPhysicalEndpoints[] (max allowed: 255) - * @param x My cluster member's X location - * @param y My cluster member's Y location - * @param z My cluster member's Z location - * @param sendFunction Function to be called to send data to other cluster members - * @param sendFunctionArg First argument to sendFunction() - * @param addressToLocationFunction Function to be called to get the location of a physical address or NULL to disable geo-handoff - * @param addressToLocationFunctionArg First argument to addressToLocationFunction() - * @return OK or UNSUPPORTED_OPERATION if this Node was not built with cluster support - */ -enum ZT_ResultCode ZT_Node_clusterInit( - ZT_Node *node, - unsigned int myId, - const struct sockaddr_storage *zeroTierPhysicalEndpoints, - unsigned int numZeroTierPhysicalEndpoints, - int x, - int y, - int z, - void (*sendFunction)(void *,unsigned int,const void *,unsigned int), - void *sendFunctionArg, - int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), - void *addressToLocationFunctionArg); - -/** - * Add a member to this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS) - * @return OK or error if clustering is disabled, ID invalid, etc. - */ -enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId); - -/** - * Remove a member from this cluster - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param memberId Member ID to remove (nothing happens if not present) - */ -void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId); - -/** - * Handle an incoming cluster state message - * - * The message itself contains cluster member IDs, and invalid or badly - * addressed messages will be silently discarded. - * - * Calling this without having called clusterInit() will do nothing. - * - * @param node Node instance - * @param msg Cluster message - * @param len Length of cluster message - */ -void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len); - -/** - * Get the current status of the cluster from this node's point of view - * - * Calling this without clusterInit() or without cluster support will just - * zero out the structure and show a cluster size of zero. - * - * @param node Node instance - * @param cs Cluster status structure to fill with data - */ -void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs); - -/** * Set trusted paths * * A trusted path is a physical network (network/bits) over which both diff --git a/make-mac.mk b/make-mac.mk index de66f49c..5622a41b 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -54,7 +54,7 @@ ifeq ($(ZT_DEBUG),1) # C25519 in particular is almost UNUSABLE in heavy testing without it. node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g $(INCLUDES) $(DEFS) else - CFLAGS?=-Ofast -fstack-protector-strong + CFLAGS?=-Os -fstack-protector-strong CFLAGS+=$(ARCH_FLAGS) -Wall -Werror -flto -fPIE -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS) STRIP=strip endif diff --git a/node/Buffer.hpp b/node/Buffer.hpp index 8e6b78fd..69ee1758 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -93,7 +93,6 @@ public: } Buffer(unsigned int l) - throw(std::out_of_range) { if (l > C) throw std::out_of_range("Buffer: construct with size larger than capacity"); @@ -102,51 +101,42 @@ public: template<unsigned int C2> Buffer(const Buffer<C2> &b) - throw(std::out_of_range) { *this = b; } Buffer(const void *b,unsigned int l) - throw(std::out_of_range) { copyFrom(b,l); } Buffer(const std::string &s) - throw(std::out_of_range) { copyFrom(s.data(),s.length()); } template<unsigned int C2> inline Buffer &operator=(const Buffer<C2> &b) - throw(std::out_of_range) { if (unlikely(b._l > C)) throw std::out_of_range("Buffer: assignment from buffer larger than capacity"); - memcpy(_b,b._b,_l = b._l); - return *this; - } - - inline Buffer &operator=(const std::string &s) - throw(std::out_of_range) - { - copyFrom(s.data(),s.length()); + if (C2 == C) { + memcpy(this,&b,sizeof(Buffer<C>)); + } else { + memcpy(_b,b._b,_l = b._l); + } return *this; } inline void copyFrom(const void *b,unsigned int l) - throw(std::out_of_range) { if (unlikely(l > C)) throw std::out_of_range("Buffer: set from C array larger than capacity"); - _l = l; memcpy(_b,b,l); + _l = l; } unsigned char operator[](const unsigned int i) const - throw(std::out_of_range) { if (unlikely(i >= _l)) throw std::out_of_range("Buffer: [] beyond end of data"); @@ -154,7 +144,6 @@ public: } unsigned char &operator[](const unsigned int i) - throw(std::out_of_range) { if (unlikely(i >= _l)) throw std::out_of_range("Buffer: [] beyond end of data"); @@ -175,14 +164,12 @@ public: * @throws std::out_of_range Field extends beyond data size */ unsigned char *field(unsigned int i,unsigned int l) - throw(std::out_of_range) { if (unlikely((i + l) > _l)) throw std::out_of_range("Buffer: field() beyond end of data"); return (unsigned char *)(_b + i); } const unsigned char *field(unsigned int i,unsigned int l) const - throw(std::out_of_range) { if (unlikely((i + l) > _l)) throw std::out_of_range("Buffer: field() beyond end of data"); @@ -198,7 +185,6 @@ public: */ template<typename T> inline void setAt(unsigned int i,const T v) - throw(std::out_of_range) { if (unlikely((i + sizeof(T)) > _l)) throw std::out_of_range("Buffer: setAt() beyond end of data"); @@ -221,7 +207,6 @@ public: */ template<typename T> inline T at(unsigned int i) const - throw(std::out_of_range) { if (unlikely((i + sizeof(T)) > _l)) throw std::out_of_range("Buffer: at() beyond end of data"); @@ -248,7 +233,6 @@ public: */ template<typename T> inline void append(const T v) - throw(std::out_of_range) { if (unlikely((_l + sizeof(T)) > C)) throw std::out_of_range("Buffer: append beyond capacity"); @@ -271,7 +255,6 @@ public: * @throws std::out_of_range Attempt to append beyond capacity */ inline void append(unsigned char c,unsigned int n) - throw(std::out_of_range) { if (unlikely((_l + n) > C)) throw std::out_of_range("Buffer: append beyond capacity"); @@ -280,6 +263,19 @@ public: } /** + * Append secure random bytes + * + * @param n Number of random bytes to append + */ + inline void appendRandom(unsigned int n) + { + if (unlikely((_l + n) > C)) + throw std::out_of_range("Buffer: append beyond capacity"); + Utils::getSecureRandom(_b + _l,n); + _l += n; + } + + /** * Append a C-array of bytes * * @param b Data @@ -287,7 +283,6 @@ public: * @throws std::out_of_range Attempt to append beyond capacity */ inline void append(const void *b,unsigned int l) - throw(std::out_of_range) { if (unlikely((_l + l) > C)) throw std::out_of_range("Buffer: append beyond capacity"); @@ -302,7 +297,6 @@ public: * @throws std::out_of_range Attempt to append beyond capacity */ inline void append(const std::string &s) - throw(std::out_of_range) { append(s.data(),(unsigned int)s.length()); } @@ -314,7 +308,6 @@ public: * @throws std::out_of_range Attempt to append beyond capacity */ inline void appendCString(const char *s) - throw(std::out_of_range) { for(;;) { if (unlikely(_l >= C)) @@ -333,7 +326,6 @@ public: */ template<unsigned int C2> inline void append(const Buffer<C2> &b) - throw(std::out_of_range) { append(b._b,b._l); } @@ -349,7 +341,6 @@ public: * @return Pointer to beginning of appended field of length 'l' */ inline char *appendField(unsigned int l) - throw(std::out_of_range) { if (unlikely((_l + l) > C)) throw std::out_of_range("Buffer: append beyond capacity"); @@ -367,7 +358,6 @@ public: * @throws std::out_of_range Capacity exceeded */ inline void addSize(unsigned int i) - throw(std::out_of_range) { if (unlikely((i + _l) > C)) throw std::out_of_range("Buffer: setSize to larger than capacity"); @@ -383,7 +373,6 @@ public: * @throws std::out_of_range Size larger than capacity */ inline void setSize(const unsigned int i) - throw(std::out_of_range) { if (unlikely(i > C)) throw std::out_of_range("Buffer: setSize to larger than capacity"); @@ -397,7 +386,6 @@ public: * @throw std::out_of_range Position is beyond size of buffer */ inline void behead(const unsigned int at) - throw(std::out_of_range) { if (!at) return; @@ -414,7 +402,6 @@ public: * @throw std::out_of_range Position plus length is beyond size of buffer */ inline void erase(const unsigned int at,const unsigned int length) - throw(std::out_of_range) { const unsigned int endr = at + length; if (unlikely(endr > _l)) @@ -495,8 +482,8 @@ public: } private: - unsigned int _l; char ZT_VAR_MAY_ALIAS _b[C]; + unsigned int _l; }; } // namespace ZeroTier diff --git a/node/Constants.hpp b/node/Constants.hpp index 3974f0ec..fbbba76e 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -150,6 +150,12 @@ #endif #endif +#ifdef __WINDOWS__ +#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop)) +#else +#define ZT_PACKED_STRUCT(D) D __attribute__((packed)) +#endif + /** * Length of a ZeroTier address in bytes */ diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index c46ed68f..b702f608 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -374,12 +374,7 @@ private: } static inline unsigned long _hc(const uint64_t i) { - /* NOTE: this assumes that 'i' is evenly distributed, which is the case for - * packet IDs and network IDs -- the two use cases in ZT for uint64_t keys. - * These values are also greater than 0xffffffff so they'll map onto a full - * bucket count just fine no matter what happens. Normally you'd want to - * hash an integer key index in a hash table. */ - return (unsigned long)i; + return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses } static inline unsigned long _hc(const uint32_t i) { diff --git a/node/Network.cpp b/node/Network.cpp index de2ea7d7..74d81941 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -682,7 +682,7 @@ static _doZtFilterResult _doZtFilter( const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0); -Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr) : +Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf) : RR(renv), _uPtr(uptr), _id(nwid), @@ -697,29 +697,30 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) _incomingConfigChunks[i].ts = 0; - char confn[128]; - Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id); - - bool gotConf = false; - Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(); - NetworkConfig *nconf = new NetworkConfig(); - try { - std::string conf(RR->node->dataStoreGet(tPtr,confn)); - if (conf.length()) { - dconf->load(conf.c_str()); - if (nconf->fromDictionary(*dconf)) { - this->setConfiguration(tPtr,*nconf,false); - _lastConfigUpdate = 0; // we still want to re-request a new config from the network - gotConf = true; + if (nconf) { + this->setConfiguration(tPtr,*nconf,false); + _lastConfigUpdate = 0; // still want to re-request since it's likely outdated + } else { + bool got = false; + Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(); + try { + int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1); + if (n > 1) { + NetworkConfig *nconf = new NetworkConfig(); + try { + if (nconf->fromDictionary(*dict)) { + this->setConfiguration(tPtr,*nconf,false); + _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated + got = true; + } + } catch ( ... ) {} + delete nconf; } - } - } catch ( ... ) {} // ignore invalids, we'll re-request - delete nconf; - delete dconf; + } catch ( ... ) {} + delete dict; - if (!gotConf) { - // Save a one-byte CR to persist membership while we request a real netconf - RR->node->dataStorePut(tPtr,confn,"\n",1,false); + if (!got) + RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,"\n",1); } if (!_portInitialized) { @@ -735,12 +736,9 @@ Network::~Network() ZT_VirtualNetworkConfig ctmp; _externalConfig(&ctmp); - char n[128]; if (_destroyed) { - // This is done in Node::leave() so we can pass tPtr + // This is done in Node::leave() so we can pass tPtr properly //RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); - RR->node->dataStoreDelete((void *)0,n); } else { RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp); } @@ -1188,10 +1186,8 @@ int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToD if (saveToDisk) { Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(); try { - char n[64]; - Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id); if (nconf.toDictionary(*d,false)) - RR->node->dataStorePut(tPtr,n,(const void *)d->data(),d->sizeBytes(),true); + RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,_id,d->data(),d->sizeBytes()); } catch ( ... ) {} delete d; } diff --git a/node/Network.hpp b/node/Network.hpp index cce6c41f..454a3f20 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -88,8 +88,9 @@ public: * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param nwid Network ID * @param uptr Arbitrary pointer used by externally-facing API (for user use) + * @param nconf Network config, if known */ - Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr); + Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf); ~Network(); diff --git a/node/Node.cpp b/node/Node.cpp index 911c9c4b..37586834 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -33,6 +33,7 @@ #include "../version.h" #include "Constants.hpp" +#include "SharedPtr.hpp" #include "Node.hpp" #include "RuntimeEnvironment.hpp" #include "NetworkController.hpp" @@ -45,6 +46,7 @@ #include "Identity.hpp" #include "SelfAwareness.hpp" #include "Cluster.hpp" +#include "Network.hpp" const struct sockaddr_storage ZT_SOCKADDR_NULL = {0}; @@ -58,6 +60,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6 _RR(this), RR(&_RR), _uPtr(uptr), + _networks(8), _now(now), _lastPingCheck(0), _lastHousekeepingRun(0) @@ -74,20 +77,31 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6 memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo)); memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification)); - std::string idtmp(dataStoreGet(tptr,"identity.secret")); - if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { - TRACE("identity.secret not found, generating..."); - RR->identity.generate(); - idtmp = RR->identity.toString(true); - if (!dataStorePut(tptr,"identity.secret",idtmp,true)) - throw std::runtime_error("unable to write identity.secret"); + char tmp[512]; + std::string tmp2; + int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,0,tmp,sizeof(tmp) - 1); + if (n > 0) { + tmp[n] = (char)0; + if (!RR->identity.fromString(tmp)) + n = -1; } - RR->publicIdentityStr = RR->identity.toString(false); - RR->secretIdentityStr = RR->identity.toString(true); - idtmp = dataStoreGet(tptr,"identity.public"); - if (idtmp != RR->publicIdentityStr) { - if (!dataStorePut(tptr,"identity.public",RR->publicIdentityStr,false)) - throw std::runtime_error("unable to write identity.public"); + if (n <= 0) { + RR->identity.generate(); + tmp2 = RR->identity.toString(true); + stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length()); + tmp2 = RR->identity.toString(false); + stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length()); + } else { + n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp,sizeof(tmp) - 1); + if (n > 0) { + tmp[n] = (char)0; + if (RR->identity.toString(false) != tmp) + n = -1; + } + if (n <= 0) { + tmp2 = RR->identity.toString(false); + stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length()); + } } try { @@ -110,7 +124,7 @@ Node::~Node() { Mutex::Lock _l(_networks_m); - _networks.clear(); // ensure that networks are destroyed before shutdow + _networks.clear(); // destroy all networks before shutdown delete RR->sa; delete RR->topology; @@ -122,6 +136,97 @@ Node::~Node() #endif } +ZT_ResultCode Node::processStateUpdate( + void *tptr, + ZT_StateObjectType type, + uint64_t id, + const void *data, + unsigned int len) +{ + ZT_ResultCode r = ZT_RESULT_OK_IGNORED; + switch(type) { + + case ZT_STATE_OBJECT_PEER: + if (len) { + } + break; + + case ZT_STATE_OBJECT_PEER_IDENTITY: + if (len) { + } + break; + + case ZT_STATE_OBJECT_NETWORK_CONFIG: + if (len <= (ZT_NETWORKCONFIG_DICT_CAPACITY - 1)) { + if (len < 2) { + Mutex::Lock _l(_networks_m); + SharedPtr<Network> &nw = _networks[id]; + if (!nw) { + nw = SharedPtr<Network>(new Network(RR,tptr,id,(void *)0,(const NetworkConfig *)0)); + r = ZT_RESULT_OK; + } + } else { + Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(reinterpret_cast<const char *>(data),len); + try { + NetworkConfig *nconf = new NetworkConfig(); + try { + if (nconf->fromDictionary(*dict)) { + Mutex::Lock _l(_networks_m); + SharedPtr<Network> &nw = _networks[id]; + if (nw) { + switch (nw->setConfiguration(tptr,*nconf,false)) { + default: + r = ZT_RESULT_ERROR_BAD_PARAMETER; + break; + case 1: + r = ZT_RESULT_OK_IGNORED; + break; + case 2: + r = ZT_RESULT_OK; + break; + } + } else { + nw = SharedPtr<Network>(new Network(RR,tptr,id,(void *)0,nconf)); + } + } else { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + } catch ( ... ) { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + delete nconf; + } catch ( ... ) { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + delete dict; + } + } else { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + break; + + case ZT_STATE_OBJECT_PLANET: + case ZT_STATE_OBJECT_MOON: + if (len <= ZT_WORLD_MAX_SERIALIZED_LENGTH) { + World w; + try { + w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(data,len)); + if (( (w.type() == World::TYPE_MOON)&&(type == ZT_STATE_OBJECT_MOON) )||( (w.type() == World::TYPE_PLANET)&&(type == ZT_STATE_OBJECT_PLANET) )) { + r = (RR->topology->addWorld(tptr,w,false)) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED; + } + } catch ( ... ) { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + } else { + r = ZT_RESULT_ERROR_BAD_PARAMETER; + } + break; + + default: break; + } + return r; +} + ZT_ResultCode Node::processWirePacket( void *tptr, uint64_t now, @@ -238,10 +343,13 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint std::vector< SharedPtr<Network> > needConfig; { Mutex::Lock _l(_networks_m); - for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) { - if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) - needConfig.push_back(n->second); - n->second->sendUpdatesToMembers(tptr); + Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks); + uint64_t *k = (uint64_t *)0; + SharedPtr<Network> *v = (SharedPtr<Network> *)0; + while (i.next(k,v)) { + if (((now - (*v)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*v)->hasConfig())) + needConfig.push_back(*v); + (*v)->sendUpdatesToMembers(tptr); } } for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n) @@ -306,37 +414,38 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr) { Mutex::Lock _l(_networks_m); - SharedPtr<Network> nw = _network(nwid); - if(!nw) { - const std::pair< uint64_t,SharedPtr<Network> > nn(nwid,SharedPtr<Network>(new Network(RR,tptr,nwid,uptr))); - _networks.insert(std::upper_bound(_networks.begin(),_networks.end(),nn),nn); - } + SharedPtr<Network> &nw = _networks[nwid]; + if (!nw) + nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0)); return ZT_RESULT_OK; } ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr) { ZT_VirtualNetworkConfig ctmp; - std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn; void **nUserPtr = (void **)0; - Mutex::Lock _l(_networks_m); - - for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) { - if (n->first != nwid) { - newn.push_back(*n); - } else { - if (uptr) - *uptr = *n->second->userPtr(); - n->second->externalConfig(&ctmp); - n->second->destroy(); - nUserPtr = n->second->userPtr(); - } + { + Mutex::Lock _l(_networks_m); + SharedPtr<Network> *nw = _networks.get(nwid); + if (!nw) + return ZT_RESULT_OK; + if (uptr) + *uptr = (*nw)->userPtr(); + (*nw)->externalConfig(&ctmp); + (*nw)->destroy(); + nUserPtr = (*nw)->userPtr(); } - _networks.swap(newn); - + if (nUserPtr) RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp); + { + Mutex::Lock _l(_networks_m); + _networks.erase(nwid); + } + + RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid); + return ZT_RESULT_OK; } @@ -431,10 +540,10 @@ ZT_PeerList *Node::peers() const ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const { Mutex::Lock _l(_networks_m); - SharedPtr<Network> nw = _network(nwid); - if(nw) { + const SharedPtr<Network> *nw = _networks.get(nwid); + if (nw) { ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig)); - nw->externalConfig(nc); + (*nw)->externalConfig(nc); return nc; } return (ZT_VirtualNetworkConfig *)0; @@ -451,8 +560,11 @@ ZT_VirtualNetworkList *Node::networks() const nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList)); nl->networkCount = 0; - for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) - n->second->externalConfig(&(nl->networks[nl->networkCount++])); + Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks)); + uint64_t *k = (uint64_t *)0; + SharedPtr<Network> *v = (SharedPtr<Network> *)0; + while (i.next(k,v)) + (*v)->externalConfig(&(nl->networks[nl->networkCount++])); return nl; } @@ -503,6 +615,7 @@ void Node::setNetconfMaster(void *networkControllerInstance) RR->localNetworkController->init(RR->identity,this); } +/* ZT_ResultCode Node::clusterInit( unsigned int myId, const struct sockaddr_storage *zeroTierPhysicalEndpoints, @@ -570,25 +683,12 @@ void Node::clusterStatus(ZT_ClusterStatus *cs) #endif memset(cs,0,sizeof(ZT_ClusterStatus)); } +*/ /****************************************************************************/ /* Node methods used only within node/ */ /****************************************************************************/ -std::string Node::dataStoreGet(void *tPtr,const char *name) -{ - char buf[1024]; - std::string r; - unsigned long olen = 0; - do { - long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen); - if (n <= 0) - return std::string(); - r.append(buf,n); - } while (r.length() < olen); - return r; -} - bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress) { if (!Path::isAddressValidForPath(remoteAddress)) @@ -599,10 +699,13 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons { Mutex::Lock _l(_networks_m); - for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) { - if (i->second->hasConfig()) { - for(unsigned int k=0;k<i->second->config().staticIpCount;++k) { - if (i->second->config().staticIps[k].containsAddress(remoteAddress)) + Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks); + uint64_t *k = (uint64_t *)0; + SharedPtr<Network> *v = (SharedPtr<Network> *)0; + while (i.next(k,v)) { + if ((*v)->hasConfig()) { + for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) { + if ((*v)->config().staticIps[k].containsAddress(remoteAddress)) return false; } } @@ -806,6 +909,23 @@ void ZT_Node_delete(ZT_Node *node) } catch ( ... ) {} } +enum ZT_ResultCode ZT_Node_processStateUpdate( + ZT_Node *node, + void *tptr, + ZT_StateObjectType type, + uint64_t id, + const void *data, + unsigned int len) +{ + try { + return reinterpret_cast<ZeroTier::Node *>(node)->processStateUpdate(tptr,type,id,data,len); + } catch (std::bad_alloc &exc) { + return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; + } catch ( ... ) { + return ZT_RESULT_FATAL_ERROR_INTERNAL; + } +} + enum ZT_ResultCode ZT_Node_processWirePacket( ZT_Node *node, void *tptr, @@ -998,56 +1118,6 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance) } catch ( ... ) {} } -enum ZT_ResultCode ZT_Node_clusterInit( - ZT_Node *node, - unsigned int myId, - const struct sockaddr_storage *zeroTierPhysicalEndpoints, - unsigned int numZeroTierPhysicalEndpoints, - int x, - int y, - int z, - void (*sendFunction)(void *,unsigned int,const void *,unsigned int), - void *sendFunctionArg, - int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), - void *addressToLocationFunctionArg) -{ - try { - return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg); - } catch ( ... ) { - return ZT_RESULT_FATAL_ERROR_INTERNAL; - } -} - -enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId) -{ - try { - return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId); - } catch ( ... ) { - return ZT_RESULT_FATAL_ERROR_INTERNAL; - } -} - -void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId) -{ - try { - reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId); - } catch ( ... ) {} -} - -void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len) -{ - try { - reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len); - } catch ( ... ) {} -} - -void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs) -{ - try { - reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs); - } catch ( ... ) {} -} - void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count) { try { diff --git a/node/Node.hpp b/node/Node.hpp index 57b5489e..f407c60c 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -46,6 +46,7 @@ #include "Path.hpp" #include "Salsa20.hpp" #include "NetworkController.hpp" +#include "Hashtable.hpp" #undef TRACE #ifdef ZT_TRACE @@ -81,6 +82,12 @@ public: // Public API Functions ---------------------------------------------------- + ZT_ResultCode processStateUpdate( + void *tptr, + ZT_StateObjectType type, + uint64_t id, + const void *data, + unsigned int len); ZT_ResultCode processWirePacket( void *tptr, uint64_t now, @@ -117,21 +124,6 @@ public: void clearLocalInterfaceAddresses(); int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); void setNetconfMaster(void *networkControllerInstance); - ZT_ResultCode clusterInit( - unsigned int myId, - const struct sockaddr_storage *zeroTierPhysicalEndpoints, - unsigned int numZeroTierPhysicalEndpoints, - int x, - int y, - int z, - void (*sendFunction)(void *,unsigned int,const void *,unsigned int), - void *sendFunctionArg, - int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *), - void *addressToLocationFunctionArg); - ZT_ResultCode clusterAddMember(unsigned int memberId); - void clusterRemoveMember(unsigned int memberId); - void clusterHandleIncomingMessage(const void *msg,unsigned int len); - void clusterStatus(ZT_ClusterStatus *cs); // Internal functions ------------------------------------------------------ @@ -169,26 +161,27 @@ public: inline SharedPtr<Network> network(uint64_t nwid) const { Mutex::Lock _l(_networks_m); - return _network(nwid); + const SharedPtr<Network> *n = _networks.get(nwid); + if (n) + return *n; + return SharedPtr<Network>(); } inline bool belongsToNetwork(uint64_t nwid) const { Mutex::Lock _l(_networks_m); - for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) { - if (i->first == nwid) - return true; - } - return false; + return _networks.contains(nwid); } inline std::vector< SharedPtr<Network> > allNetworks() const { std::vector< SharedPtr<Network> > nw; Mutex::Lock _l(_networks_m); - nw.reserve(_networks.size()); - for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) - nw.push_back(i->second); + Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks)); + uint64_t *k = (uint64_t *)0; + SharedPtr<Network> *v = (SharedPtr<Network> *)0; + while (i.next(k,v)) + nw.push_back(*v); return nw; } @@ -198,17 +191,16 @@ public: return _directPaths; } - inline bool dataStorePut(void *tPtr,const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,data,len,(int)secure) == 0); } - inline bool dataStorePut(void *tPtr,const char *name,const std::string &data,bool secure) { return dataStorePut(tPtr,name,(const void *)data.data(),(unsigned int)data.length(),secure); } - inline void dataStoreDelete(void *tPtr,const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,(const void *)0,0,0); } - std::string dataStoreGet(void *tPtr,const char *name); - inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); } inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); } inline bool online() const throw() { return _online; } + inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id,void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); } + inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); } + inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); } + #ifdef ZT_TRACE void postTrace(const char *module,unsigned int line,const char *fmt,...); #endif @@ -222,6 +214,8 @@ public: World planet() const; std::vector<World> moons() const; + inline const Identity &identity() const { return _RR.identity; } + /** * Register that we are expecting a reply to a packet ID * @@ -281,16 +275,6 @@ public: virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); private: - inline SharedPtr<Network> _network(uint64_t nwid) const - { - // assumes _networks_m is locked - for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) { - if (i->first == nwid) - return i->second; - } - return SharedPtr<Network>(); - } - RuntimeEnvironment _RR; RuntimeEnvironment *RR; void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P @@ -303,7 +287,7 @@ private: // Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification() uint64_t _lastIdentityVerification[16384]; - std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks; + Hashtable< uint64_t,SharedPtr<Network> > _networks; Mutex _networks_m; std::vector<InetAddress> _directPaths; diff --git a/node/Topology.cpp b/node/Topology.cpp index 80f4ed4e..d4b424ff 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -68,15 +68,15 @@ Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) : _trustedPathCount(0), _amRoot(false) { - try { - World cachedPlanet; - std::string buf(RR->node->dataStoreGet(tPtr,"planet")); - if (buf.length() > 0) { - Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(buf.data(),(unsigned int)buf.length()); - cachedPlanet.deserialize(dswtmp,0); - } - addWorld(tPtr,cachedPlanet,false); - } catch ( ... ) {} + uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH]; + int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,0,tmp,sizeof(tmp)); + if (n > 0) { + try { + World cachedPlanet; + cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0); + addWorld(tPtr,cachedPlanet,false); + } catch ( ... ) {} // ignore invalid cached planets + } World defaultPlanet; { @@ -158,9 +158,8 @@ Identity Topology::getIdentity(void *tPtr,const Address &zta) void Topology::saveIdentity(void *tPtr,const Identity &id) { if (id) { - char p[128]; - Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt()); - RR->node->dataStorePut(tPtr,p,id.toString(false),false); + const std::string tmp(id.toString(false)); + RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER_IDENTITY,id.address().toInt(),tmp.data(),(unsigned int)tmp.length()); } } @@ -327,19 +326,11 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew) return false; } - char savePath[64]; - if (existing->type() == World::TYPE_MOON) { - Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",existing->id()); - } else { - Utils::scopy(savePath,sizeof(savePath),"planet"); - } try { - Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp; - existing->serialize(dswtmp,false); - RR->node->dataStorePut(tPtr,savePath,dswtmp.data(),dswtmp.size(),false); - } catch ( ... ) { - RR->node->dataStoreDelete(tPtr,savePath); - } + Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf; + existing->serialize(sbuf,false); + RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,existing->id(),sbuf.data(),sbuf.size()); + } catch ( ... ) {} _memoizeUpstreams(tPtr); @@ -348,21 +339,18 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew) void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed) { - char savePath[64]; - Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id); - - try { - std::string moonBin(RR->node->dataStoreGet(tPtr,savePath)); - if (moonBin.length() > 1) { - Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wtmp(moonBin.data(),(unsigned int)moonBin.length()); + char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH]; + int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,id,tmp,sizeof(tmp)); + if (n > 0) { + try { World w; - w.deserialize(wtmp); + w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n)); if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) { addWorld(tPtr,w,true); return; } - } - } catch ( ... ) {} + } catch ( ... ) {} + } if (seed) { Mutex::Lock _l(_upstreams_m); @@ -381,9 +369,7 @@ void Topology::removeMoon(void *tPtr,const uint64_t id) if (m->id() != id) { nm.push_back(*m); } else { - char savePath[64]; - Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id); - RR->node->dataStoreDelete(tPtr,savePath); + RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,id); } } _moons.swap(nm); @@ -425,12 +411,12 @@ void Topology::clean(uint64_t now) Identity Topology::_getIdentity(void *tPtr,const Address &zta) { - char p[128]; - Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)zta.toInt()); - std::string ids(RR->node->dataStoreGet(tPtr,p)); - if (ids.length() > 0) { + char tmp[512]; + int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER_IDENTITY,zta.toInt(),tmp,sizeof(tmp) - 1); + if (n > 0) { + tmp[n] = (char)0; try { - return Identity(ids); + return Identity(tmp); } catch ( ... ) {} // ignore invalid IDs } return Identity(); diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp index ee832825..1839ecc2 100644 --- a/osdep/Binder.hpp +++ b/osdep/Binder.hpp @@ -66,11 +66,7 @@ #include "Phy.hpp" #include "OSUtils.hpp" -/** - * Period between binder rescans/refreshes - * - * OneService also does this on detected restarts. - */ +// Period between refreshes of bindings #define ZT_BINDER_REFRESH_PERIOD 30000 namespace ZeroTier { @@ -105,10 +101,7 @@ public: Binder() {} /** - * Close all bound ports - * - * This should be called on shutdown. It closes listen sockets and UDP ports - * but not TCP connections from any TCP listen sockets. + * Close all bound ports, should be called on shutdown * * @param phy Physical interface */ @@ -116,9 +109,9 @@ public: void closeAll(Phy<PHY_HANDLER_TYPE> &phy) { Mutex::Lock _l(_lock); - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - phy.close(i->udpSock,false); - phy.close(i->tcpListenSock,false); + for(std::vector<_Binding>::iterator b(_bindings.begin());b!=_bindings.end();++b) { + phy.close(b->udpSock,false); + phy.close(b->tcpListenSock,false); } } @@ -129,7 +122,7 @@ public: * changes, on startup, or periodically (e.g. every 30-60s). * * @param phy Physical interface - * @param port Port to bind to on all interfaces (TCP and UDP) + * @param ports Ports to bind on all interfaces * @param ignoreInterfacesByName Ignore these interfaces by name * @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*) * @param ignoreInterfacesByAddress Ignore these interfaces by address @@ -137,11 +130,10 @@ public: * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method */ template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER> - void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker) + void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,INTERFACE_CHECKER &ifChecker) { std::map<InetAddress,std::string> localIfAddrs; - PhySocket *udps; - //PhySocket *tcps; + PhySocket *udps,*tcps; Mutex::Lock _l(_lock); #ifdef __WINDOWS__ @@ -161,8 +153,10 @@ public: case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_PRIVATE: - ip.setPort(port); - localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string())); + for(int x=0;x<portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string())); + } break; } } @@ -231,8 +225,10 @@ public: case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_PRIVATE: - ip.setPort(port); - localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname))); + for(int x=0;x<portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname))); + } break; } } @@ -249,11 +245,8 @@ public: configuration.ifc_buf = nullptr; if (controlfd < 0) goto ip4_address_error; - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; - configuration.ifc_buf = (char*)malloc(configuration.ifc_len); - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) { @@ -262,9 +255,8 @@ public: if (addr->sa_family != AF_INET) continue; std::string ifname = request.ifr_ifrn.ifrn_name; // name can either be just interface name or interface name followed by ':' and arbitrary label - if (ifname.find(':') != std::string::npos) { + if (ifname.find(':') != std::string::npos) ifname = ifname.substr(0, ifname.find(':')); - } InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0); if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) { @@ -274,8 +266,10 @@ public: case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_PRIVATE: - ip.setPort(port); - localIfAddrs.insert(std::pair<InetAddress,std::string>(ip, ifname)); + for(int x=0;x<portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname)); + } break; } } @@ -306,8 +300,10 @@ public: case InetAddress::IP_SCOPE_GLOBAL: case InetAddress::IP_SCOPE_SHARED: case InetAddress::IP_SCOPE_PRIVATE: - ip.setPort(port); - localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name))); + for(int x=0;x<portCount;++x) { + ip.setPort(ports[x]); + localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name))); + } break; } } @@ -322,59 +318,57 @@ public: // Default to binding to wildcard if we can't enumerate addresses if (localIfAddrs.empty()) { - localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string())); - localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string())); + for(int x=0;x<portCount;++x) { + localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string())); + localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string())); + } } - // Close any old bindings to anything that doesn't exist anymore - for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) { - if (localIfAddrs.find(bi->address) == localIfAddrs.end()) { - phy.close(bi->udpSock,false); - phy.close(bi->tcpListenSock,false); + std::vector<_Binding> newBindings; + + // Save bindings that are still valid, close those that are not + for(std::vector<_Binding>::iterator b(_bindings.begin());b!=_bindings.end();++b) { + if (localIfAddrs.find(b->address) != localIfAddrs.end()) { + newBindings.push_back(*b); + } else { + phy.close(b->udpSock,false); + phy.close(b->tcpListenSock,false); } } - std::vector<_Binding> newBindings; + // Create new bindings for those not already bound for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) { - typename std::vector<_Binding>::const_iterator bi(_bindings.begin()); - while (bi != _bindings.end()) { - if (bi->address == ii->first) { - newBindings.push_back(*bi); + typename std::vector<_Binding>::const_iterator bi(newBindings.begin()); + while (bi != newBindings.end()) { + if (bi->address == ii->first) break; - } ++bi; } - - if (bi == _bindings.end()) { + if (bi == newBindings.end()) { udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); - if (udps) { - //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0); - //if (tcps) { + tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0); + if ((udps)&&(tcps)) { #ifdef __LINUX__ - // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!) - if (ii->second.length() > 0) { - int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps); - char tmp[256]; - Utils::scopy(tmp,sizeof(tmp),ii->second.c_str()); - if (fd >= 0) { - if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) { - fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str()); - } - } - } + // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!) + if (ii->second.length() > 0) { + char tmp[256]; + Utils::scopy(tmp,sizeof(tmp),ii->second.c_str()); + int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps); + if (fd >= 0) + setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); + fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps); + if (fd >= 0) + setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); + } #endif // __LINUX__ - newBindings.push_back(_Binding()); - newBindings.back().udpSock = udps; - //newBindings.back().tcpListenSock = tcps; - newBindings.back().address = ii->first; - //} else { - // phy.close(udps,false); - //} + newBindings.push_back(_Binding()); + newBindings.back().udpSock = udps; + newBindings.back().tcpListenSock = tcps; + newBindings.back().address = ii->first; } } } - // Swapping pointers and then letting the old one fall out of scope is faster than copying again _bindings.swap(newBindings); } @@ -402,47 +396,78 @@ public: * @param data Data to send * @param len Length of data * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only) + * @return -1 == local doesn't match any bound address, 0 == send failure, 1 == send successful */ template<typename PHY_HANDLER_TYPE> - inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const + inline int udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const { + PhySocket *s; + typename std::vector<_Binding>::const_iterator i; + int result; Mutex::Lock _l(_lock); - if (local) { - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - if (i->address == local) { - if ((v4ttl)&&(local.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,v4ttl); - const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); - if ((v4ttl)&&(local.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,255); - return result; + + if (remote.ss_family == AF_INET) { + if (local) { + for(i=_bindings.begin();i!=_bindings.end();++i) { + if ( + (i->address.ss_family == AF_INET) && + (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_port == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_port) && + (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_addr.s_addr) + ) + { + s = i->udpSock; + goto Binder_send_packet; + } + } + } else { + for(i=_bindings.begin();i!=_bindings.end();++i) { + if (i->address.ss_family == AF_INET) { + s = i->udpSock; + goto Binder_send_packet; + } } } - return false; } else { - bool result = false; - for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) { - if (i->address.ss_family == remote.ss_family) { - if ((v4ttl)&&(remote.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,v4ttl); - result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len); - if ((v4ttl)&&(remote.ss_family == AF_INET)) - phy.setIp4UdpTtl(i->udpSock,255); + if (local) { + for(i=_bindings.begin();i!=_bindings.end();++i) { + if ( + (i->address.ss_family == AF_INET6) && + (reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_port) && + (!memcmp(reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_addr.s6_addr,16)) + ) + { + s = i->udpSock; + goto Binder_send_packet; + } + } + } else { + for(i=_bindings.begin();i!=_bindings.end();++i) { + if (i->address.ss_family == AF_INET6) { + s = i->udpSock; + goto Binder_send_packet; + } } } - return result; } + + return -1; + +Binder_send_packet: + if (v4ttl) phy.setIp4UdpTtl(s,v4ttl); + result = (int)phy.udpSend(s,reinterpret_cast<const struct sockaddr *>(&remote),data,len); + if (v4ttl) phy.setIp4UdpTtl(s,255); + return result; } /** * @return All currently bound local interface addresses */ - inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() + inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const { - Mutex::Lock _l(_lock); std::vector<InetAddress> aa; - for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) - aa.push_back(i->address); + Mutex::Lock _l(_lock); + for(std::vector<_Binding>::const_iterator b(_bindings.begin());b!=_bindings.end();++b) + aa.push_back(b->address); return aa; } diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index b7fce982..53e8bb97 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -287,7 +287,7 @@ int64_t OSUtils::getFileSize(const char *path) bool OSUtils::readFile(const char *path,std::string &buf) { - char tmp[1024]; + char tmp[16384]; FILE *f = fopen(path,"rb"); if (f) { for(;;) { diff --git a/service/OneService.cpp b/service/OneService.cpp index b96f3aed..0fadc191 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -31,7 +31,6 @@ #include <string> #include <map> -#include <set> #include <vector> #include <algorithm> #include <list> @@ -47,6 +46,9 @@ #include "../node/MAC.hpp" #include "../node/Identity.hpp" #include "../node/World.hpp" +#include "../node/Salsa20.hpp" +#include "../node/Poly1305.hpp" +#include "../node/SHA512.hpp" #include "../osdep/Phy.hpp" #include "../osdep/Thread.hpp" @@ -85,16 +87,6 @@ using json = nlohmann::json; -/** - * Uncomment to enable UDP breakage switch - * - * If this is defined, the presence of a file called /tmp/ZT_BREAK_UDP - * will cause direct UDP TX/RX to stop working. This can be used to - * test TCP tunneling fallback and other robustness features. Deleting - * this file will cause it to start working again. - */ -//#define ZT_BREAK_UDP - #include "../controller/EmbeddedNetworkController.hpp" #ifdef ZT_USE_TEST_TAP @@ -105,11 +97,13 @@ namespace ZeroTier { typedef TestEthernetTap EthernetTap; } #else #ifdef ZT_SDK - #include "../controller/EmbeddedNetworkController.hpp" - #include "../node/Node.hpp" - // Use the virtual netcon endpoint instead of a tun/tap port driver - #include "../src/SocketTap.hpp" - namespace ZeroTier { typedef SocketTap EthernetTap; } + +#include "../controller/EmbeddedNetworkController.hpp" +#include "../node/Node.hpp" +// Use the virtual netcon endpoint instead of a tun/tap port driver +#include "../src/SocketTap.hpp" +namespace ZeroTier { typedef SocketTap EthernetTap; } + #else #ifdef __APPLE__ @@ -148,9 +142,6 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } // How often to check for new multicast subscriptions on a tap device #define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 -// Path under ZT1 home for controller database if controller is enabled -#define ZT_CONTROLLER_DB_PATH "controller.d" - // TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away) #define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443" @@ -166,10 +157,22 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } // Clean files from iddb.d that are older than this (60 days) #define ZT_IDDB_CLEANUP_AGE 5184000000ULL +// Maximum write buffer size for outgoing TCP connections (sanity limit) +#define ZT_TCP_MAX_WRITEQ_SIZE 33554432 + +// How often to check TCP connections and cluster links and send status to cluster peers +#define ZT_TCP_CHECK_PERIOD 15000 + +// TCP activity timeout +#define ZT_TCP_ACTIVITY_TIMEOUT 60000 + namespace ZeroTier { namespace { +// Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode) +static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) }; + static std::string _trimString(const std::string &s) { unsigned long end = (unsigned long)s.length(); @@ -309,18 +312,12 @@ class OneServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf); static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData); -static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize); -static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure); +static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,const void *data,int len); +static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen); static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl); static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr); static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result); - -#ifdef ZT_ENABLE_CLUSTER -static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len); -static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z); -#endif - static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); static int ShttpOnMessageBegin(http_parser *parser); @@ -359,36 +356,55 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = { }; #endif +/** + * A TCP connection and related state and buffers + */ struct TcpConnection { enum { + TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection TCP_HTTP_INCOMING, - TCP_HTTP_OUTGOING, // not currently used - TCP_TUNNEL_OUTGOING // fale-SSL outgoing tunnel -- HTTP-related fields are not used + TCP_HTTP_OUTGOING, + TCP_TUNNEL_OUTGOING, // TUNNELED mode proxy outbound connection + TCP_CLUSTER_BACKPLANE } type; - bool shouldKeepAlive; OneServiceImpl *parent; PhySocket *sock; - InetAddress from; + InetAddress remoteAddr; + unsigned long lastReceive; + + // Used for inbound HTTP connections http_parser parser; unsigned long messageSize; - uint64_t lastActivity; - std::string currentHeaderField; std::string currentHeaderValue; - std::string url; std::string status; std::map< std::string,std::string > headers; - std::string body; - std::string writeBuf; - Mutex writeBuf_m; + // Used for cluster backplane connections + uint64_t clusterMemberId; + unsigned int clusterMemberVersionMajor; + unsigned int clusterMemberVersionMinor; + unsigned int clusterMemberVersionRev; + std::vector< InetAddress > clusterMemberLocalAddresses; + Mutex clusterMemberLocalAddresses_m; + + std::string readq; + std::string writeq; + Mutex writeq_m; }; -// Used to pseudo-randomize local source port picking -static volatile unsigned int _udpPortPickerCounter = 0; +/** + * Message types for cluster backplane communication + */ +enum ClusterMessageType +{ + CLUSTER_MESSAGE_STATUS = 0, + CLUSTER_MESSAGE_STATE_OBJECT = 1, + CLUSTER_MESSAGE_PROXY_SEND = 2 +}; class OneServiceImpl : public OneService { @@ -398,14 +414,21 @@ public: const std::string _homePath; std::string _authToken; std::string _controllerDbPath; + const std::string _iddbPath; + const std::string _networksPath; + const std::string _moonsPath; + EmbeddedNetworkController *_controller; Phy<OneServiceImpl *> _phy; Node *_node; SoftwareUpdater *_updater; bool _updateAutoApply; unsigned int _primaryPort; + volatile unsigned int _udpPortPickerCounter; + uint64_t _clusterMemberId; + uint8_t _clusterKey[32]; // secret key for cluster backplane config - // Local configuration and memo-ized static path definitions + // Local configuration and memo-ized information from it json _localConfig; Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints; Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints; @@ -415,6 +438,7 @@ public: std::vector< InetAddress > _globalV6Blacklist; std::vector< InetAddress > _allowManagementFrom; std::vector< std::string > _interfacePrefixBlacklist; + std::vector< InetAddress > _clusterBackplaneAddresses; Mutex _localConfig_m; /* @@ -428,13 +452,8 @@ public: * destructively with uPnP port mapping behavior in very weird buggy ways. * It's only used if uPnP/NAT-PMP is enabled in this build. */ - Binder _bindings[3]; unsigned int _ports[3]; - uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr - - // Sockets for JSON API -- bound only to V4 and V6 localhost - PhySocket *_v4TcpControlSocket; - PhySocket *_v6TcpControlSocket; + Binder _binder; // Time we last received a packet from a global address uint64_t _lastDirectReceiveFromGlobal; @@ -470,7 +489,8 @@ public: Mutex _nets_m; // Active TCP/IP connections - std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only + std::vector< TcpConnection * > _tcpConnections; + Mutex _tcpConnections_m; TcpConnection *_tcpFallbackTunnel; // Termination status information @@ -484,13 +504,6 @@ public: PortMapper *_portMapper; #endif - // Cluster management instance if enabled -#ifdef ZT_ENABLE_CLUSTER - PhySocket *_clusterMessageSocket; - ClusterDefinition *_clusterDefinition; - unsigned int _clusterMemberId; -#endif - // Set to false to force service to stop volatile bool _run; Mutex _run_m; @@ -499,15 +512,18 @@ public: OneServiceImpl(const char *hp,unsigned int port) : _homePath((hp) ? hp : ".") - ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S ZT_CONTROLLER_DB_PATH) + ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d") + ,_iddbPath(_homePath + ZT_PATH_SEPARATOR_S "iddb.d") + ,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d") + ,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d") ,_controller((EmbeddedNetworkController *)0) ,_phy(this,false,true) ,_node((Node *)0) ,_updater((SoftwareUpdater *)0) ,_updateAutoApply(false) ,_primaryPort(port) - ,_v4TcpControlSocket((PhySocket *)0) - ,_v6TcpControlSocket((PhySocket *)0) + ,_udpPortPickerCounter(0) + ,_clusterMemberId(0) ,_lastDirectReceiveFromGlobal(0) #ifdef ZT_TCP_FALLBACK_RELAY ,_lastSendToGlobalV4(0) @@ -520,11 +536,6 @@ public: #ifdef ZT_USE_MINIUPNPC ,_portMapper((PortMapper *)0) #endif -#ifdef ZT_ENABLE_CLUSTER - ,_clusterMessageSocket((PhySocket *)0) - ,_clusterDefinition((ClusterDefinition *)0) - ,_clusterMemberId(0) -#endif ,_run(true) { _ports[0] = 0; @@ -534,23 +545,11 @@ public: virtual ~OneServiceImpl() { - for(int i=0;i<3;++i) - _bindings[i].closeAll(_phy); - - _phy.close(_v4TcpControlSocket); - _phy.close(_v6TcpControlSocket); - -#ifdef ZT_ENABLE_CLUSTER - _phy.close(_clusterMessageSocket); -#endif - + _binder.closeAll(_phy); #ifdef ZT_USE_MINIUPNPC delete _portMapper; #endif delete _controller; -#ifdef ZT_ENABLE_CLUSTER - delete _clusterDefinition; -#endif } virtual ReasonForTermination run() @@ -576,15 +575,11 @@ public: _authToken = _trimString(_authToken); } - // Clean up any legacy files if present - OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "peers.save").c_str()); - OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "world").c_str()); - { struct ZT_Node_Callbacks cb; cb.version = 0; - cb.dataStoreGetFunction = SnodeDataStoreGetFunction; - cb.dataStorePutFunction = SnodeDataStorePutFunction; + cb.stateGetFunction = SnodeStateGetFunction; + cb.statePutFunction = SnodeStatePutFunction; cb.wirePacketSendFunction = SnodeWirePacketSendFunction; cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction; cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction; @@ -600,9 +595,10 @@ public: InetAddress trustedPathNetworks[ZT_MAX_TRUSTED_PATHS]; unsigned int trustedPathCount = 0; - // Old style "trustedpaths" flat file -- will eventually go away + // LEGACY: support old "trustedpaths" flat file FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r"); if (trustpaths) { + fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S); char buf[1024]; while ((fgets(buf,sizeof(buf),trustpaths))&&(trustedPathCount < ZT_MAX_TRUSTED_PATHS)) { int fno = 0; @@ -664,9 +660,11 @@ public: if (trustedPathCount) _node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(trustedPathNetworks),trustedPathIds,trustedPathCount); } + + // Apply other runtime configuration from local.conf applyLocalConfig(); - // Bind TCP control socket + // Make sure we can use the primary port, and hunt for one if configured to do so const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random for(int k=0;k<portTrials;++k) { if (_primaryPort == 0) { @@ -674,35 +672,8 @@ public: Utils::getSecureRandom(&randp,sizeof(randp)); _primaryPort = 20000 + (randp % 45500); } - if (_trialBind(_primaryPort)) { - struct sockaddr_in in4; - memset(&in4,0,sizeof(in4)); - in4.sin_family = AF_INET; - in4.sin_addr.s_addr = Utils::hton((uint32_t)((_allowManagementFrom.size() > 0) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1 - in4.sin_port = Utils::hton((uint16_t)_primaryPort); - _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this); - - struct sockaddr_in6 in6; - memset((void *)&in6,0,sizeof(in6)); - in6.sin6_family = AF_INET6; - in6.sin6_port = in4.sin_port; - if (_allowManagementFrom.size() == 0) - in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1 - _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this); - - // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that - // have only IPv4 or only IPv6 stacks. - if ((_v4TcpControlSocket)||(_v6TcpControlSocket)) { - _ports[0] = _primaryPort; - break; - } else { - if (_v4TcpControlSocket) - _phy.close(_v4TcpControlSocket,false); - if (_v6TcpControlSocket) - _phy.close(_v6TcpControlSocket,false); - _primaryPort = 0; - } + _ports[0] = _primaryPort; } else { _primaryPort = 0; } @@ -714,7 +685,7 @@ public: return _termReason; } - // Write file containing primary port to be read by CLIs, etc. + // Save primary port to a file so CLIs and GUIs can learn it easily char portstr[64]; Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]); OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S "zerotier-one.port").c_str(),std::string(portstr)); @@ -722,7 +693,7 @@ public: // Attempt to bind to a secondary port chosen from our ZeroTier address. // This exists because there are buggy NATs out there that fail if more // than one device behind the same NAT tries to use the same internal - // private address port number. + // private address port number. Buggy NATs are a running theme. _ports[1] = 20000 + ((unsigned int)_node->address() % 45500); for(int i=0;;++i) { if (i > 1000) { @@ -761,13 +732,11 @@ public: } #endif - // Populate ports in big-endian format for quick compare - for(int i=0;i<3;++i) - _portsBE[i] = Utils::hton((uint16_t)_ports[i]); - + // Network controller is now enabled by default for desktop and server _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str()); _node->setNetconfMaster((void *)_controller); +/* #ifdef ZT_ENABLE_CLUSTER if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str())) { _clusterDefinition = new ClusterDefinition(_node->address(),(_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str()); @@ -816,8 +785,10 @@ public: } } #endif +*/ - { // Load existing networks + // Join existing networks in networks.d + { std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str())); for(std::vector<std::string>::iterator f(networksDotD.begin());f!=networksDotD.end();++f) { std::size_t dot = f->find_last_of('.'); @@ -825,7 +796,9 @@ public: _node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0); } } - { // Load existing moons + + // Orbit existing moons in moons.d + { std::vector<std::string> moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str())); for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) { std::size_t dot = f->find_last_of('.'); @@ -834,6 +807,16 @@ public: } } + // Derive the cluster's shared secret backplane encryption key by hashing its shared secret identity + { + uint8_t tmp[64]; + SHA512::hash(tmp,_node->identity().privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN); + memcpy(_clusterKey,tmp,32); + } + _clusterMemberId = _node->prng(); + if (!_clusterMemberId) _clusterMemberId = 1; + + // Main I/O loop _nextBackgroundTaskDeadline = 0; uint64_t clockShouldBe = OSUtils::now(); _lastRestart = clockShouldBe; @@ -842,6 +825,7 @@ public: uint64_t lastUpdateCheck = clockShouldBe; uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle uint64_t lastCleanedIddb = 0; + uint64_t lastTcpCheck = 0; for(;;) { _run_m.lock(); if (!_run) { @@ -859,7 +843,7 @@ public: // Clean iddb.d on start and every 24 hours if ((now - lastCleanedIddb) > 86400000) { lastCleanedIddb = now; - OSUtils::cleanDirectory((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str(),now - ZT_IDDB_CLEANUP_AGE); + OSUtils::cleanDirectory(_iddbPath.c_str(),now - ZT_IDDB_CLEANUP_AGE); } // Attempt to detect sleep/wake events by detecting delay overruns @@ -879,11 +863,13 @@ public: // Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default) if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) { lastBindRefresh = now; + unsigned int p[3]; + unsigned int pc = 0; for(int i=0;i<3;++i) { - if (_ports[i]) { - _bindings[i].refresh(_phy,_ports[i],*this); - } + if (_ports[i]) + p[pc++] = _ports[i]; } + _binder.refresh(_phy,p,pc,*this); { Mutex::Lock _l(_nets_m); for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) { @@ -893,15 +879,18 @@ public: } } + // Run background task processor in core if it's time to do so uint64_t dl = _nextBackgroundTaskDeadline; if (dl <= now) { _node->processBackgroundTasks((void *)0,now,&_nextBackgroundTaskDeadline); dl = _nextBackgroundTaskDeadline; } + // Close TCP fallback tunnel if we have direct UDP if ((_tcpFallbackTunnel)&&((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2))) _phy.close(_tcpFallbackTunnel->sock); + // Sync multicast group memberships if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) { lastTapMulticastGroupCheck = now; Mutex::Lock _l(_nets_m); @@ -917,6 +906,7 @@ public: } } + // Sync information about physical network interfaces if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) { lastLocalInterfaceAddressCheck = now; @@ -930,19 +920,65 @@ public: } #endif - std::vector<InetAddress> boundAddrs(_bindings[0].allBoundLocalInterfaceAddresses()); + std::vector<InetAddress> boundAddrs(_binder.allBoundLocalInterfaceAddresses()); for(std::vector<InetAddress>::const_iterator i(boundAddrs.begin());i!=boundAddrs.end();++i) _node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i))); } + // Check TCP connections and cluster links + if ((now - lastTcpCheck) >= ZT_TCP_CHECK_PERIOD) { + lastTcpCheck = now; + + std::vector<PhySocket *> toClose; + std::vector<InetAddress> clusterLinksUp; + { + Mutex::Lock _l(_tcpConnections_m); + for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) { + TcpConnection *const tc = *c; + tc->writeq_m.lock(); + const unsigned long wql = (unsigned long)tc->writeq.length(); + tc->writeq_m.unlock(); + if ((tc->sock)&&((wql > ZT_TCP_MAX_WRITEQ_SIZE)||((now - tc->lastReceive) > ZT_TCP_ACTIVITY_TIMEOUT))) { + toClose.push_back(tc->sock); + } else if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) { + clusterLinksUp.push_back(tc->remoteAddr); + sendMyCurrentClusterState(tc); + } + } + } + for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s) + _phy.close(*s,true); + + { + Mutex::Lock _l(_localConfig_m); + for(std::vector<InetAddress>::const_iterator ca(_clusterBackplaneAddresses.begin());ca!=_clusterBackplaneAddresses.end();++ca) { + if (std::find(clusterLinksUp.begin(),clusterLinksUp.end(),*ca) == clusterLinksUp.end()) { + TcpConnection *tc = new TcpConnection(); + { + Mutex::Lock _l(_tcpConnections_m); + _tcpConnections.push_back(tc); + } + + tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE; + tc->remoteAddr = *ca; + tc->lastReceive = OSUtils::now(); + tc->parent = this; + tc->sock = (PhySocket *)0; // set in connect handler + tc->messageSize = 0; + + tc->clusterMemberId = 0; // not known yet + + bool connected = false; + _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&(*ca)),connected,(void *)tc,true); + } + } + } + } + const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100; clockShouldBe = now + (uint64_t)delay; _phy.poll(delay); } - } catch (std::exception &exc) { - Mutex::Lock _l(_termReason_m); - _termReason = ONE_UNRECOVERABLE_ERROR; - _fatalErrorMessage = exc.what(); } catch ( ... ) { Mutex::Lock _l(_termReason_m); _termReason = ONE_UNRECOVERABLE_ERROR; @@ -950,6 +986,7 @@ public: } try { + Mutex::Lock _l(_tcpConnections_m); while (!_tcpConnections.empty()) _phy.close((*_tcpConnections.begin())->sock); } catch ( ... ) {} @@ -991,35 +1028,35 @@ public: } #ifdef ZT_SDK - virtual void leave(const char *hp) - { - _node->leave(Utils::hexStrToU64(hp),NULL,NULL); - } + virtual void leave(const char *hp) + { + _node->leave(Utils::hexStrToU64(hp),NULL,NULL); + } virtual void join(const char *hp) { _node->join(Utils::hexStrToU64(hp),NULL,NULL); } - virtual std::string givenHomePath() - { - return _homePath; - } + virtual std::string givenHomePath() + { + return _homePath; + } - virtual EthernetTap * getTap(uint64_t nwid) - { + virtual EthernetTap * getTap(uint64_t nwid) + { Mutex::Lock _l(_nets_m); std::map<uint64_t,NetworkState>::const_iterator n(_nets.find(nwid)); if (n == _nets.end()) - return NULL; + return NULL; return n->second.tap; - } + } - virtual EthernetTap *getTap(InetAddress &addr) - { - Mutex::Lock _l(_nets_m); + virtual EthernetTap *getTap(InetAddress &addr) + { + Mutex::Lock _l(_nets_m); std::map<uint64_t,NetworkState>::iterator it; - for(it = _nets.begin(); it != _nets.end(); it++) { + for(it = _nets.begin(); it != _nets.end(); it++) { if(it->second.tap) { for(int j=0; j<it->second.tap->_ips.size(); j++) { if(it->second.tap->_ips[j].isEqualPrefix(addr) || it->second.tap->_ips[j].ipsEqual(addr) || it->second.tap->_ips[j].containsAddress(addr)) { @@ -1027,9 +1064,9 @@ public: } } } - } - return NULL; - } + } + return NULL; + } virtual Node * getNode() { @@ -1040,9 +1077,8 @@ public: { Mutex::Lock _l(_nets_m); std::map<uint64_t,NetworkState>::iterator i; - for(i = _nets.begin(); i != _nets.end(); i++) { - delete i->second.tap; - } + for(i = _nets.begin(); i != _nets.end(); i++) + delete i->second.tap; } #endif // ZT_SDK @@ -1073,8 +1109,8 @@ public: return false; n->second.settings = settings; - char nlcpath[256]; - Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid); + char nlcpath[4096]; + Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid); FILE *out = fopen(nlcpath,"w"); if (out) { fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged); @@ -1089,7 +1125,9 @@ public: return true; } - // Internal implementation methods ----------------------------------------- + // ========================================================================= + // Internal implementation methods for control plane, route setup, etc. + // ========================================================================= inline unsigned int handleControlPlaneHttpRequest( const InetAddress &fromAddress, @@ -1216,7 +1254,6 @@ public: settings["portMappingEnabled"] = false; // not supported in build #endif #ifndef ZT_SDK - settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT); settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL); #endif @@ -1224,6 +1261,7 @@ public: res["planetWorldId"] = planet.id(); res["planetWorldTimestamp"] = planet.timestamp(); +/* #ifdef ZT_ENABLE_CLUSTER json cj; ZT_ClusterStatus cs; @@ -1250,6 +1288,7 @@ public: #else res["cluster"] = json(); #endif +*/ scode = 200; } else if (ps[0] == "moon") { @@ -1770,24 +1809,237 @@ public: } // ========================================================================= - // Handlers for Node and Phy<> callbacks + // Cluster messaging functions // ========================================================================= - inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) + // mlen must be at least 24 + void encryptClusterMessage(char *data,unsigned int mlen) { -#ifdef ZT_ENABLE_CLUSTER - if (sock == _clusterMessageSocket) { - _lastDirectReceiveFromGlobal = OSUtils::now(); - _node->clusterHandleIncomingMessage(data,len); - return; + uint8_t key[32]; + memcpy(key,_clusterKey,32); + for(int i=0;i<8;++i) key[i] ^= data[i]; + Salsa20 s20(key,data + 8); + + uint8_t macKey[32]; + uint8_t mac[16]; + memset(macKey,0,32); + s20.crypt12(macKey,macKey,32); + s20.crypt12(data + 24,data + 24,mlen - 24); + Poly1305::compute(mac,data + 24,mlen - 24,macKey); + memcpy(data + 16,mac,8); + } + + void announceStatusToClusterMember(TcpConnection *tc) + { + try { + Buffer<8194> buf; + + buf.appendRandom(16); + buf.addSize(8); // space for MAC + buf.append((uint8_t)CLUSTER_MESSAGE_STATUS); + buf.append(_clusterMemberId); + buf.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR); + buf.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR); + buf.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + + std::vector<InetAddress> lif(_binder.allBoundLocalInterfaceAddresses()); + buf.append((uint16_t)lif.size()); + for(std::vector<InetAddress>::const_iterator i(lif.begin());i!=lif.end();++i) + i->serialize(buf); + + Mutex::Lock _l(tc->writeq_m); + + if (tc->writeq.length() == 0) + _phy.setNotifyWritable(tc->sock,true); + + const unsigned int mlen = buf.size(); + tc->writeq.push_back((char)((mlen >> 16) & 0xff)); + tc->writeq.push_back((char)((mlen >> 8) & 0xff)); + tc->writeq.push_back((char)(mlen & 0xff)); + + char *const data = reinterpret_cast<char *>(buf.unsafeData()); + encryptClusterMessage(data,mlen); + tc->writeq.append(data,mlen); + } catch ( ... ) { + fprintf(stderr,"WARNING: unexpected exception announcing status to cluster members" ZT_EOL_S); } -#endif + } -#ifdef ZT_BREAK_UDP - if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) - return; -#endif + bool proxySendViaCluster(const InetAddress &fromAddress,const InetAddress &dest,const void *data,unsigned int len,unsigned int ttl) + { + Mutex::Lock _l(_tcpConnections_m); + for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) { + TcpConnection *const tc = *c; + if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) { + Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m); + for(std::vector<InetAddress>::const_iterator i(tc->clusterMemberLocalAddresses.begin());i!=tc->clusterMemberLocalAddresses.end();++i) { + if (*i == fromAddress) { + Buffer<1024> buf; + + buf.appendRandom(16); + buf.addSize(8); // space for MAC + buf.append((uint8_t)CLUSTER_MESSAGE_PROXY_SEND); + buf.append((uint8_t)ttl); + dest.serialize(buf); + fromAddress.serialize(buf); + + Mutex::Lock _l3(tc->writeq_m); + + if (tc->writeq.length() == 0) + _phy.setNotifyWritable(tc->sock,true); + + const unsigned int mlen = buf.size() + len; + tc->writeq.push_back((char)((mlen >> 16) & 0xff)); + tc->writeq.push_back((char)((mlen >> 8) & 0xff)); + tc->writeq.push_back((char)(mlen & 0xff)); + + const unsigned long startpos = (unsigned long)tc->writeq.length(); + tc->writeq.append(reinterpret_cast<const char *>(buf.data()),buf.size()); + tc->writeq.append(reinterpret_cast<const char *>(data),len); + + char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos; + encryptClusterMessage(outdata,mlen); + + return true; + } + } + } + } + return false; + } + + void replicateStateObject(const ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len,TcpConnection *tc) + { + char buf[34]; + Mutex::Lock _l2(tc->writeq_m); + + if (tc->writeq.length() == 0) + _phy.setNotifyWritable(tc->sock,true); + + const unsigned int mlen = len + 34; + + tc->writeq.push_back((char)((mlen >> 16) & 0xff)); + tc->writeq.push_back((char)((mlen >> 8) & 0xff)); + tc->writeq.push_back((char)(mlen & 0xff)); + + Utils::getSecureRandom(buf,16); + buf[24] = (char)CLUSTER_MESSAGE_STATE_OBJECT; + buf[25] = (char)type; + buf[26] = (char)((id >> 56) & 0xff); + buf[27] = (char)((id >> 48) & 0xff); + buf[28] = (char)((id >> 40) & 0xff); + buf[29] = (char)((id >> 32) & 0xff); + buf[30] = (char)((id >> 24) & 0xff); + buf[31] = (char)((id >> 16) & 0xff); + buf[32] = (char)((id >> 8) & 0xff); + buf[33] = (char)(id & 0xff); + + const unsigned long startpos = (unsigned long)tc->writeq.length(); + tc->writeq.append(buf,34); + tc->writeq.append(reinterpret_cast<const char *>(data),len); + + char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos; + encryptClusterMessage(outdata,mlen); + } + + void replicateStateObjectToCluster(const ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len,const uint64_t everyoneBut) + { + std::vector<uint64_t> sentTo; + if (everyoneBut) + sentTo.push_back(everyoneBut); + Mutex::Lock _l(_tcpConnections_m); + for(std::vector<TcpConnection *>::const_iterator ci(_tcpConnections.begin());ci!=_tcpConnections.end();++ci) { + TcpConnection *const c = *ci; + if ((c->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(c->clusterMemberId != 0)&&(std::find(sentTo.begin(),sentTo.end(),c->clusterMemberId) == sentTo.end())) { + sentTo.push_back(c->clusterMemberId); + replicateStateObject(type,id,data,len,c); + } + } + } + + void writeStateObject(enum ZT_StateObjectType type,uint64_t id,const void *data,int len) + { + char p[4096]; + bool secure = false; + switch(type) { + case ZT_STATE_OBJECT_IDENTITY_PUBLIC: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str()); + break; + case ZT_STATE_OBJECT_IDENTITY_SECRET: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str()); + secure = true; + break; + case ZT_STATE_OBJECT_PEER_IDENTITY: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "iddb.d/%.10llx",_homePath.c_str(),(unsigned long long)id); + break; + case ZT_STATE_OBJECT_NETWORK_CONFIG: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id); + secure = true; + break; + case ZT_STATE_OBJECT_PLANET: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); + break; + case ZT_STATE_OBJECT_MOON: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id); + break; + default: + p[0] = (char)0; + break; + } + if (p[0]) { + if (len >= 0) { + FILE *f = fopen(p,"w"); + if (f) { + if (fwrite(data,len,1,f) != 1) + fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p); + fclose(f); + if (secure) + OSUtils::lockDownFile(p,false); + } else { + fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p); + } + } else { + OSUtils::rm(p); + } + } + } + + void sendMyCurrentClusterState(TcpConnection *tc) + { + // We currently don't need to dump everything. Networks and moons are most important. + // The rest will get caught up rapidly due to constant peer updates, etc. + std::string buf; + std::vector<std::string> l(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str(),false)); + for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) { + buf.clear(); + if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) { + if (f->length() == 21) { + const uint64_t nwid = Utils::hexStrToU64(f->substr(0,16).c_str()); + if (nwid) + replicateStateObject(ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,buf.data(),(int)buf.length(),tc); + } + } + } + l = OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "moons.d").c_str(),false); + for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) { + buf.clear(); + if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) { + if (f->length() == 21) { + const uint64_t moonId = Utils::hexStrToU64(f->substr(0,16).c_str()); + if (moonId) + replicateStateObject(ZT_STATE_OBJECT_MOON,moonId,buf.data(),(int)buf.length(),tc); + } + } + } + } + + // ========================================================================= + // Handlers for Node and Phy<> callbacks + // ========================================================================= + + inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) + { if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) _lastDirectReceiveFromGlobal = OSUtils::now(); @@ -1811,38 +2063,33 @@ public: inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) { - if (!success) + if (!success) { + phyOnTcpClose(sock,uptr); return; + } - // Outgoing TCP connections are always TCP fallback tunnel connections. - - TcpConnection *tc = new TcpConnection(); - _tcpConnections.insert(tc); - - tc->type = TcpConnection::TCP_TUNNEL_OUTGOING; - tc->shouldKeepAlive = true; - tc->parent = this; + TcpConnection *const tc = reinterpret_cast<TcpConnection *>(*uptr); + if (!tc) { // sanity check + _phy.close(sock,true); + return; + } tc->sock = sock; - // from and parser are not used - tc->messageSize = 0; // unused - tc->lastActivity = OSUtils::now(); - // HTTP stuff is not used - tc->writeBuf = ""; - *uptr = (void *)tc; - - // Send "hello" message - tc->writeBuf.push_back((char)0x17); - tc->writeBuf.push_back((char)0x03); - tc->writeBuf.push_back((char)0x03); // fake TLS 1.2 header - tc->writeBuf.push_back((char)0x00); - tc->writeBuf.push_back((char)0x04); // mlen == 4 - tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MAJOR); - tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MINOR); - tc->writeBuf.push_back((char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff)); - tc->writeBuf.push_back((char)(ZEROTIER_ONE_VERSION_REVISION & 0xff)); - _phy.setNotifyWritable(sock,true); - - _tcpFallbackTunnel = tc; + + if (tc->type == TcpConnection::TCP_TUNNEL_OUTGOING) { + if (_tcpFallbackTunnel) + _phy.close(_tcpFallbackTunnel->sock); + _tcpFallbackTunnel = tc; + _phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO)); + } else if (tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE) { + { + Mutex::Lock _l(tc->writeq_m); + tc->writeq.push_back((char)0x93); // identifies type of connection as cluster backplane + } + announceStatusToClusterMember(tc); + _phy.setNotifyWritable(sock,true); + } else { + _phy.close(sock,true); + } } inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) @@ -1852,149 +2099,321 @@ public: return; } else { TcpConnection *tc = new TcpConnection(); - _tcpConnections.insert(tc); - tc->type = TcpConnection::TCP_HTTP_INCOMING; - tc->shouldKeepAlive = true; + { + Mutex::Lock _l(_tcpConnections_m); + _tcpConnections.push_back(tc); + } + + tc->type = TcpConnection::TCP_UNCATEGORIZED_INCOMING; tc->parent = this; tc->sock = sockN; - tc->from = from; + tc->remoteAddr = from; + tc->lastReceive = OSUtils::now(); http_parser_init(&(tc->parser),HTTP_REQUEST); tc->parser.data = (void *)tc; tc->messageSize = 0; - tc->lastActivity = OSUtils::now(); - tc->currentHeaderField = ""; - tc->currentHeaderValue = ""; - tc->url = ""; - tc->status = ""; - tc->headers.clear(); - tc->body = ""; - tc->writeBuf = ""; + *uptrN = (void *)tc; } } - inline void phyOnTcpClose(PhySocket *sock,void **uptr) + void phyOnTcpClose(PhySocket *sock,void **uptr) { TcpConnection *tc = (TcpConnection *)*uptr; if (tc) { - if (tc == _tcpFallbackTunnel) + if (tc == _tcpFallbackTunnel) { _tcpFallbackTunnel = (TcpConnection *)0; - _tcpConnections.erase(tc); + } + { + Mutex::Lock _l(_tcpConnections_m); + _tcpConnections.erase(std::remove(_tcpConnections.begin(),_tcpConnections.end(),tc),_tcpConnections.end()); + } delete tc; } } - inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) + void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) { - TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); - switch(tc->type) { + try { + if (!len) return; // sanity check, should never happen + TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); + tc->lastReceive = OSUtils::now(); + switch(tc->type) { + + case TcpConnection::TCP_UNCATEGORIZED_INCOMING: + switch(reinterpret_cast<uint8_t *>(data)[0]) { + // 0x93 is first byte of cluster backplane connections + case 0x93: { + // We only allow this from cluster backplane IPs. We also authenticate + // each packet cryptographically, so this is just a first line of defense. + bool allow = false; + { + Mutex::Lock _l(_localConfig_m); + for(std::vector< InetAddress >::const_iterator i(_clusterBackplaneAddresses.begin());i!=_clusterBackplaneAddresses.end();++i) { + if (tc->remoteAddr.ipsEqual(*i)) { + allow = true; + break; + } + } + } + if (allow) { + tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE; + tc->clusterMemberId = 0; // unknown, waiting for first status message + announceStatusToClusterMember(tc); + if (len > 1) + phyOnTcpData(sock,uptr,reinterpret_cast<uint8_t *>(data) + 1,len - 1); + } else { + _phy.close(sock); + } + } break; + + // HTTP: GET, PUT, POST, HEAD + case 'G': + case 'P': + case 'H': { + // This is only allowed from IPs permitted to access the management + // backplane, which is just 127.0.0.1/::1 unless otherwise configured. + bool allow; + { + Mutex::Lock _l(_localConfig_m); + if (_allowManagementFrom.size() == 0) { + allow = (tc->remoteAddr.ipScope() == InetAddress::IP_SCOPE_LOOPBACK); + } else { + allow = false; + for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) { + if (i->containsAddress(tc->remoteAddr)) { + allow = true; + break; + } + } + } + } + if (allow) { + tc->type = TcpConnection::TCP_HTTP_INCOMING; + phyOnTcpData(sock,uptr,data,len); + } else { + _phy.close(sock); + } + } break; - case TcpConnection::TCP_HTTP_INCOMING: - case TcpConnection::TCP_HTTP_OUTGOING: - http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len); - if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK)) { - _phy.close(sock); + // Drop unknown protocols + default: + _phy.close(sock); + break; + } + return; + + case TcpConnection::TCP_HTTP_INCOMING: + case TcpConnection::TCP_HTTP_OUTGOING: + http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len); + if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK)) + _phy.close(sock); return; - } - break; - case TcpConnection::TCP_TUNNEL_OUTGOING: - tc->body.append((const char *)data,len); - while (tc->body.length() >= 5) { - const char *data = tc->body.data(); - const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) ); - if (tc->body.length() >= (mlen + 5)) { - InetAddress from; + case TcpConnection::TCP_TUNNEL_OUTGOING: + tc->readq.append((const char *)data,len); + while (tc->readq.length() >= 5) { + const char *data = tc->readq.data(); + const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) ); + if (tc->readq.length() >= (mlen + 5)) { + InetAddress from; unsigned long plen = mlen; // payload length, modified if there's an IP header - data += 5; // skip forward past pseudo-TLS junk and mlen - if (plen == 4) { - // Hello message, which isn't sent by proxy and would be ignored by client - } else if (plen) { - // Messages should contain IPv4 or IPv6 source IP address data - switch(data[0]) { - case 4: // IPv4 - if (plen >= 7) { - from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff)); - data += 7; // type + 4 byte IP + 2 byte port - plen -= 7; - } else { + data += 5; // skip forward past pseudo-TLS junk and mlen + if (plen == 4) { + // Hello message, which isn't sent by proxy and would be ignored by client + } else if (plen) { + // Messages should contain IPv4 or IPv6 source IP address data + switch(data[0]) { + case 4: // IPv4 + if (plen >= 7) { + from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff)); + data += 7; // type + 4 byte IP + 2 byte port + plen -= 7; + } else { + _phy.close(sock); + return; + } + break; + case 6: // IPv6 + if (plen >= 19) { + from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff)); + data += 19; // type + 16 byte IP + 2 byte port + plen -= 19; + } else { + _phy.close(sock); + return; + } + break; + case 0: // none/omitted + ++data; + --plen; + break; + default: // invalid address type _phy.close(sock); return; - } - break; - case 6: // IPv6 - if (plen >= 19) { - from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff)); - data += 19; // type + 16 byte IP + 2 byte port - plen -= 19; - } else { + } + + if (from) { + InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff); + const ZT_ResultCode rc = _node->processWirePacket( + (void *)0, + OSUtils::now(), + reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress), + reinterpret_cast<struct sockaddr_storage *>(&from), + data, + plen, + &_nextBackgroundTaskDeadline); + if (ZT_ResultCode_isFatal(rc)) { + char tmp[256]; + Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc); + Mutex::Lock _l(_termReason_m); + _termReason = ONE_UNRECOVERABLE_ERROR; + _fatalErrorMessage = tmp; + this->terminate(); _phy.close(sock); return; } + } + } + + if (tc->readq.length() > (mlen + 5)) + tc->readq.erase(tc->readq.begin(),tc->readq.begin() + (mlen + 5)); + else tc->readq.clear(); + } else break; + } + return; + + case TcpConnection::TCP_CLUSTER_BACKPLANE: + tc->readq.append((const char *)data,len); + if (tc->readq.length() >= 28) { // got 3-byte message size + 16-byte IV + 8-byte MAC + 1-byte type (encrypted) + uint8_t *data = reinterpret_cast<uint8_t *>(const_cast<char *>(tc->readq.data())); + unsigned long mlen = ( ((unsigned long)data[0] << 16) | ((unsigned long)data[1] << 8) | (unsigned long)data[2] ); + if ((mlen < 25)||(mlen > ZT_TCP_MAX_WRITEQ_SIZE)) { + _phy.close(sock); + return; + } else if (tc->readq.length() >= (mlen + 3)) { // got entire message + data += 3; + + uint8_t key[32]; + memcpy(key,_clusterKey,32); + for(int i=0;i<8;++i) key[i] ^= data[i]; // first 8 bytes of IV get XORed with key + Salsa20 s20(key,data + 8); // last 8 bytes of IV are fed into Salsa20 directly as its 64-bit IV + + uint8_t macKey[32]; + uint8_t mac[16]; + memset(macKey,0,32); + s20.crypt12(macKey,macKey,32); + Poly1305::compute(mac,data + 24,mlen - 24,macKey); + if (!Utils::secureEq(mac,data + 16,8)) { + _phy.close(sock); + return; + } + s20.crypt12(data + 24,data + 24,mlen - 24); + + switch((ClusterMessageType)data[24]) { + case CLUSTER_MESSAGE_STATUS: + if (mlen > (25 + 16)) { + Buffer<4096> tmp(data + 25,mlen - 25); + try { + const uint64_t cmid = tmp.at<uint64_t>(0); + if (cmid == _clusterMemberId) { // shouldn't happen, but don't allow self-to-self + _phy.close(sock); + return; + } + if (!tc->clusterMemberId) { + tc->clusterMemberId = cmid; + sendMyCurrentClusterState(tc); + } + tc->clusterMemberVersionMajor = tmp.at<uint16_t>(8); + tc->clusterMemberVersionMinor = tmp.at<uint16_t>(10); + tc->clusterMemberVersionRev = tmp.at<uint16_t>(12); + const unsigned int clusterMemberLocalAddressCount = tmp.at<uint16_t>(14); + std::vector<InetAddress> la; + unsigned int ptr = 16; + for(unsigned int k=0;k<clusterMemberLocalAddressCount;++k) { + la.push_back(InetAddress()); + ptr += la.back().deserialize(tmp,ptr); + } + { + Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m); + tc->clusterMemberLocalAddresses.swap(la); + } + } catch ( ... ) {} + } break; - case 0: // none/omitted - ++data; - --plen; + + case CLUSTER_MESSAGE_STATE_OBJECT: + if (mlen >= (25 + 9)) { // type + object ID + [data] + const uint64_t objId = ( + ((uint64_t)data[26] << 56) | + ((uint64_t)data[27] << 48) | + ((uint64_t)data[28] << 40) | + ((uint64_t)data[29] << 32) | + ((uint64_t)data[30] << 24) | + ((uint64_t)data[31] << 16) | + ((uint64_t)data[32] << 8) | + (uint64_t)data[33] + ); + if (_node->processStateUpdate((void *)0,(ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34)) == ZT_RESULT_OK) { + writeStateObject((ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34)); + replicateStateObjectToCluster((ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34),tc->clusterMemberId); + } + } break; - default: // invalid address type - _phy.close(sock); - return; - } - if (from) { - InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff); - const ZT_ResultCode rc = _node->processWirePacket( - (void *)0, - OSUtils::now(), - reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress), - reinterpret_cast<struct sockaddr_storage *>(&from), - data, - plen, - &_nextBackgroundTaskDeadline); - if (ZT_ResultCode_isFatal(rc)) { - char tmp[256]; - Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc); - Mutex::Lock _l(_termReason_m); - _termReason = ONE_UNRECOVERABLE_ERROR; - _fatalErrorMessage = tmp; - this->terminate(); - _phy.close(sock); - return; - } + case CLUSTER_MESSAGE_PROXY_SEND: + if (mlen > 25) { + Buffer<4096> tmp(data + 25,mlen - 25); + try { + InetAddress dest,src; + const unsigned int ttl = (unsigned int)tmp[0]; + unsigned int ptr = 1; + ptr += dest.deserialize(tmp); + ptr += src.deserialize(tmp,ptr); + if (ptr < tmp.size()) + _binder.udpSend(_phy,src,dest,reinterpret_cast<const uint8_t *>(tmp.data()) + ptr,tmp.size() - ptr,ttl); + } catch ( ... ) {} + } + break; } - } - if (tc->body.length() > (mlen + 5)) - tc->body = tc->body.substr(mlen + 5); - else tc->body = ""; - } else break; - } - break; + tc->readq.erase(tc->readq.begin(),tc->readq.begin() + mlen); + } + } + return; + } + } catch ( ... ) { + _phy.close(sock); } } inline void phyOnTcpWritable(PhySocket *sock,void **uptr) { TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); - Mutex::Lock _l(tc->writeBuf_m); - if (tc->writeBuf.length() > 0) { - long sent = (long)_phy.streamSend(sock,tc->writeBuf.data(),(unsigned long)tc->writeBuf.length(),true); - if (sent > 0) { - tc->lastActivity = OSUtils::now(); - if ((unsigned long)sent >= (unsigned long)tc->writeBuf.length()) { - tc->writeBuf = ""; - _phy.setNotifyWritable(sock,false); - if (!tc->shouldKeepAlive) - _phy.close(sock); // will call close handler to delete from _tcpConnections - } else { - tc->writeBuf = tc->writeBuf.substr(sent); + bool closeit = false; + { + Mutex::Lock _l(tc->writeq_m); + if (tc->writeq.length() > 0) { + long sent = (long)_phy.streamSend(sock,tc->writeq.data(),(unsigned long)tc->writeq.length(),true); + if (sent > 0) { + if ((unsigned long)sent >= (unsigned long)tc->writeq.length()) { + tc->writeq.clear(); + _phy.setNotifyWritable(sock,false); + + if (tc->type == TcpConnection::TCP_HTTP_INCOMING) + closeit = true; // HTTP keep alive not supported + } else { + tc->writeq.erase(tc->writeq.begin(),tc->writeq.begin() + sent); + } } + } else { + _phy.setNotifyWritable(sock,false); } - } else { - _phy.setNotifyWritable(sock,false); } + if (closeit) + _phy.close(sock); } inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} @@ -2155,80 +2574,51 @@ public: } } - inline long nodeDataStoreGetFunction(const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize) + inline void nodeStatePutFunction(enum ZT_StateObjectType type,uint64_t id,const void *data,int len) { - std::string p(_dataStorePrepPath(name)); - if (!p.length()) - return -2; - - FILE *f = fopen(p.c_str(),"rb"); - if (!f) - return -1; - if (fseek(f,0,SEEK_END) != 0) { - fclose(f); - return -2; - } - long ts = ftell(f); - if (ts < 0) { - fclose(f); - return -2; - } - *totalSize = (unsigned long)ts; - if (fseek(f,(long)readIndex,SEEK_SET) != 0) { - fclose(f); - return -2; - } - long n = (long)fread(buf,1,bufSize,f); - fclose(f); - return n; + writeStateObject(type,id,data,len); + replicateStateObjectToCluster(type,id,data,len,0); } - inline int nodeDataStorePutFunction(const char *name,const void *data,unsigned long len,int secure) + inline int nodeStateGetFunction(enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen) { - std::string p(_dataStorePrepPath(name)); - if (!p.length()) - return -2; - - if (!data) { - OSUtils::rm(p.c_str()); - return 0; + char p[4096]; + switch(type) { + case ZT_STATE_OBJECT_IDENTITY_PUBLIC: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str()); + break; + case ZT_STATE_OBJECT_IDENTITY_SECRET: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str()); + break; + case ZT_STATE_OBJECT_PEER_IDENTITY: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "iddb.d/%.10llx",_homePath.c_str(),(unsigned long long)id); + break; + case ZT_STATE_OBJECT_NETWORK_CONFIG: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id); + break; + case ZT_STATE_OBJECT_PLANET: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); + break; + case ZT_STATE_OBJECT_MOON: + Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id); + break; + default: + return -1; } - - FILE *f = fopen(p.c_str(),"wb"); - if (!f) - return -1; - if (fwrite(data,len,1,f) == 1) { - fclose(f); - if (secure) - OSUtils::lockDownFile(p.c_str(),false); - return 0; - } else { + FILE *f = fopen(p,"r"); + if (f) { + int n = (int)fread(data,1,maxlen,f); fclose(f); - OSUtils::rm(p.c_str()); - return -1; + if (n >= 0) + return n; } + return -1; } inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl) { - unsigned int fromBindingNo = 0; - - if (addr->ss_family == AF_INET) { - if (reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == 0) { - // If sender is sending from wildcard (null address), choose the secondary backup - // port 1/4 of the time. (but only for IPv4) - fromBindingNo = (++_udpPortPickerCounter & 0x4) >> 2; - if (!_ports[fromBindingNo]) - fromBindingNo = 0; - } else { - const uint16_t lp = reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port; - if (lp == _portsBE[1]) - fromBindingNo = 1; - else if (lp == _portsBE[2]) - fromBindingNo = 2; - } - #ifdef ZT_TCP_FALLBACK_RELAY + if (addr->ss_family == AF_INET) { // TCP fallback tunnel support, currently IPv4 only if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) { // Engage TCP tunnel fallback if we haven't received anything valid from a global @@ -2237,46 +2627,59 @@ public: const uint64_t now = OSUtils::now(); if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) { if (_tcpFallbackTunnel) { - Mutex::Lock _l(_tcpFallbackTunnel->writeBuf_m); - if (!_tcpFallbackTunnel->writeBuf.length()) + Mutex::Lock _l(_tcpFallbackTunnel->writeq_m); + if (_tcpFallbackTunnel->writeq.length() == 0) _phy.setNotifyWritable(_tcpFallbackTunnel->sock,true); - unsigned long mlen = len + 7; - _tcpFallbackTunnel->writeBuf.push_back((char)0x17); - _tcpFallbackTunnel->writeBuf.push_back((char)0x03); - _tcpFallbackTunnel->writeBuf.push_back((char)0x03); // fake TLS 1.2 header - _tcpFallbackTunnel->writeBuf.push_back((char)((mlen >> 8) & 0xff)); - _tcpFallbackTunnel->writeBuf.push_back((char)(mlen & 0xff)); - _tcpFallbackTunnel->writeBuf.push_back((char)4); // IPv4 - _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4); - _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2); - _tcpFallbackTunnel->writeBuf.append((const char *)data,len); + const unsigned long mlen = len + 7; + _tcpFallbackTunnel->writeq.push_back((char)0x17); + _tcpFallbackTunnel->writeq.push_back((char)0x03); + _tcpFallbackTunnel->writeq.push_back((char)0x03); // fake TLS 1.2 header + _tcpFallbackTunnel->writeq.push_back((char)((mlen >> 8) & 0xff)); + _tcpFallbackTunnel->writeq.push_back((char)(mlen & 0xff)); + _tcpFallbackTunnel->writeq.push_back((char)4); // IPv4 + _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4); + _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2); + _tcpFallbackTunnel->writeq.append((const char *)data,len); } else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) { - bool connected = false; const InetAddress addr(ZT_TCP_FALLBACK_RELAY); - _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected); + TcpConnection *tc = new TcpConnection(); + { + Mutex::Lock _l(_tcpConnections_m); + _tcpConnections.push_back(tc); + } + tc->type = TcpConnection::TCP_TUNNEL_OUTGOING; + tc->remoteAddr = addr; + tc->lastReceive = OSUtils::now(); + tc->parent = this; + tc->sock = (PhySocket *)0; // set in connect handler + tc->messageSize = 0; + bool connected = false; + _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected,(void *)tc,true); } } _lastSendToGlobalV4 = now; } -#endif // ZT_TCP_FALLBACK_RELAY - } else if (addr->ss_family == AF_INET6) { - if (reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port != 0) { - const uint16_t lp = reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port; - if (lp == _portsBE[1]) - fromBindingNo = 1; - else if (lp == _portsBE[2]) - fromBindingNo = 2; - } - } else { - return -1; } + // Even when relaying we still send via UDP. This way if UDP starts + // working we can instantly "fail forward" to it and stop using TCP + // proxy fallback, which is slow. +#endif // ZT_TCP_FALLBACK_RELAY -#ifdef ZT_BREAK_UDP - if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) - return 0; // silently break UDP -#endif + switch (_binder.udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) { + case -1: // local bound address not found, so see if a cluster peer owns it + if (localAddr->ss_family != 0) { + return (proxySendViaCluster(*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1; + } else { + return -1; // failure + } + break; + + case 0: // failure + return -1; - return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1; + default: // success + return 0; + } } inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) @@ -2362,39 +2765,22 @@ public: inline void onHttpRequestToServer(TcpConnection *tc) { - char tmpn[256]; + char tmpn[4096]; std::string data; std::string contentType("text/plain"); // default if not changed in handleRequest() unsigned int scode = 404; - bool allow; - { - Mutex::Lock _l(_localConfig_m); - if (_allowManagementFrom.size() == 0) { - allow = (tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK); - } else { - allow = false; - for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) { - if (i->containsAddress(tc->from)) { - allow = true; - break; - } - } - } - } + // Note that we check allowed IP ranges when HTTP connections are first detected in + // phyOnTcpData(). If we made it here the source IP is okay. - if (allow) { - try { - scode = handleControlPlaneHttpRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType); - } catch (std::exception &exc) { - fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what()); - scode = 500; - } catch ( ... ) { - fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S); - scode = 500; - } - } else { - scode = 401; + try { + scode = handleControlPlaneHttpRequest(tc->remoteAddr,tc->parser.method,tc->url,tc->headers,tc->readq,data,contentType); + } catch (std::exception &exc) { + fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what()); + scode = 500; + } catch ( ... ) { + fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S); + scode = 500; } const char *scodestr; @@ -2410,19 +2796,16 @@ public: default: scodestr = "Error"; break; } - Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n",scode,scodestr); + Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: %s\r\nContent-Length: %lu\r\nConnection: close\r\n\r\n", + scode, + scodestr, + contentType.c_str(), + (unsigned long)data.length()); { - Mutex::Lock _l(tc->writeBuf_m); - tc->writeBuf.assign(tmpn); - tc->writeBuf.append("Content-Type: "); - tc->writeBuf.append(contentType); - Utils::snprintf(tmpn,sizeof(tmpn),"\r\nContent-Length: %lu\r\n",(unsigned long)data.length()); - tc->writeBuf.append(tmpn); - if (!tc->shouldKeepAlive) - tc->writeBuf.append("Connection: close\r\n"); - tc->writeBuf.append("\r\n"); + Mutex::Lock _l(tc->writeq_m); + tc->writeq = tmpn; if (tc->parser.method != HTTP_HEAD) - tc->writeBuf.append(data); + tc->writeq.append(data); } _phy.setNotifyWritable(tc->sock,true); @@ -2430,8 +2813,7 @@ public: inline void onHttpResponseFromClient(TcpConnection *tc) { - if (!tc->shouldKeepAlive) - _phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections + _phy.close(tc->sock); } bool shouldBindInterface(const char *ifname,const InetAddress &ifaddr) @@ -2475,23 +2857,6 @@ public: return true; } - std::string _dataStorePrepPath(const char *name) const - { - std::string p(_homePath); - p.push_back(ZT_PATH_SEPARATOR); - char lastc = (char)0; - for(const char *n=name;(*n);++n) { - if ((*n == '.')&&(lastc == '.')) - return std::string(); // don't allow ../../ stuff as a precaution - if (*n == '/') { - OSUtils::mkdir(p.c_str()); - p.push_back(ZT_PATH_SEPARATOR); - } else p.push_back(*n); - lastc = *n; - } - return p; - } - bool _trialBind(unsigned int port) { struct sockaddr_in in4; @@ -2532,10 +2897,10 @@ static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); } static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData) { reinterpret_cast<OneServiceImpl *>(uptr)->nodeEventCallback(event,metaData); } -static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize) -{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); } -static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure) -{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); } +static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,const void *data,int len) +{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); } +static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen) +{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); } static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl) { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); } static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) @@ -2544,22 +2909,6 @@ static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t z { return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); } static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result) { return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); } - -#ifdef ZT_ENABLE_CLUSTER -static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len) -{ - OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr); - const ClusterDefinition::MemberDefinition &md = (*(impl->_clusterDefinition))[toMemberId]; - if (md.clusterEndpoint) - impl->_phy.udpSend(impl->_clusterMessageSocket,reinterpret_cast<const struct sockaddr *>(&(md.clusterEndpoint)),data,len); -} -static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z) -{ - OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr); - return (int)(impl->_clusterDefinition->geo().locate(*(reinterpret_cast<const InetAddress *>(addr)),*x,*y,*z)); -} -#endif - static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast<OneServiceImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } @@ -2569,10 +2918,10 @@ static int ShttpOnMessageBegin(http_parser *parser) tc->currentHeaderField = ""; tc->currentHeaderValue = ""; tc->messageSize = 0; - tc->url = ""; - tc->status = ""; + tc->url.clear(); + tc->status.clear(); tc->headers.clear(); - tc->body = ""; + tc->readq.clear(); return 0; } static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length) @@ -2589,16 +2938,7 @@ static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length) #else static int ShttpOnStatus(http_parser *parser) #endif -{ - /* - TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data); - tc->messageSize += (unsigned long)length; - if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) - return -1; - tc->status.append(ptr,length); - */ - return 0; -} +{ return 0; } static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length) { TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data); @@ -2636,14 +2976,12 @@ static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length) tc->messageSize += (unsigned long)length; if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) return -1; - tc->body.append(ptr,length); + tc->readq.append(ptr,length); return 0; } static int ShttpOnMessageComplete(http_parser *parser) { TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data); - tc->shouldKeepAlive = (http_should_keep_alive(parser) != 0); - tc->lastActivity = OSUtils::now(); if (tc->type == TcpConnection::TCP_HTTP_INCOMING) { tc->parent->onHttpRequestToServer(tc); } else { diff --git a/service/OneService.hpp b/service/OneService.hpp index b770a3c0..eba10ca0 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -33,10 +33,10 @@ #include "../node/InetAddress.hpp" #ifdef ZT_SDK - #include "../node/Node.hpp" - // Use the virtual netcon endpoint instead of a tun/tap port driver - #include "../src/SocketTap.hpp" - namespace ZeroTier { typedef SocketTap EthernetTap; } +#include "../node/Node.hpp" +// Use the virtual netcon endpoint instead of a tun/tap port driver +#include "../src/SocketTap.hpp" +namespace ZeroTier { typedef SocketTap EthernetTap; } #endif namespace ZeroTier { @@ -147,42 +147,15 @@ public: virtual std::string portDeviceName(uint64_t nwid) const = 0; #ifdef ZT_SDK - /** - * Leaves a network - */ - virtual void leave(const char *hp) = 0; - - /** - * Joins a network - */ + virtual void leave(const char *hp) = 0; virtual void join(const char *hp) = 0; - - /** - * Returns the homePath given by the client application - */ - virtual std::string givenHomePath() = 0; - - /* - * Returns a SocketTap that is associated with the given nwid - */ - virtual EthernetTap * getTap(uint64_t nwid) = 0; - - /* - * Returns a SocketTap that cant function as a route to the specified host - */ - virtual EthernetTap * getTap(InetAddress &addr) = 0; - - /* - * Returns a pointer to the Node - */ + virtual std::string givenHomePath() = 0; + virtual EthernetTap * getTap(uint64_t nwid) = 0; + virtual EthernetTap * getTap(InetAddress &addr) = 0; virtual Node * getNode() = 0; - - /* - * Delete all SocketTap interfaces - */ virtual void removeNets() = 0; #endif - + /** * Terminate background service (can be called from other threads) */ |