summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cluster.cpp (renamed from node/Cluster.cpp)0
-rw-r--r--Cluster.hpp (renamed from node/Cluster.hpp)0
-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--controller/EmbeddedNetworkController.cpp26
-rw-r--r--controller/JSONDB.cpp10
-rw-r--r--include/ZeroTierOne.h419
-rw-r--r--make-linux.mk4
-rw-r--r--make-mac.mk2
-rw-r--r--node/Address.hpp4
-rw-r--r--node/Buffer.hpp53
-rw-r--r--node/Constants.hpp6
-rw-r--r--node/Dictionary.hpp4
-rw-r--r--node/Hashtable.hpp7
-rw-r--r--node/IncomingPacket.cpp1
-rw-r--r--node/InetAddress.cpp8
-rw-r--r--node/MAC.hpp2
-rw-r--r--node/MulticastGroup.hpp2
-rw-r--r--node/Network.cpp59
-rw-r--r--node/Network.hpp3
-rw-r--r--node/NetworkConfig.cpp2
-rw-r--r--node/Node.cpp295
-rw-r--r--node/Node.hpp64
-rw-r--r--node/Peer.cpp1
-rw-r--r--node/Switch.cpp1
-rw-r--r--node/Topology.cpp70
-rw-r--r--node/Utils.cpp5
-rw-r--r--node/Utils.hpp3
-rw-r--r--objects.mk1
-rw-r--r--one.cpp20
-rw-r--r--osdep/BSDEthernetTap.cpp16
-rw-r--r--osdep/Binder.hpp220
-rw-r--r--osdep/Http.cpp4
-rw-r--r--osdep/LinuxEthernetTap.cpp10
-rw-r--r--osdep/OSUtils.cpp8
-rw-r--r--osdep/OSXEthernetTap.cpp14
-rw-r--r--osdep/PortMapper.cpp4
-rw-r--r--osdep/WindowsEthernetTap.cpp10
-rw-r--r--selftest.cpp2
-rw-r--r--service/ClusterDefinition.hpp2
-rw-r--r--service/OneService.cpp1399
-rw-r--r--service/OneService.hpp45
-rw-r--r--service/SoftwareUpdater.cpp2
54 files changed, 1518 insertions, 1290 deletions
diff --git a/node/Cluster.cpp b/Cluster.cpp
index 119aec29..119aec29 100644
--- a/node/Cluster.cpp
+++ b/Cluster.cpp
diff --git a/node/Cluster.hpp b/Cluster.hpp
index 74b091f5..74b091f5 100644
--- a/node/Cluster.hpp
+++ b/Cluster.hpp
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/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
index e2eaa788..85c759e7 100644
--- a/controller/EmbeddedNetworkController.cpp
+++ b/controller/EmbeddedNetworkController.cpp
@@ -122,12 +122,12 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
break;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
r["type"] = "MATCH_MAC_SOURCE";
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
r["mac"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
r["type"] = "MATCH_MAC_DEST";
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)rule.v.mac[0],(unsigned int)rule.v.mac[1],(unsigned int)rule.v.mac[2],(unsigned int)rule.v.mac[3],(unsigned int)rule.v.mac[4],(unsigned int)rule.v.mac[5]);
r["mac"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
@@ -179,7 +179,7 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
break;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
r["type"] = "MATCH_CHARACTERISTICS";
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",rule.v.characteristics);
r["mask"] = tmp;
break;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
@@ -514,7 +514,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
_db.eachMember(nwid,[&responseBody](uint64_t networkId,uint64_t nodeId,const json &member) {
if ((member.is_object())&&(member.size() > 0)) {
char tmp[128];
- Utils::snprintf(tmp,sizeof(tmp),"%s%.10llx\":%llu",(responseBody.length() > 1) ? ",\"" : "\"",(unsigned long long)nodeId,(unsigned long long)OSUtils::jsonInt(member["revision"],0));
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s%.10llx\":%llu",(responseBody.length() > 1) ? ",\"" : "\"",(unsigned long long)nodeId,(unsigned long long)OSUtils::jsonInt(member["revision"],0));
responseBody.append(tmp);
}
});
@@ -548,7 +548,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
for(std::vector<uint64_t>::const_iterator i(networkIds.begin());i!=networkIds.end();++i) {
if (responseBody.length() > 1)
responseBody.push_back(',');
- Utils::snprintf(tmp,sizeof(tmp),"\"%.16llx\"",(unsigned long long)*i);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"\"%.16llx\"",(unsigned long long)*i);
responseBody.append(tmp);
}
responseBody.push_back(']');
@@ -562,7 +562,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET(
// Controller status
char tmp[4096];
- Utils::snprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now());
responseBody = tmp;
responseContentType = "application/json";
return 200;
@@ -603,14 +603,14 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+ Utils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
- Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address);
+ Utils::ztsnprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)address);
json member;
_db.getNetworkMember(nwid,address,member);
@@ -748,7 +748,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
if (!nwid)
return 503;
}
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
+ Utils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
json network;
_db.getNetwork(nwid,network);
@@ -995,7 +995,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
_queue.post(qe);
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"{\"clock\":%llu,\"ping\":%s}",(unsigned long long)now,OSUtils::jsonDump(b).c_str());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"{\"clock\":%llu,\"ping\":%s}",(unsigned long long)now,OSUtils::jsonDump(b).c_str());
responseBody = tmp;
responseContentType = "application/json";
@@ -1083,7 +1083,7 @@ void EmbeddedNetworkController::threadMain()
auto ms = this->_memberStatus.find(_MemberStatusKey(networkId,nodeId));
if (ms != _memberStatus.end())
lrt = ms->second.lastRequestTime;
- Utils::snprintf(tmp,sizeof(tmp),"%s\"%.16llx-%.10llx\":%llu",
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s\"%.16llx-%.10llx\":%llu",
(first) ? "" : ",",
(unsigned long long)networkId,
(unsigned long long)nodeId,
@@ -1093,7 +1093,7 @@ void EmbeddedNetworkController::threadMain()
});
}
char tmp2[256];
- Utils::snprintf(tmp2,sizeof(tmp2),"},\"clock\":%llu,\"startTime\":%llu}",(unsigned long long)now,(unsigned long long)_startTime);
+ Utils::ztsnprintf(tmp2,sizeof(tmp2),"},\"clock\":%llu,\"startTime\":%llu}",(unsigned long long)now,(unsigned long long)_startTime);
pong.append(tmp2);
_db.writeRaw("pong",pong);
}
@@ -1126,7 +1126,7 @@ void EmbeddedNetworkController::_request(
ms.lastRequestTime = now;
}
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
+ Utils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
if (!_db.getNetworkAndMember(nwid,identity.address().toInt(),network,member,ns)) {
_sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_OBJECT_NOT_FOUND);
return;
diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp
index e0dd1742..acf23700 100644
--- a/controller/JSONDB.cpp
+++ b/controller/JSONDB.cpp
@@ -94,7 +94,7 @@ bool JSONDB::writeRaw(const std::string &n,const std::string &obj)
std::string body;
std::map<std::string,std::string> reqHeaders;
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%lu",(unsigned long)obj.length());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%lu",(unsigned long)obj.length());
reqHeaders["Content-Length"] = tmp;
reqHeaders["Content-Type"] = "application/json";
const unsigned int sc = Http::PUT(0,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast<const struct sockaddr *>(&_httpAddr),(_basePath+"/"+n).c_str(),reqHeaders,obj.data(),(unsigned long)obj.length(),headers,body);
@@ -164,7 +164,7 @@ bool JSONDB::getNetworkMember(const uint64_t networkId,const uint64_t nodeId,nlo
void JSONDB::saveNetwork(const uint64_t networkId,const nlohmann::json &networkConfig)
{
char n[64];
- Utils::snprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
+ Utils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
writeRaw(n,OSUtils::jsonDump(networkConfig));
{
Mutex::Lock _l(_networks_m);
@@ -176,7 +176,7 @@ void JSONDB::saveNetwork(const uint64_t networkId,const nlohmann::json &networkC
void JSONDB::saveNetworkMember(const uint64_t networkId,const uint64_t nodeId,const nlohmann::json &memberConfig)
{
char n[256];
- Utils::snprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
+ Utils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
writeRaw(n,OSUtils::jsonDump(memberConfig));
{
Mutex::Lock _l(_networks_m);
@@ -202,7 +202,7 @@ nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId)
}
char n[256];
- Utils::snprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
+ Utils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId);
if (_httpAddr) {
// Deletion is currently done by Central in harnessed mode
@@ -229,7 +229,7 @@ nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId)
nlohmann::json JSONDB::eraseNetworkMember(const uint64_t networkId,const uint64_t nodeId,bool recomputeSummaryInfo)
{
char n[256];
- Utils::snprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
+ Utils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId);
if (_httpAddr) {
// Deletion is currently done by the caller in Central harnessed mode
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-linux.mk b/make-linux.mk
index 8073aa72..c27c600d 100644
--- a/make-linux.mk
+++ b/make-linux.mk
@@ -69,9 +69,9 @@ ifeq ($(ZT_DEBUG),1)
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CXXFLAGS=-Wall -O2 -g -pthread $(INCLUDES) $(DEFS)
else
override DEFS+=-D_FORTIFY_SOURCE=2
- CFLAGS?=-O3 -fstack-protector
+ CFLAGS?=-Os -fstack-protector
override CFLAGS+=-Wall -Wno-deprecated -fPIE -pthread $(INCLUDES) -DNDEBUG $(DEFS)
- CXXFLAGS?=-O3 -fstack-protector
+ CXXFLAGS?=-Os -fstack-protector
override CXXFLAGS+=-Wall -Wno-deprecated -Wno-unused-result -Wreorder -fPIE -std=c++11 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
override LDFLAGS+=-pie -Wl,-z,relro,-z,now
STRIP?=strip
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/Address.hpp b/node/Address.hpp
index 9d2d1734..98e32858 100644
--- a/node/Address.hpp
+++ b/node/Address.hpp
@@ -144,7 +144,7 @@ public:
inline std::string toString() const
{
char buf[16];
- Utils::snprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
+ Utils::ztsnprintf(buf,sizeof(buf),"%.10llx",(unsigned long long)_a);
return std::string(buf);
};
@@ -154,7 +154,7 @@ public:
*/
inline void toString(char *buf,unsigned int len) const
{
- Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
+ Utils::ztsnprintf(buf,len,"%.10llx",(unsigned long long)_a);
}
/**
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/Dictionary.hpp b/node/Dictionary.hpp
index 4413d628..0b000f13 100644
--- a/node/Dictionary.hpp
+++ b/node/Dictionary.hpp
@@ -391,7 +391,7 @@ public:
inline bool add(const char *key,uint64_t value)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
return this->add(key,tmp,-1);
}
@@ -401,7 +401,7 @@ public:
inline bool add(const char *key,const Address &a)
{
char tmp[32];
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
return this->add(key,tmp,-1);
}
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/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 9140c502..1d55c9f3 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -42,7 +42,6 @@
#include "Salsa20.hpp"
#include "SHA512.hpp"
#include "World.hpp"
-#include "Cluster.hpp"
#include "Node.hpp"
#include "CertificateOfMembership.hpp"
#include "CertificateOfRepresentation.hpp"
diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp
index 0fbb2d68..17d7c72e 100644
--- a/node/InetAddress.cpp
+++ b/node/InetAddress.cpp
@@ -152,7 +152,7 @@ std::string InetAddress::toString() const
char buf[128];
switch(ss_family) {
case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d/%d",
+ Utils::ztsnprintf(buf,sizeof(buf),"%d.%d.%d.%d/%d",
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
@@ -161,7 +161,7 @@ std::string InetAddress::toString() const
);
return std::string(buf);
case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d",
+ Utils::ztsnprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d",
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
@@ -190,7 +190,7 @@ std::string InetAddress::toIpString() const
char buf[128];
switch(ss_family) {
case AF_INET:
- Utils::snprintf(buf,sizeof(buf),"%d.%d.%d.%d",
+ Utils::ztsnprintf(buf,sizeof(buf),"%d.%d.%d.%d",
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[0],
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[1],
(int)(reinterpret_cast<const unsigned char *>(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr)))[2],
@@ -198,7 +198,7 @@ std::string InetAddress::toIpString() const
);
return std::string(buf);
case AF_INET6:
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
+ Utils::ztsnprintf(buf,sizeof(buf),"%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x",
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[0]),
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[1]),
(int)(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr[2]),
diff --git a/node/MAC.hpp b/node/MAC.hpp
index e7717d99..db50aeb1 100644
--- a/node/MAC.hpp
+++ b/node/MAC.hpp
@@ -178,7 +178,7 @@ public:
*/
inline void toString(char *buf,unsigned int len) const
{
- Utils::snprintf(buf,len,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)(*this)[0],(int)(*this)[1],(int)(*this)[2],(int)(*this)[3],(int)(*this)[4],(int)(*this)[5]);
+ Utils::ztsnprintf(buf,len,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)(*this)[0],(int)(*this)[1],(int)(*this)[2],(int)(*this)[3],(int)(*this)[4],(int)(*this)[5]);
}
/**
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
index 4240db67..7cbec2e0 100644
--- a/node/MulticastGroup.hpp
+++ b/node/MulticastGroup.hpp
@@ -100,7 +100,7 @@ public:
inline std::string toString() const
{
char buf[64];
- Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.8lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi);
+ Utils::ztsnprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.8lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi);
return std::string(buf);
}
diff --git a/node/Network.cpp b/node/Network.cpp
index de2ea7d7..8c6f2ce8 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -42,7 +42,6 @@
#include "NetworkController.hpp"
#include "Node.hpp"
#include "Peer.hpp"
-#include "Cluster.hpp"
// Uncomment to make the rules engine dump trace info to stdout
//#define ZT_RULES_ENGINE_DEBUGGING 1
@@ -52,7 +51,7 @@ namespace ZeroTier {
namespace {
#ifdef ZT_RULES_ENGINE_DEBUGGING
-#define FILTER_TRACE(f,...) { Utils::snprintf(dpbuf,sizeof(dpbuf),f,##__VA_ARGS__); dlog.push_back(std::string(dpbuf)); }
+#define FILTER_TRACE(f,...) { Utils::ztsnprintf(dpbuf,sizeof(dpbuf),f,##__VA_ARGS__); dlog.push_back(std::string(dpbuf)); }
static const char *_rtn(const ZT_VirtualNetworkRuleType rt)
{
switch(rt) {
@@ -682,7 +681,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 +696,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 +735,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 +1185,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;
}
@@ -1266,7 +1261,7 @@ void Network::requestConfiguration(void *tPtr)
nconf->rules[13].t = (uint8_t)ZT_NETWORK_RULE_ACTION_DROP;
nconf->type = ZT_NETWORK_TYPE_PUBLIC;
- Utils::snprintf(nconf->name,sizeof(nconf->name),"adhoc-%.04x-%.04x",(int)startPortRange,(int)endPortRange);
+ Utils::ztsnprintf(nconf->name,sizeof(nconf->name),"adhoc-%.04x-%.04x",(int)startPortRange,(int)endPortRange);
this->setConfiguration(tPtr,*nconf,false);
delete nconf;
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/NetworkConfig.cpp b/node/NetworkConfig.cpp
index c39f6cab..65101c3a 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -94,7 +94,7 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
if (ets.length() > 0)
ets.push_back(',');
char tmp2[16];
- Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
+ Utils::ztsnprintf(tmp2,sizeof(tmp2),"%x",et);
ets.append(tmp2);
}
et = 0;
diff --git a/node/Node.cpp b/node/Node.cpp
index 911c9c4b..ab49e63b 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"
@@ -44,7 +45,7 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
+#include "Network.hpp"
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
@@ -58,6 +59,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 +76,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 +123,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 +135,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 +342,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 +413,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 +539,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 +559,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 +614,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 +682,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 +698,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;
}
}
@@ -640,7 +742,7 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
va_end(ap);
tmp2[sizeof(tmp2)-1] = (char)0;
- Utils::snprintf(tmp1,sizeof(tmp1),"[%s] %s:%u %s",nowstr,module,line,tmp2);
+ Utils::ztsnprintf(tmp1,sizeof(tmp1),"[%s] %s:%u %s",nowstr,module,line,tmp2);
postEvent((void *)0,ZT_EVENT_TRACE,tmp1);
}
#endif // ZT_TRACE
@@ -806,6 +908,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 +1117,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/Peer.cpp b/node/Peer.cpp
index 01905833..84086048 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -32,7 +32,6 @@
#include "Switch.hpp"
#include "Network.hpp"
#include "SelfAwareness.hpp"
-#include "Cluster.hpp"
#include "Packet.hpp"
namespace ZeroTier {
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 211b706a..2be54b37 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -43,7 +43,6 @@
#include "Peer.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
-#include "Cluster.hpp"
namespace ZeroTier {
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/node/Utils.cpp b/node/Utils.cpp
index d69e5335..d2321e16 100644
--- a/node/Utils.cpp
+++ b/node/Utils.cpp
@@ -244,8 +244,7 @@ bool Utils::scopy(char *dest,unsigned int len,const char *src)
return true;
}
-unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error)
+unsigned int Utils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
{
va_list ap;
@@ -256,7 +255,7 @@ unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
if ((n >= (int)len)||(n < 0)) {
if (len)
buf[len - 1] = (char)0;
- throw std::length_error("buf[] overflow in Utils::snprintf");
+ throw std::length_error("buf[] overflow");
}
return (unsigned int)n;
diff --git a/node/Utils.hpp b/node/Utils.hpp
index 25a90055..212ef247 100644
--- a/node/Utils.hpp
+++ b/node/Utils.hpp
@@ -244,8 +244,7 @@ public:
* @param ... Format arguments
* @throws std::length_error buf[] too short (buf[] will still be left null-terminated)
*/
- static unsigned int snprintf(char *buf,unsigned int len,const char *fmt,...)
- throw(std::length_error);
+ static unsigned int ztsnprintf(char *buf,unsigned int len,const char *fmt,...);
/**
* Count the number of bits set in an integer
diff --git a/objects.mk b/objects.mk
index fd25a3d3..c8231f08 100644
--- a/objects.mk
+++ b/objects.mk
@@ -3,7 +3,6 @@ CORE_OBJS=\
node/Capability.o \
node/CertificateOfMembership.o \
node/CertificateOfOwnership.o \
- node/Cluster.o \
node/Identity.o \
node/IncomingPacket.o \
node/InetAddress.o \
diff --git a/one.cpp b/one.cpp
index 93504cfb..cbf09121 100644
--- a/one.cpp
+++ b/one.cpp
@@ -260,9 +260,9 @@ static int cli(int argc,char **argv)
if (hd) {
char p[4096];
#ifdef __APPLE__
- Utils::snprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
+ Utils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
#else
- Utils::snprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
+ Utils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
#endif
OSUtils::readFile(p,authToken);
}
@@ -278,7 +278,7 @@ static int cli(int argc,char **argv)
InetAddress addr;
{
char addrtmp[256];
- Utils::snprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
+ Utils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
addr = InetAddress(addrtmp);
}
@@ -366,7 +366,7 @@ static int cli(int argc,char **argv)
std::string addr = path["address"];
const uint64_t now = OSUtils::now();
const double lq = (path.count("linkQuality")) ? (double)path["linkQuality"] : -1.0;
- Utils::snprintf(tmp,sizeof(tmp),"%s;%llu;%llu;%1.2f",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"],lq);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s;%llu;%llu;%1.2f",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"],lq);
bestPath = tmp;
break;
}
@@ -378,7 +378,7 @@ static int cli(int argc,char **argv)
int64_t vmin = p["versionMinor"];
int64_t vrev = p["versionRev"];
if (vmaj >= 0) {
- Utils::snprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
+ Utils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
} else {
ver[0] = '-';
ver[1] = (char)0;
@@ -527,9 +527,9 @@ static int cli(int argc,char **argv)
const uint64_t seed = Utils::hexStrToU64(arg2.c_str());
if ((worldId)&&(seed)) {
char jsons[1024];
- Utils::snprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
+ Utils::ztsnprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str());
char cl[128];
- Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ Utils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
requestHeaders["Content-Type"] = "application/json";
requestHeaders["Content-Length"] = cl;
unsigned int scode = Http::POST(
@@ -579,11 +579,11 @@ static int cli(int argc,char **argv)
if (eqidx != std::string::npos) {
if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) {
char jsons[1024];
- Utils::snprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
+ Utils::ztsnprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
arg2.substr(0,eqidx).c_str(),
(((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false"));
char cl[128];
- Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
+ Utils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
requestHeaders["Content-Type"] = "application/json";
requestHeaders["Content-Length"] = cl;
unsigned int scode = Http::POST(
@@ -864,7 +864,7 @@ static int idtool(int argc,char **argv)
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf;
w.serialize(wbuf);
char fn[128];
- Utils::snprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
+ Utils::ztsnprintf(fn,sizeof(fn),"%.16llx.moon",w.id());
OSUtils::writeFile(fn,wbuf.data(),wbuf.size());
printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now);
}
diff --git a/osdep/BSDEthernetTap.cpp b/osdep/BSDEthernetTap.cpp
index 5bb5fbd1..f07f9e5a 100644
--- a/osdep/BSDEthernetTap.cpp
+++ b/osdep/BSDEthernetTap.cpp
@@ -114,8 +114,8 @@ BSDEthernetTap::BSDEthernetTap(
std::vector<std::string> devFiles(OSUtils::listDirectory("/dev"));
for(int i=9993;i<(9993+128);++i) {
- Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ Utils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ Utils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
if (std::find(devFiles.begin(),devFiles.end(),std::string(tmpdevname)) == devFiles.end()) {
long cpid = (long)vfork();
if (cpid == 0) {
@@ -152,8 +152,8 @@ BSDEthernetTap::BSDEthernetTap(
/* Other BSDs like OpenBSD only have a limited number of tap devices that cannot be renamed */
for(int i=0;i<64;++i) {
- Utils::snprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
- Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
+ Utils::ztsnprintf(tmpdevname,sizeof(tmpdevname),"tap%d",i);
+ Utils::ztsnprintf(devpath,sizeof(devpath),"/dev/%s",tmpdevname);
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
_dev = tmpdevname;
@@ -171,9 +171,9 @@ BSDEthernetTap::BSDEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ Utils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ Utils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ Utils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -385,7 +385,7 @@ void BSDEthernetTap::setMtu(unsigned int mtu)
long cpid = (long)vfork();
if (cpid == 0) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%u",mtu);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
_exit(-1);
} else if (cpid > 0) {
diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp
index ee832825..a0b47367 100644
--- a/osdep/Binder.hpp
+++ b/osdep/Binder.hpp
@@ -57,6 +57,7 @@
#include <algorithm>
#include <utility>
#include <map>
+#include <set>
#include "../node/NonCopyable.hpp"
#include "../node/InetAddress.hpp"
@@ -66,11 +67,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 +102,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 +110,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 +123,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 +131,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 +154,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 +226,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 +246,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 +256,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 +267,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 +301,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 +319,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,50 +397,95 @@ 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;
}
+ /**
+ * @param addr Address to check
+ * @return True if this is a bound local interface address
+ */
+ inline bool isBoundLocalInterfaceAddress(const InetAddress &addr) const
+ {
+ Mutex::Lock _l(_lock);
+ for(std::vector<_Binding>::const_iterator b(_bindings.begin());b!=_bindings.end();++b) {
+ if (b->address == addr)
+ return true;
+ }
+ return false;
+ }
+
private:
std::vector<_Binding> _bindings;
Mutex _lock;
diff --git a/osdep/Http.cpp b/osdep/Http.cpp
index f1d3bfe2..3c556f44 100644
--- a/osdep/Http.cpp
+++ b/osdep/Http.cpp
@@ -244,10 +244,10 @@ unsigned int Http::_do(
try {
char tmp[1024];
- Utils::snprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path);
handler.writeBuf.append(tmp);
for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) {
- Utils::snprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str());
handler.writeBuf.append(tmp);
}
handler.writeBuf.append("\r\n");
diff --git a/osdep/LinuxEthernetTap.cpp b/osdep/LinuxEthernetTap.cpp
index ccaa92ef..fc5199f1 100644
--- a/osdep/LinuxEthernetTap.cpp
+++ b/osdep/LinuxEthernetTap.cpp
@@ -97,7 +97,7 @@ LinuxEthernetTap::LinuxEthernetTap(
char procpath[128],nwids[32];
struct stat sbuf;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
+ Utils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
@@ -134,7 +134,7 @@ LinuxEthernetTap::LinuxEthernetTap(
std::map<std::string,std::string>::const_iterator gdmEntry = globalDeviceMap.find(nwids);
if (gdmEntry != globalDeviceMap.end()) {
Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),gdmEntry->second.c_str());
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ Utils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
recalledDevice = (stat(procpath,&sbuf) != 0);
}
@@ -142,8 +142,8 @@ LinuxEthernetTap::LinuxEthernetTap(
#ifdef __SYNOLOGY__
int devno = 50;
do {
- Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ Utils::ztsnprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"eth%d",devno++);
+ Utils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
} while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist
#else
char devno = 0;
@@ -158,7 +158,7 @@ LinuxEthernetTap::LinuxEthernetTap(
_base32_5_to_8(reinterpret_cast<const uint8_t *>(tmp2) + 5,tmp3 + 10);
tmp3[15] = (char)0;
memcpy(ifr.ifr_name,tmp3,16);
- Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ Utils::ztsnprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
} while (stat(procpath,&sbuf) == 0);
#endif
}
diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp
index 46c1a8ae..06508e77 100644
--- a/osdep/OSUtils.cpp
+++ b/osdep/OSUtils.cpp
@@ -134,7 +134,7 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan)
if (date.QuadPart > 0) {
date.QuadPart -= adjust.QuadPart;
if ((uint64_t)((date.QuadPart / 10000000) * 1000) < olderThan) {
- Utils::snprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName);
+ Utils::ztsnprintf(tmp, sizeof(tmp), "%s\\%s", path, ffd.cFileName);
if (DeleteFileA(tmp))
++cleaned;
}
@@ -157,7 +157,7 @@ long OSUtils::cleanDirectory(const char *path,const uint64_t olderThan)
break;
if (dptr) {
if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,".."))&&(dptr->d_type == DT_REG)) {
- Utils::snprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%s/%s",path,dptr->d_name);
if (stat(tmp,&st) == 0) {
uint64_t mt = (uint64_t)(st.st_mtime);
if ((mt > 0)&&((mt * 1000) < olderThan)) {
@@ -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(;;) {
@@ -464,7 +464,7 @@ std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl)
return jv;
} else if (jv.is_number()) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
return tmp;
} else if (jv.is_boolean()) {
return ((bool)jv ? std::string("1") : std::string("0"));
diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp
index f5e1c43f..e082408e 100644
--- a/osdep/OSXEthernetTap.cpp
+++ b/osdep/OSXEthernetTap.cpp
@@ -336,7 +336,7 @@ OSXEthernetTap::OSXEthernetTap(
char devpath[64],ethaddr[64],mtustr[32],metstr[32],nwids[32];
struct stat stattmp;
- Utils::snprintf(nwids,sizeof(nwids),"%.16llx",nwid);
+ Utils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
Mutex::Lock _gl(globalTapCreateLock);
@@ -391,13 +391,13 @@ OSXEthernetTap::OSXEthernetTap(
// Open the first unused tap device if we didn't recall a previous one.
if (!recalledDevice) {
for(int i=0;i<64;++i) {
- Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
+ Utils::ztsnprintf(devpath,sizeof(devpath),"/dev/zt%d",i);
if (stat(devpath,&stattmp))
throw std::runtime_error("no more TAP devices available");
_fd = ::open(devpath,O_RDWR);
if (_fd > 0) {
char foo[16];
- Utils::snprintf(foo,sizeof(foo),"zt%d",i);
+ Utils::ztsnprintf(foo,sizeof(foo),"zt%d",i);
_dev = foo;
break;
}
@@ -413,9 +413,9 @@ OSXEthernetTap::OSXEthernetTap(
}
// Configure MAC address and MTU, bring interface up
- Utils::snprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
- Utils::snprintf(mtustr,sizeof(mtustr),"%u",_mtu);
- Utils::snprintf(metstr,sizeof(metstr),"%u",_metric);
+ Utils::ztsnprintf(ethaddr,sizeof(ethaddr),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ Utils::ztsnprintf(mtustr,sizeof(mtustr),"%u",_mtu);
+ Utils::ztsnprintf(metstr,sizeof(metstr),"%u",_metric);
long cpid = (long)vfork();
if (cpid == 0) {
::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"lladdr",ethaddr,"mtu",mtustr,"metric",metstr,"up",(const char *)0);
@@ -636,7 +636,7 @@ void OSXEthernetTap::setMtu(unsigned int mtu)
long cpid = (long)vfork();
if (cpid == 0) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%u",mtu);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%u",mtu);
execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"mtu",tmp,(const char *)0);
_exit(-1);
} else if (cpid > 0) {
diff --git a/osdep/PortMapper.cpp b/osdep/PortMapper.cpp
index 99286172..df868e7a 100644
--- a/osdep/PortMapper.cpp
+++ b/osdep/PortMapper.cpp
@@ -205,7 +205,7 @@ public:
memset(externalip,0,sizeof(externalip));
memset(&urls,0,sizeof(urls));
memset(&data,0,sizeof(data));
- Utils::snprintf(inport,sizeof(inport),"%d",localPort);
+ Utils::ztsnprintf(inport,sizeof(inport),"%d",localPort);
if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
#ifdef ZT_PORTMAPPER_TRACE
@@ -220,7 +220,7 @@ public:
int tryPort = (int)localPort + tries;
if (tryPort >= 65535)
tryPort = (tryPort - 65535) + 1025;
- Utils::snprintf(outport,sizeof(outport),"%u",tryPort);
+ Utils::ztsnprintf(outport,sizeof(outport),"%u",tryPort);
// First check and see if this port is already mapped to the
// same unique name. If so, keep this mapping and don't try
diff --git a/osdep/WindowsEthernetTap.cpp b/osdep/WindowsEthernetTap.cpp
index c5d82d8e..b96ad791 100644
--- a/osdep/WindowsEthernetTap.cpp
+++ b/osdep/WindowsEthernetTap.cpp
@@ -484,7 +484,7 @@ WindowsEthernetTap::WindowsEthernetTap(
char tag[24];
// We "tag" registry entries with the network ID to identify persistent devices
- Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
+ Utils::ztsnprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)nwid);
Mutex::Lock _l(_systemTapInitLock);
@@ -601,10 +601,10 @@ WindowsEthernetTap::WindowsEthernetTap(
if (_netCfgInstanceId.length() > 0) {
char tmps[64];
- unsigned int tmpsl = Utils::snprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
+ unsigned int tmpsl = Utils::ztsnprintf(tmps,sizeof(tmps),"%.2X-%.2X-%.2X-%.2X-%.2X-%.2X",(unsigned int)mac[0],(unsigned int)mac[1],(unsigned int)mac[2],(unsigned int)mac[3],(unsigned int)mac[4],(unsigned int)mac[5]) + 1;
RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"NetworkAddress",REG_SZ,tmps,tmpsl);
RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MAC",REG_SZ,tmps,tmpsl);
- tmpsl = Utils::snprintf(tmps, sizeof(tmps), "%d", mtu);
+ tmpsl = Utils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
RegSetKeyValueA(nwAdapters,_mySubkeyName.c_str(),"MTU",REG_SZ,tmps,tmpsl);
DWORD tmp = 0;
@@ -879,7 +879,7 @@ void WindowsEthernetTap::setMtu(unsigned int mtu)
HKEY nwAdapters;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ | KEY_WRITE, &nwAdapters) == ERROR_SUCCESS) {
char tmps[64];
- unsigned int tmpsl = Utils::snprintf(tmps, sizeof(tmps), "%d", mtu);
+ unsigned int tmpsl = Utils::ztsnprintf(tmps, sizeof(tmps), "%d", mtu);
RegSetKeyValueA(nwAdapters, _mySubkeyName.c_str(), "MTU", REG_SZ, tmps, tmpsl);
RegCloseKey(nwAdapters);
}
@@ -902,7 +902,7 @@ void WindowsEthernetTap::threadMain()
HANDLE wait4[3];
OVERLAPPED tapOvlRead,tapOvlWrite;
- Utils::snprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
+ Utils::ztsnprintf(tapPath,sizeof(tapPath),"\\\\.\\Global\\%s.tap",_netCfgInstanceId.c_str());
try {
while (_run) {
diff --git a/selftest.cpp b/selftest.cpp
index 8175d708..ff171aa3 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -831,7 +831,7 @@ static int testOther()
memset(key, 0, sizeof(key));
memset(value, 0, sizeof(value));
for(unsigned int q=0;q<32;++q) {
- Utils::snprintf(key[q],16,"%.8lx",(unsigned long)(rand() % 1000) + (q * 1000));
+ Utils::ztsnprintf(key[q],16,"%.8lx",(unsigned long)(rand() % 1000) + (q * 1000));
int r = rand() % 128;
for(int x=0;x<r;++x)
value[q][x] = ("0123456789\0\t\r\n= ")[rand() % 16];
diff --git a/service/ClusterDefinition.hpp b/service/ClusterDefinition.hpp
index 9947e46b..b6317ff7 100644
--- a/service/ClusterDefinition.hpp
+++ b/service/ClusterDefinition.hpp
@@ -72,7 +72,7 @@ public:
return;
char myAddressStr[64];
- Utils::snprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
+ Utils::ztsnprintf(myAddressStr,sizeof(myAddressStr),"%.10llx",myAddress);
std::vector<std::string> lines(OSUtils::split(cf.c_str(),"\r\n","",""));
for(std::vector<std::string>::iterator l(lines.begin());l!=lines.end();++l) {
diff --git a/service/OneService.cpp b/service/OneService.cpp
index b96f3aed..993fb116 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();
@@ -207,10 +210,10 @@ static void _networkToJson(nlohmann::json &nj,const ZT_VirtualNetworkConfig *nc,
case ZT_NETWORK_TYPE_PUBLIC: ntype = "PUBLIC"; break;
}
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",nc->nwid);
nj["id"] = tmp;
nj["nwid"] = tmp;
- Utils::snprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(unsigned int)((nc->mac >> 40) & 0xff),(unsigned int)((nc->mac >> 32) & 0xff),(unsigned int)((nc->mac >> 24) & 0xff),(unsigned int)((nc->mac >> 16) & 0xff),(unsigned int)((nc->mac >> 8) & 0xff),(unsigned int)(nc->mac & 0xff));
nj["mac"] = tmp;
nj["name"] = nc->name;
nj["status"] = nstatus;
@@ -257,12 +260,12 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
case ZT_PEER_ROLE_PLANET: prole = "PLANET"; break;
}
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
pj["address"] = tmp;
pj["versionMajor"] = peer->versionMajor;
pj["versionMinor"] = peer->versionMinor;
pj["versionRev"] = peer->versionRev;
- Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",peer->versionMajor,peer->versionMinor,peer->versionRev);
pj["version"] = tmp;
pj["latency"] = peer->latency;
pj["role"] = prole;
@@ -286,7 +289,7 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
static void _moonToJson(nlohmann::json &mj,const World &world)
{
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",world.id());
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",world.id());
mj["id"] = tmp;
mj["timestamp"] = world.timestamp();
mj["signature"] = Utils::hex(world.signature().data,(unsigned int)world.signature().size());
@@ -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,15 +685,15 @@ 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]);
+ Utils::ztsnprintf(portstr,sizeof(portstr),"%u",_ports[0]);
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S "zerotier-one.port").c_str(),std::string(portstr));
// 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) {
@@ -754,70 +725,19 @@ public:
}
if (_ports[2]) {
char uniqueName[64];
- Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
+ Utils::ztsnprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]);
_portMapper = new PortMapper(_ports[2],uniqueName);
}
}
}
#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());
- if (_clusterDefinition->size() > 0) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- PhySocket *cs = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(m->clusterEndpoint)));
- if (cs) {
- if (_clusterMessageSocket) {
- _phy.close(_clusterMessageSocket,false);
- _phy.close(cs,false);
-
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "cluster: can't determine my cluster member ID: able to bind more than one cluster message socket IP/port!";
- return _termReason;
- }
- _clusterMessageSocket = cs;
- _clusterMemberId = m->id;
- }
- }
-
- if (!_clusterMessageSocket) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "cluster: can't determine my cluster member ID: unable to bind to any cluster message socket IP/port.";
- return _termReason;
- }
-
- const ClusterDefinition::MemberDefinition &me = (*_clusterDefinition)[_clusterMemberId];
- InetAddress endpoints[255];
- unsigned int numEndpoints = 0;
- for(std::vector<InetAddress>::const_iterator i(me.zeroTierEndpoints.begin());i!=me.zeroTierEndpoints.end();++i)
- endpoints[numEndpoints++] = *i;
-
- if (_node->clusterInit(_clusterMemberId,reinterpret_cast<const struct sockaddr_storage *>(endpoints),numEndpoints,me.x,me.y,me.z,&SclusterSendFunction,this,_clusterDefinition->geo().available() ? &SclusterGeoIpFunction : 0,this) == ZT_RESULT_OK) {
- std::vector<ClusterDefinition::MemberDefinition> members(_clusterDefinition->members());
- for(std::vector<ClusterDefinition::MemberDefinition>::iterator m(members.begin());m!=members.end();++m) {
- if (m->id != _clusterMemberId)
- _node->clusterAddMember(m->id);
- }
- }
- } else {
- delete _clusterDefinition;
- _clusterDefinition = (ClusterDefinition *)0;
- }
- }
-#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 +745,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 +756,24 @@ public:
}
}
+ // Derive the cluster's shared secret backplane encryption key by hashing its shared secret identity
+ {
+ uint8_t tmp[64];
+ uint8_t sk[ZT_C25519_PRIVATE_KEY_LEN + 4];
+ memcpy(sk,_node->identity().privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+ sk[ZT_C25519_PRIVATE_KEY_LEN] = 0xab;
+ sk[ZT_C25519_PRIVATE_KEY_LEN + 1] = 0xcd;
+ sk[ZT_C25519_PRIVATE_KEY_LEN + 2] = 0xef;
+ sk[ZT_C25519_PRIVATE_KEY_LEN + 3] = 0xab; // add an arbitrary nonce, just because
+ SHA512::hash(tmp,sk,ZT_C25519_PRIVATE_KEY_LEN + 4);
+ memcpy(_clusterKey,tmp,32);
+ }
+
+ // Assign a random non-zero cluster member ID to identify vs. other cluster members
+ Utils::getSecureRandom(&_clusterMemberId,sizeof(_clusterMemberId));
+ if (!_clusterMemberId) _clusterMemberId = 1;
+
+ // Main I/O loop
_nextBackgroundTaskDeadline = 0;
uint64_t clockShouldBe = OSUtils::now();
_lastRestart = clockShouldBe;
@@ -842,6 +782,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 +800,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 +820,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 +836,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 +863,7 @@ public:
}
}
+ // Sync information about physical network interfaces
if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
@@ -930,19 +877,67 @@ 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;
+
+ // Send status to active cluster links and close overflowed and dead ones
+ 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);
+
+ // Attempt to connect to cluster links we don't have an active connection to
+ {
+ 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()) && (!_binder.isBoundLocalInterfaceAddress(*ca)) ) {
+ 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 +945,7 @@ public:
}
try {
+ Mutex::Lock _l(_tcpConnections_m);
while (!_tcpConnections.empty())
_phy.close((*_tcpConnections.begin())->sock);
} catch ( ... ) {}
@@ -991,35 +987,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 +1023,9 @@ public:
}
}
}
- }
- return NULL;
- }
+ }
+ return NULL;
+ }
virtual Node * getNode()
{
@@ -1040,9 +1036,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 +1068,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::ztsnprintf(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 +1084,9 @@ public:
return true;
}
- // Internal implementation methods -----------------------------------------
+ // =========================================================================
+ // Internal implementation methods for control plane, route setup, etc.
+ // =========================================================================
inline unsigned int handleControlPlaneHttpRequest(
const InetAddress &fromAddress,
@@ -1191,7 +1188,7 @@ public:
ZT_NodeStatus status;
_node->status(&status);
- Utils::snprintf(tmp,sizeof(tmp),"%.10llx",status.address);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",status.address);
res["address"] = tmp;
res["publicIdentity"] = status.publicIdentity;
res["online"] = (bool)(status.online != 0);
@@ -1200,7 +1197,7 @@ public:
res["versionMinor"] = ZEROTIER_ONE_VERSION_MINOR;
res["versionRev"] = ZEROTIER_ONE_VERSION_REVISION;
res["versionBuild"] = ZEROTIER_ONE_VERSION_BUILD;
- Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%d.%d.%d",ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
res["version"] = tmp;
res["clock"] = OSUtils::now();
@@ -1216,7 +1213,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 +1220,7 @@ public:
res["planetWorldId"] = planet.id();
res["planetWorldTimestamp"] = planet.timestamp();
+/*
#ifdef ZT_ENABLE_CLUSTER
json cj;
ZT_ClusterStatus cs;
@@ -1250,6 +1247,7 @@ public:
#else
res["cluster"] = json();
#endif
+*/
scode = 200;
} else if (ps[0] == "moon") {
@@ -1375,7 +1373,7 @@ public:
if ((scode != 200)&&(seed != 0)) {
char tmp[64];
- Utils::snprintf(tmp,sizeof(tmp),"%.16llx",id);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"%.16llx",id);
res["id"] = tmp;
res["roots"] = json::array();
res["timestamp"] = 0;
@@ -1601,6 +1599,16 @@ public:
}
}
+ json &cl = settings["cluster"];
+ _clusterBackplaneAddresses.clear();
+ if (cl.is_array()) {
+ for(unsigned long i=0;i<cl.size();++i) {
+ const InetAddress cip(OSUtils::jsonString(cl[i],""));
+ if ((cip.ss_family == AF_INET)||(cip.ss_family == AF_INET6))
+ _clusterBackplaneAddresses.push_back(cip);
+ }
+ }
+
json &controllerDbHttpHost = settings["controllerDbHttpHost"];
json &controllerDbHttpPort = settings["controllerDbHttpPort"];
json &controllerDbHttpPath = settings["controllerDbHttpPath"];
@@ -1609,7 +1617,7 @@ public:
std::string h = controllerDbHttpHost;
_controllerDbPath.append(h);
char dbp[128];
- Utils::snprintf(dbp,sizeof(dbp),"%d",(int)controllerDbHttpPort);
+ Utils::ztsnprintf(dbp,sizeof(dbp),"%d",(int)controllerDbHttpPort);
_controllerDbPath.push_back(':');
_controllerDbPath.append(dbp);
if (controllerDbHttpPath.is_string()) {
@@ -1703,7 +1711,7 @@ public:
if (syncRoutes) {
char tapdev[64];
#ifdef __WINDOWS__
- Utils::snprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
+ Utils::ztsnprintf(tapdev,sizeof(tapdev),"%.16llx",(unsigned long long)n.tap->luid().Value);
#else
Utils::scopy(tapdev,sizeof(tapdev),n.tap->deviceName().c_str());
#endif
@@ -1770,24 +1778,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::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PEER_IDENTITY:
+ Utils::ztsnprintf(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::ztsnprintf(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::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ Utils::ztsnprintf(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();
@@ -1801,7 +2022,7 @@ public:
&_nextBackgroundTaskDeadline);
if (ZT_ResultCode_isFatal(rc)) {
char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ Utils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
_fatalErrorMessage = tmp;
@@ -1811,38 +2032,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 +2068,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::ztsnprintf(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 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;
- case 0: // none/omitted
- ++data;
- --plen;
+
+ 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;
- 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;
- }
- }
+ tc->readq.erase(tc->readq.begin(),tc->readq.begin() + mlen);
}
+ }
+ return;
- if (tc->body.length() > (mlen + 5))
- tc->body = tc->body.substr(mlen + 5);
- else tc->body = "";
- } else break;
- }
- break;
-
+ }
+ } 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) {}
@@ -2014,7 +2402,7 @@ public:
if (!n.tap) {
try {
char friendlyName[128];
- Utils::snprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
+ Utils::ztsnprintf(friendlyName,sizeof(friendlyName),"ZeroTier One [%.16llx]",nwid);
n.tap = new EthernetTap(
_homePath.c_str(),
@@ -2028,7 +2416,7 @@ public:
*nuptr = (void *)&n;
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);
+ Utils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
std::string nlcbuf;
if (OSUtils::readFile(nlcpath,nlcbuf)) {
Dictionary<4096> nc;
@@ -2114,7 +2502,7 @@ public:
#endif
if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
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);
+ Utils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
OSUtils::rm(nlcpath);
}
} else {
@@ -2155,80 +2543,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::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_PEER_IDENTITY:
+ Utils::ztsnprintf(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::ztsnprintf(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::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ Utils::ztsnprintf(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) {
+ FILE *f = fopen(p,"r");
+ if (f) {
+ int n = (int)fread(data,1,maxlen,f);
fclose(f);
- if (secure)
- OSUtils::lockDownFile(p.c_str(),false);
- return 0;
- } else {
- 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 +2596,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;
- return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
+ case 0: // failure
+ return -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 +2734,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 +2765,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::ztsnprintf(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 +2782,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 +2826,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 +2866,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 +2878,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 +2887,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 +2907,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 +2945,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)
*/
diff --git a/service/SoftwareUpdater.cpp b/service/SoftwareUpdater.cpp
index d94beab5..e0519827 100644
--- a/service/SoftwareUpdater.cpp
+++ b/service/SoftwareUpdater.cpp
@@ -284,7 +284,7 @@ bool SoftwareUpdater::check(const uint64_t now)
if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) {
_lastCheckTime = now;
char tmp[512];
- const unsigned int len = Utils::snprintf(tmp,sizeof(tmp),
+ const unsigned int len = Utils::ztsnprintf(tmp,sizeof(tmp),
"%c{\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "\":%d,"
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "\":%d,"
"\"" ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "\":%d,"