Browse Source

Refactoring in progress... pardon our dust...

Adam Ierymenko 11 years ago
parent
commit
1d37204a37

+ 329 - 0
include/ZeroTierOne.h

@@ -0,0 +1,329 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2011-2014  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+/*
+ * This defines the external C API for ZeroTier One, the core network
+ * virtualization engine.
+ */
+
+#ifndef ZT_ZEROTIERONE_H
+#define ZT_ZEROTIERONE_H
+
+#include <stdint.h>
+
+/* ------------------------------------------------------------------------ */
+/* Query result buffers                                                     */
+/* ------------------------------------------------------------------------ */
+
+/**
+ * Node status result buffer
+ */
+struct ZT1_Node_Status
+{
+	/**
+	 * Public identity in string form
+	 */
+	char publicIdentity[256];
+
+	/**
+	 * ZeroTier address in 10-digit hex form
+	 */
+	char address[16];
+
+	/**
+	 * ZeroTier address in least significant 40 bits of 64-bit integer
+	 */
+	uint64_t rawAddress;
+
+	/**
+	 * Number of known peers (including supernodes)
+	 */
+	unsigned int knownPeers;
+
+	/**
+	 * Number of upstream supernodes
+	 */
+	unsigned int supernodes;
+
+	/**
+	 * Number of peers with active direct links
+	 */
+	unsigned int directlyConnectedPeers;
+
+	/**
+	 * Number of peers that have recently communicated with us
+	 */
+	unsigned int alivePeers;
+
+	/**
+	 * Success rate at establishing direct links (0.0 to 1.0, approximate)
+	 */
+	float directLinkSuccessRate;
+
+	/**
+	 * True if connectivity appears good
+	 */
+	bool online;
+
+	/**
+	 * True if running; all other fields are technically undefined if this is false
+	 */
+	bool running;
+};
+
+/**
+ * Physical address result buffer
+ */
+struct ZT1_Node_PhysicalAddress
+{
+	/**
+	 * Physical address type
+	 */
+	enum {
+		ZT1_Node_PhysicalAddress_TYPE_NULL = 0,     /* none/invalid */
+		ZT1_Node_PhysicalAddress_TYPE_IPV4 = 1,     /* 32-bit IPv4 address (and port) */
+		ZT1_Node_PhysicalAddress_TYPE_IPV6 = 2,     /* 128-bit IPv6 address (and port) */
+		ZT1_Node_PhysicalAddress_TYPE_ETHERNET = 3  /* 48-bit Ethernet MAC address */
+	} type;
+
+	/**
+	 * Address in raw binary form -- length depends on type
+	 */
+	unsigned char bits[16];
+
+	/**
+	 * Port or netmask bits (for IPV4 and IPV6)
+	 */
+	unsigned int port;
+
+	/**
+	 * Address in canonical human-readable form
+	 */
+	char ascii[64];
+
+	/**
+	 * Zone index identifier (thing after % on IPv6 link-local addresses only)
+	 */
+	char zoneIndex[16];
+};
+
+/**
+ * Network path result buffer
+ */
+struct ZT1_Node_PhysicalPath
+{
+	/**
+	 * Physical path type
+	 */
+	enum { /* These must be numerically the same as type in Path.hpp */
+		ZT1_Node_PhysicalPath_TYPE_NULL = 0,     /* none/invalid */
+		ZT1_Node_PhysicalPath_TYPE_UDP = 1,      /* UDP association */
+		ZT1_Node_PhysicalPath_TYPE_TCP_OUT = 2,  /* outgoing TCP tunnel using pseudo-SSL */
+		ZT1_Node_PhysicalPath_TYPE_TCP_IN = 3,   /* incoming TCP tunnel using pseudo-SSL */
+		ZT1_Node_PhysicalPath_TYPE_ETHERNET = 4  /* raw ethernet frames over trusted backplane */
+	} type;
+
+	/**
+	 * Physical address of endpoint
+	 */
+	struct ZT1_Node_PhysicalAddress address;
+
+	/**
+	 * Time since last send in milliseconds or -1 for never
+	 */
+	long lastSend;
+
+	/**
+	 * Time since last receive in milliseconds or -1 for never
+	 */
+	long lastReceive;
+
+	/**
+	 * Time since last ping in milliseconds or -1 for never
+	 */
+	long lastPing;
+
+	/**
+	 * Is path active/connected? Non-fixed active paths may be garbage collected over time.
+	 */
+	bool active;
+
+	/**
+	 * Is path fixed? (i.e. not learned, static)
+	 */
+	bool fixed;
+};
+
+/**
+ * Peer status result buffer
+ */
+struct ZT1_Node_Peer
+{
+	/**
+	 * Remote peer version: major.minor.revision (or empty if unknown)
+	 */
+	char remoteVersion[16];
+
+	/**
+	 * ZeroTier address of peer as 10-digit hex string
+	 */
+	char address[16];
+
+	/**
+	 * ZeroTier address in least significant 40 bits of 64-bit integer
+	 */
+	uint64_t rawAddress;
+
+	/**
+	 * Last measured latency in milliseconds or zero if unknown
+	 */
+	unsigned int latency;
+
+	/**
+	 * Array of network paths to peer
+	 */
+	struct ZT1_Node_PhysicalPath *paths;
+
+	/**
+	 * Number of paths (size of paths[])
+	 */
+	unsigned int numPaths;
+};
+
+/**
+ * List of peers
+ */
+struct ZT1_Node_PeerList
+{
+	struct ZT1_Node_Peer *peers;
+	unsigned int numPeers;
+};
+
+/**
+ * Network status result buffer
+ */
+struct ZT1_Node_Network
+{
+	/**
+	 * 64-bit network ID
+	 */
+	uint64_t nwid;
+
+	/**
+	 * 64-bit network ID in hex form
+	 */
+	char nwidHex[32];
+
+	/**
+	 * Short network name
+	 */
+	char name[256];
+
+	/**
+	 * Longer network description
+	 */
+	char description[4096];
+
+	/**
+	 * Device name (system-dependent)
+	 */
+	char device[256];
+
+	/**
+	 * Status code in string format
+	 */
+	char statusStr[64];
+
+	/**
+	 * Ethernet MAC address of this endpoint in string form
+	 */
+	char macStr[32];
+
+	/**
+	 * Ethernet MAC address of this endpoint on the network in raw binary form
+	 */
+	unsigned char mac[6];
+
+	/**
+	 * Age of configuration in milliseconds or -1 if never refreshed
+	 */
+	long configAge;
+
+	/**
+	 * Assigned layer-3 IPv4 and IPv6 addresses
+	 *
+	 * Note that PhysicalAddress also supports other address types, but this
+	 * list will only list IP address assignments. The port field will contain
+	 * the number of bits in the netmask -- e.g. 192.168.1.1/24.
+	 */
+	struct ZT1_Node_PhysicalAddress *ips;
+
+	/**
+	 * Number of layer-3 IPs (size of ips[])
+	 */
+	unsigned int numIps;
+
+	/**
+	 * Network status code
+	 */
+	enum { /* Must be same as Status in Network.hpp */
+		ZT1_Node_Network_INITIALIZING = 0,
+		ZT1_Node_Network_WAITING_FOR_FIRST_AUTOCONF = 1,
+		ZT1_Node_Network_OK = 2,
+		ZT1_Node_Network_ACCESS_DENIED = 3,
+		ZT1_Node_Network_NOT_FOUND = 4,
+		ZT1_Node_Network_INITIALIZATION_FAILED = 5,
+		ZT1_Node_Network_NO_MORE_DEVICES = 6
+	} status;
+
+	/**
+	 * True if traffic on network is enabled
+	 */
+	bool enabled;
+
+	/**
+	 * Is this a private network? If false, network lacks access control.
+	 */
+	bool isPrivate;
+};
+
+/**
+ * Return buffer for list of networks
+ */
+struct ZT1_Node_NetworkList
+{
+	struct ZT1_Node_Network *networks;
+	unsigned int numNetworks;
+};
+
+/* ------------------------------------------------------------------------ */
+/* ZeroTier One C API                                                       */
+/* ------------------------------------------------------------------------ */
+
+/* coming soon... */
+
+#endif

+ 0 - 0
node/IpcConnection.cpp → ipc/IpcConnection.cpp


+ 0 - 0
node/IpcConnection.hpp → ipc/IpcConnection.hpp


+ 0 - 0
node/IpcListener.cpp → ipc/IpcListener.cpp


+ 0 - 0
node/IpcListener.hpp → ipc/IpcListener.hpp


+ 166 - 0
ipc/NodeControlClient.cpp

@@ -0,0 +1,166 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2011-2014  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "NodeControlClient.hpp"
+
+#include "../node/Constants.hpp"
+#include "../node/Utils.hpp"
+
+namespace ZeroTier {
+
+struct _NodeControlClientImpl
+{
+	void (*resultHandler)(void *,const char *);
+	void *arg;
+	IpcConnection *ipcc;
+	std::string err;
+};
+
+static void _CBipcResultHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *result)
+{
+	if ((event == IpcConnection::IPC_EVENT_COMMAND)&&(result)) {
+		if (strcmp(result,"200 auth OK"))
+			((_NodeControlClientImpl *)arg)->resultHandler(((_NodeControlClientImpl *)arg)->arg,result);
+	}
+}
+
+NodeControlClient::NodeControlClient(const char *hp,void (*resultHandler)(void *,const char *),void *arg,const char *authToken)
+	throw() :
+	_impl((void *)new _NodeControlClientImpl)
+{
+	_NodeControlClientImpl *impl = (_NodeControlClientImpl *)_impl;
+	impl->ipcc = (IpcConnection *)0;
+
+	if (!hp)
+		hp = ZT_DEFAULTS.defaultHomePath.c_str();
+
+	std::string at;
+	if (authToken)
+		at = authToken;
+	else if (!Utils::readFile(authTokenDefaultSystemPath(),at)) {
+		if (!Utils::readFile(authTokenDefaultUserPath(),at)) {
+			impl->err = "no authentication token specified and authtoken.secret not readable";
+			return;
+		}
+	}
+
+	std::string myid;
+	if (Utils::readFile((std::string(hp) + ZT_PATH_SEPARATOR_S + "identity.public").c_str(),myid)) {
+		std::string myaddr(myid.substr(0,myid.find(':')));
+		if (myaddr.length() != 10)
+			impl->err = "invalid address extracted from identity.public";
+		else {
+			try {
+				impl->resultHandler = resultHandler;
+				impl->arg = arg;
+				impl->ipcc = new IpcConnection((std::string(ZT_IPC_ENDPOINT_BASE) + myaddr).c_str(),&_CBipcResultHandler,_impl);
+				impl->ipcc->printf("auth %s"ZT_EOL_S,at.c_str());
+			} catch ( ... ) {
+				impl->ipcc = (IpcConnection *)0;
+				impl->err = "failure connecting to running ZeroTier One service";
+			}
+		}
+	} else impl->err = "unable to read identity.public";
+}
+
+NodeControlClient::~NodeControlClient()
+{
+	if (_impl) {
+		delete ((_NodeControlClientImpl *)_impl)->ipcc;
+		delete (_NodeControlClientImpl *)_impl;
+	}
+}
+
+const char *NodeControlClient::error() const
+	throw()
+{
+	if (((_NodeControlClientImpl *)_impl)->err.length())
+		return ((_NodeControlClientImpl *)_impl)->err.c_str();
+	return (const char *)0;
+}
+
+void NodeControlClient::send(const char *command)
+	throw()
+{
+	try {
+		if (((_NodeControlClientImpl *)_impl)->ipcc)
+			((_NodeControlClientImpl *)_impl)->ipcc->printf("%s"ZT_EOL_S,command);
+	} catch ( ... ) {}
+}
+
+std::vector<std::string> NodeControlClient::splitLine(const char *line)
+{
+	return Utils::split(line," ","\\","\"");
+}
+
+const char *NodeControlClient::authTokenDefaultUserPath()
+{
+	static std::string dlp;
+	static Mutex dlp_m;
+
+	Mutex::Lock _l(dlp_m);
+
+#ifdef __WINDOWS__
+
+	if (!dlp.length()) {
+		char buf[16384];
+		if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_APPDATA,NULL,0,buf)))
+			dlp = (std::string(buf) + "\\ZeroTier\\One\\authtoken.secret");
+	}
+
+#else // not __WINDOWS__
+
+	if (!dlp.length()) {
+		const char *home = getenv("HOME");
+		if (home) {
+#ifdef __APPLE__
+			dlp = (std::string(home) + "/Library/Application Support/ZeroTier/One/authtoken.secret");
+#else
+			dlp = (std::string(home) + "/.zeroTierOneAuthToken");
+#endif
+		}
+	}
+
+#endif // __WINDOWS__ or not __WINDOWS__
+
+	return dlp.c_str();
+}
+
+const char *NodeControlClient::authTokenDefaultSystemPath()
+{
+	static std::string dsp;
+	static Mutex dsp_m;
+
+	Mutex::Lock _l(dsp_m);
+
+	if (!dsp.length())
+		dsp = (ZT_DEFAULTS.defaultHomePath + ZT_PATH_SEPARATOR_S"authtoken.secret");
+
+	return dsp.c_str();
+}
+
+} // namespace ZeroTier

+ 111 - 0
ipc/NodeControlClient.hpp

@@ -0,0 +1,111 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2011-2014  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <string>
+#include <vector>
+
+#ifndef ZT_NODECONTROLCLIENT_HPP
+#define ZT_NODECONTROLCLIENT_HPP
+
+namespace ZeroTier {
+
+/**
+ * Client for controlling a local ZeroTier One node
+ *
+ * Windows note: be sure you call WSAStartup() before using this,
+ * otherwise it will be unable to open a local UDP socket to
+ * communicate with the service.
+ */
+class NodeControlClient
+{
+public:
+	/**
+	 * Create a new node config client
+	 *
+	 * Initialization may fail. Call error() to check.
+	 *
+	 * @param hp Home path of ZeroTier One instance or NULL for default system home path
+	 * @param resultHandler Function to call when commands provide results
+	 * @param arg First argument to result handler
+	 * @param authToken Authentication token or NULL (default) to read from authtoken.secret in home path
+	 */
+	NodeControlClient(const char *hp,void (*resultHandler)(void *,const char *),void *arg,const char *authToken = (const char *)0)
+		throw();
+
+	~NodeControlClient();
+
+	/**
+	 * @return Initialization error or NULL if none
+	 */
+	const char *error() const
+		throw();
+
+	/**
+	 * Send a command to the local node
+	 *
+	 * Note that the returned conversation ID will never be 0. A return value
+	 * of 0 indicates a fatal error such as failure to bind to any local UDP
+	 * port.
+	 *
+	 * @param command
+	 * @return Conversation ID that will be provided to result handler when/if results are sent back
+	 */
+	void send(const char *command)
+		throw();
+	inline void send(const std::string &command)
+		throw() { return send(command.c_str()); }
+
+	/**
+	 * Split a line of results
+	 *
+	 * @param line Line to split
+	 * @return Vector of fields
+	 */
+	static std::vector<std::string> splitLine(const char *line);
+	static inline std::vector<std::string> splitLine(const std::string &line) { return splitLine(line.c_str()); }
+
+	/**
+	 * @return Default path for current user's authtoken.secret
+	 */
+	static const char *authTokenDefaultUserPath();
+
+	/**
+	 * @return Default path to system authtoken.secret
+	 */
+	static const char *authTokenDefaultSystemPath();
+
+private:
+	// NodeControlClient is not copyable
+	NodeControlClient(const NodeControlClient&);
+	const NodeControlClient& operator=(const NodeControlClient&);
+
+	void *_impl;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 9 - 0
node/Address.hpp

@@ -193,6 +193,15 @@ public:
 		return std::string(buf);
 	};
 
+	/**
+	 * @param buf Buffer to fill
+	 * @param len Length of buffer
+	 */
+	inline void toString(char *buf,unsigned int len) const
+	{
+		Utils::snprintf(buf,len,"%.10llx",(unsigned long long)_a);
+	}
+
 	/**
 	 * @return True if this address is not zero
 	 */

+ 0 - 4
node/EthernetTapFactory.hpp

@@ -30,10 +30,6 @@
 
 #include <stdint.h>
 
-#include <stdexcept>
-#include <vector>
-#include <string>
-
 #include "MAC.hpp"
 #include "NonCopyable.hpp"
 #include "Buffer.hpp"

+ 11 - 8
node/MAC.hpp

@@ -170,14 +170,17 @@ public:
 	inline std::string toString() const
 	{
 		char tmp[24];
-		std::string s;
-		Utils::snprintf(tmp,sizeof(tmp),"%.12llx",_m);
-		for(int i=0;i<12;++i) {
-			if ((i > 0)&&((i % 2) == 0))
-				s.push_back(':');
-			s.push_back(tmp[i]);
-		}
-		return s;
+		toString(tmp,sizeof(tmp));
+		return std::string(tmp);
+	}
+
+	/**
+	 * @param buf Buffer to contain human-readable MAC
+	 * @param len Length of buffer
+	 */
+	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]);
 	}
 
 	/**

+ 7 - 7
node/Network.hpp

@@ -110,13 +110,13 @@ public:
 	 */
 	enum Status
 	{
-		NETWORK_INITIALIZING,               // Creating tap device and setting up state
-		NETWORK_WAITING_FOR_FIRST_AUTOCONF, // Waiting for initial setup with netconf master
-		NETWORK_OK,                         // Network is up, seems to be working
-		NETWORK_ACCESS_DENIED,              // Netconf node reported permission denied
-		NETWORK_NOT_FOUND,                  // Netconf node reported network not found
-		NETWORK_INITIALIZATION_FAILED,      // Cannot initialize device (OS/installation problem?)
-		NETWORK_NO_MORE_DEVICES             // OS cannot create any more tap devices (some OSes have a limit)
+		NETWORK_INITIALIZING = 0,               // Creating tap device and setting up state
+		NETWORK_WAITING_FOR_FIRST_AUTOCONF = 1, // Waiting for initial setup with netconf master
+		NETWORK_OK = 2,                         // Network is up, seems to be working
+		NETWORK_ACCESS_DENIED = 3,              // Netconf node reported permission denied
+		NETWORK_NOT_FOUND = 4,                  // Netconf node reported network not found
+		NETWORK_INITIALIZATION_FAILED = 5,      // Cannot initialize device (OS/installation problem?)
+		NETWORK_NO_MORE_DEVICES = 6             // OS cannot create any more tap devices (some OSes have a limit)
 	};
 
 	/**

+ 287 - 137
node/Node.cpp

@@ -74,148 +74,12 @@
 #include "Service.hpp"
 #include "SoftwareUpdater.hpp"
 #include "Buffer.hpp"
-#include "IpcConnection.hpp"
 #include "AntiRecursion.hpp"
 #include "RoutingTable.hpp"
 #include "HttpClient.hpp"
 
 namespace ZeroTier {
 
-// ---------------------------------------------------------------------------
-
-struct _NodeControlClientImpl
-{
-	void (*resultHandler)(void *,const char *);
-	void *arg;
-	IpcConnection *ipcc;
-	std::string err;
-};
-
-static void _CBipcResultHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *result)
-{
-	if ((event == IpcConnection::IPC_EVENT_COMMAND)&&(result)) {
-		if (strcmp(result,"200 auth OK"))
-			((_NodeControlClientImpl *)arg)->resultHandler(((_NodeControlClientImpl *)arg)->arg,result);
-	}
-}
-
-Node::NodeControlClient::NodeControlClient(const char *hp,void (*resultHandler)(void *,const char *),void *arg,const char *authToken)
-	throw() :
-	_impl((void *)new _NodeControlClientImpl)
-{
-	_NodeControlClientImpl *impl = (_NodeControlClientImpl *)_impl;
-	impl->ipcc = (IpcConnection *)0;
-
-	if (!hp)
-		hp = ZT_DEFAULTS.defaultHomePath.c_str();
-
-	std::string at;
-	if (authToken)
-		at = authToken;
-	else if (!Utils::readFile(authTokenDefaultSystemPath(),at)) {
-		if (!Utils::readFile(authTokenDefaultUserPath(),at)) {
-			impl->err = "no authentication token specified and authtoken.secret not readable";
-			return;
-		}
-	}
-
-	std::string myid;
-	if (Utils::readFile((std::string(hp) + ZT_PATH_SEPARATOR_S + "identity.public").c_str(),myid)) {
-		std::string myaddr(myid.substr(0,myid.find(':')));
-		if (myaddr.length() != 10)
-			impl->err = "invalid address extracted from identity.public";
-		else {
-			try {
-				impl->resultHandler = resultHandler;
-				impl->arg = arg;
-				impl->ipcc = new IpcConnection((std::string(ZT_IPC_ENDPOINT_BASE) + myaddr).c_str(),&_CBipcResultHandler,_impl);
-				impl->ipcc->printf("auth %s"ZT_EOL_S,at.c_str());
-			} catch ( ... ) {
-				impl->ipcc = (IpcConnection *)0;
-				impl->err = "failure connecting to running ZeroTier One service";
-			}
-		}
-	} else impl->err = "unable to read identity.public";
-}
-
-Node::NodeControlClient::~NodeControlClient()
-{
-	if (_impl) {
-		delete ((_NodeControlClientImpl *)_impl)->ipcc;
-		delete (_NodeControlClientImpl *)_impl;
-	}
-}
-
-const char *Node::NodeControlClient::error() const
-	throw()
-{
-	if (((_NodeControlClientImpl *)_impl)->err.length())
-		return ((_NodeControlClientImpl *)_impl)->err.c_str();
-	return (const char *)0;
-}
-
-void Node::NodeControlClient::send(const char *command)
-	throw()
-{
-	try {
-		if (((_NodeControlClientImpl *)_impl)->ipcc)
-			((_NodeControlClientImpl *)_impl)->ipcc->printf("%s"ZT_EOL_S,command);
-	} catch ( ... ) {}
-}
-
-std::vector<std::string> Node::NodeControlClient::splitLine(const char *line)
-{
-	return Utils::split(line," ","\\","\"");
-}
-
-const char *Node::NodeControlClient::authTokenDefaultUserPath()
-{
-	static std::string dlp;
-	static Mutex dlp_m;
-
-	Mutex::Lock _l(dlp_m);
-
-#ifdef __WINDOWS__
-
-	if (!dlp.length()) {
-		char buf[16384];
-		if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_APPDATA,NULL,0,buf)))
-			dlp = (std::string(buf) + "\\ZeroTier\\One\\authtoken.secret");
-	}
-
-#else // not __WINDOWS__
-
-	if (!dlp.length()) {
-		const char *home = getenv("HOME");
-		if (home) {
-#ifdef __APPLE__
-			dlp = (std::string(home) + "/Library/Application Support/ZeroTier/One/authtoken.secret");
-#else
-			dlp = (std::string(home) + "/.zeroTierOneAuthToken");
-#endif
-		}
-	}
-
-#endif // __WINDOWS__ or not __WINDOWS__
-
-	return dlp.c_str();
-}
-
-const char *Node::NodeControlClient::authTokenDefaultSystemPath()
-{
-	static std::string dsp;
-	static Mutex dsp_m;
-
-	Mutex::Lock _l(dsp_m);
-
-	if (!dsp.length())
-		dsp = (ZT_DEFAULTS.defaultHomePath + ZT_PATH_SEPARATOR_S"authtoken.secret");
-
-	return dsp.c_str();
-}
-
-// ---------------------------------------------------------------------------
-
 struct _NodeImpl
 {
 	RuntimeEnvironment renv;
@@ -541,6 +405,7 @@ Node::ReasonForTermination Node::run()
 			return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unable to initialize IPC socket: is ZeroTier One already running?");
 		}
 		_r->node = this;
+
 #ifdef ZT_AUTO_UPDATE
 		if (ZT_DEFAULTS.updateLatestNfoURL.length()) {
 			_r->updater = new SoftwareUpdater(_r);
@@ -799,7 +664,7 @@ Node::ReasonForTermination Node::run()
 	return impl->terminate();
 }
 
-const char *Node::reasonForTermination() const
+const char *Node::terminationMessage() const
 	throw()
 {
 	if ((!((_NodeImpl *)_impl)->started)||(((_NodeImpl *)_impl)->running))
@@ -822,6 +687,291 @@ void Node::resync()
 	((_NodeImpl *)_impl)->renv.sm->whack();
 }
 
+bool Node::online()
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	if (!impl->running)
+		return false;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+	uint64_t now = Utils::now();
+	uint64_t since = _r->timeOfLastResynchronize;
+	std::vector< SharedPtr<Peer> > snp(_r->topology->supernodePeers());
+	for(std::vector< SharedPtr<Peer> >::const_iterator sn(snp.begin());sn!=snp.end();++sn) {
+		uint64_t lastRec = (*sn)->lastDirectReceive();
+		if ((lastRec)&&(lastRec > since)&&((now - lastRec) < ZT_PEER_PATH_ACTIVITY_TIMEOUT))
+			return true;
+	}
+	return false;
+}
+
+void Node::join(uint64_t nwid)
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+	_r->nc->join(nwid);
+}
+
+void Node::leave(uint64_t nwid)
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+	_r->nc->leave(nwid);
+}
+
+struct GatherPeerStatistics
+{
+	uint64_t now;
+	ZT1_Node_Status *status;
+	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+	{
+		++status->knownPeers;
+		if (p->hasActiveDirectPath(now))
+			++status->directlyConnectedPeers;
+		if (p->alive(now))
+			++status->alivePeers;
+	}
+};
+void Node::status(ZT1_Node_Status *status)
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+
+	memset(status,0,sizeof(ZT1_Node_Status));
+
+	Utils::scopy(status->publicIdentity,sizeof(status->publicIdentity),_r->identity.toString(false).c_str());
+	_r->identity.address().toString(status->address,sizeof(status->address));
+	status->rawAddress = _r->identity.address().toInt();
+
+	status->knownPeers = 0;
+	status->supernodes = _r->topology->numSupernodes();
+	status->directlyConnectedPeers = 0;
+	status->alivePeers = 0;
+	GatherPeerStatistics gps;
+	gps.now = Utils::now();
+	gps.status = status;
+	_r->topology->eachPeer(gps);
+
+	if (status->alivePeers > 0) {
+		double dlsr = (double)status->directlyConnectedPeers / (double)status->alivePeers;
+		if (dlsr > 1.0) dlsr = 1.0;
+		if (dlsr < 0.0) dlsr = 0.0;
+		status->directLinkSuccessRate = (float)dlsr;
+	} else status->directLinkSuccessRate = 1.0f; // no connections to no active peers == 100% success at nothing
+
+	status->online = online();
+	status->running = impl->running;
+}
+
+struct CollectPeersAndPaths
+{
+	std::vector< std::pair< SharedPtr<Peer>,std::vector<Path> > > data;
+	inline void operator()(Topology &t,const SharedPtr<Peer> &p) { data.push_back(std::pair< SharedPtr<Peer>,std::vector<Path> >(p,p->paths())); }
+};
+struct SortPeersAndPathsInAscendingAddressOrder
+{
+	inline bool operator()(const std::pair< SharedPtr<Peer>,std::vector<Path> > &a,const std::pair< SharedPtr<Peer>,std::vector<Path> > &b) const { return (a.first->address() < b.first->address()); }
+};
+ZT1_Node_PeerList *Node::listPeers()
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+
+	CollectPeersAndPaths pp;
+	_r->topology->eachPeer(pp);
+	std::sort(pp.data.begin(),pp.data.end(),SortPeersAndPathsInAscendingAddressOrder());
+
+	unsigned int returnBufSize = sizeof(ZT1_Node_PeerList);
+	for(std::vector< std::pair< SharedPtr<Peer>,std::vector<Path> > >::iterator p(pp.data.begin());p!=pp.data.end();++p)
+		returnBufSize += sizeof(ZT1_Node_Peer) + (sizeof(ZT1_Node_PhysicalPath) * p->second.size());
+
+	char *buf = (char *)::malloc(returnBufSize);
+	if (!buf)
+		return (ZT1_Node_PeerList *)0;
+	memset(buf,0,returnBufSize);
+
+	ZT1_Node_PeerList *pl = (ZT1_Node_PeerList *)buf;
+	buf += sizeof(ZT1_Node_PeerList);
+
+	pl->peers = (ZT1_Node_Peer *)buf;
+	buf += (sizeof(ZT1_Node_Peer) * pp.data.size());
+	pl->numPeers = 0;
+
+	uint64_t now = Utils::now();
+	for(std::vector< std::pair< SharedPtr<Peer>,std::vector<Path> > >::iterator p(pp.data.begin());p!=pp.data.end();++p) {
+		ZT1_Node_Peer *prec = &(pl->peers[pl->numPeers++]);
+		if (p->first->remoteVersionKnown())
+			Utils::snprintf(prec->remoteVersion,sizeof(prec->remoteVersion),"%u.%u.%u",p->first->remoteVersionMajor(),p->first->remoteVersionMinor(),p->first->remoteVersionRevision());
+		p->first->address().toString(prec->address,sizeof(prec->address));
+		prec->rawAddress = p->first->address().toInt();
+		prec->latency = p->first->latency();
+
+		prec->paths = (ZT1_Node_PhysicalPath *)buf;
+		buf += sizeof(ZT1_Node_PhysicalPath) * p->second.size();
+
+		prec->numPaths = 0;
+		for(std::vector<Path>::iterator pi(p->second.begin());pi!=p->second.end();++pi) {
+			ZT1_Node_PhysicalPath *path = &(prec->paths[prec->numPaths++]);
+			path->type = static_cast<typeof(path->type)>(pi->type());
+			if (pi->address().isV6()) {
+				path->address.type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV6;
+				memcpy(path->address.bits,pi->address().rawIpData(),16);
+				// TODO: zoneIndex not supported yet, but should be once echo-location works w/V6
+			} else {
+				path->address.type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV4;
+				memcpy(path->address.bits,pi->address().rawIpData(),4);
+			}
+			path->address.port = pi->address().port();
+			Utils::scopy(path->address.ascii,sizeof(path->address.ascii),pi->address().toIpString().c_str());
+			path->lastSend = (pi->lastSend() > 0) ? ((long)(now - pi->lastSend())) : (long)-1;
+			path->lastReceive = (pi->lastReceived() > 0) ? ((long)(now - pi->lastReceived())) : (long)-1;
+			path->lastPing = (pi->lastPing() > 0) ? ((long)(now - pi->lastPing())) : (long)-1;
+			path->active = pi->active(now);
+			path->fixed = pi->fixed();
+		}
+	}
+
+	return pl;
+}
+
+// Fills out everything but ips[] and numIps, which must be done more manually
+static void _fillNetworkQueryResultBuffer(const SharedPtr<Network> &network,const SharedPtr<NetworkConfig> &nconf,ZT1_Node_Network *nbuf)
+{
+	nbuf->nwid = network->id();
+	Utils::snprintf(nbuf->nwidHex,sizeof(nbuf->nwidHex),"%.16llx",(unsigned long long)network->id());
+	if (nconf) {
+		Utils::scopy(nbuf->name,sizeof(nbuf->name),nconf->name().c_str());
+		Utils::scopy(nbuf->description,sizeof(nbuf->description),nconf->description().c_str());
+	}
+	Utils::scopy(nbuf->device,sizeof(nbuf->device),network->tapDeviceName().c_str());
+	Utils::scopy(nbuf->statusStr,sizeof(nbuf->statusStr),Network::statusString(network->status()));
+	network->mac().toString(nbuf->macStr,sizeof(nbuf->macStr));
+	network->mac().copyTo(nbuf->mac,sizeof(nbuf->mac));
+	uint64_t lcu = network->lastConfigUpdate();
+	if (lcu > 0)
+		nbuf->configAge = (long)(Utils::now() - lcu);
+	else nbuf->configAge = -1;
+	nbuf->status = static_cast<typeof(nbuf->status)>(network->status());
+	nbuf->enabled = network->enabled();
+	nbuf->isPrivate = (nconf) ? nconf->isPrivate() : true;
+}
+
+ZT1_Node_Network *Node::getNetworkStatus(uint64_t nwid)
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+
+	SharedPtr<Network> network(_r->nc->network(nwid));
+	if (!network)
+		return (ZT1_Node_Network *)0;
+	SharedPtr<NetworkConfig> nconf(network->config2());
+	std::set<InetAddress> ips(network->ips());
+
+	char *buf = (char *)::malloc(sizeof(ZT1_Node_Network) + (sizeof(ZT1_Node_PhysicalAddress) * ips.size()));
+	if (!buf)
+		return (ZT1_Node_Network *)0;
+	memset(buf,0,sizeof(ZT1_Node_Network) + (sizeof(ZT1_Node_PhysicalAddress) * ips.size()));
+
+	ZT1_Node_Network *nbuf = (ZT1_Node_Network *)buf;
+	buf += sizeof(ZT1_Node_Network);
+
+	_fillNetworkQueryResultBuffer(network,nconf,nbuf);
+
+	nbuf->ips = (ZT1_Node_PhysicalAddress *)buf;
+	nbuf->numIps = 0;
+	for(std::set<InetAddress>::iterator ip(ips.begin());ip!=ips.end();++ip) {
+		ZT1_Node_PhysicalAddress *ipb = &(nbuf->ips[nbuf->numIps++]);
+		if (ip->isV6()) {
+			ipb->type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV6;
+			memcpy(ipb->bits,ip->rawIpData(),16);
+		} else {
+			ipb->type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV4;
+			memcpy(ipb->bits,ip->rawIpData(),4);
+		}
+		ipb->port = ip->port();
+		Utils::scopy(ipb->ascii,sizeof(ipb->ascii),ip->toIpString().c_str());
+	}
+
+	return nbuf;
+}
+
+ZT1_Node_NetworkList *Node::listNetworks()
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+
+	std::vector< SharedPtr<Network> > networks(_r->nc->networks());
+	std::vector< SharedPtr<NetworkConfig> > nconfs(networks.size());
+	std::vector< std::set<InetAddress> > ipsv(networks.size());
+
+	unsigned long returnBufSize = sizeof(ZT1_Node_NetworkList);
+	for(unsigned long i=0;i<networks.size();++i) {
+		nconfs[i] = networks[i]->config2();
+		ipsv[i] = networks[i]->ips();
+		returnBufSize += sizeof(ZT1_Node_Network) + (sizeof(ZT1_Node_PhysicalAddress) * ipsv[i].size());
+	}
+
+	char *buf = (char *)::malloc(returnBufSize);
+	if (!buf)
+		return (ZT1_Node_NetworkList *)0;
+	memset(buf,0,returnBufSize);
+
+	ZT1_Node_NetworkList *nl = (ZT1_Node_NetworkList *)buf;
+	buf += sizeof(ZT1_Node_NetworkList);
+
+	nl->networks = (ZT1_Node_Network *)buf;
+	buf += sizeof(ZT1_Node_Network) * networks.size();
+
+	for(unsigned long i=0;i<networks.size();++i) {
+		ZT1_Node_Network *nbuf = &(nl->networks[nl->numNetworks++]);
+
+		_fillNetworkQueryResultBuffer(networks[i],nconfs[i],nbuf);
+
+		nbuf->ips = (ZT1_Node_PhysicalAddress *)buf;
+		buf += sizeof(ZT1_Node_PhysicalAddress);
+
+		nbuf->numIps = 0;
+		for(std::set<InetAddress>::iterator ip(ipsv[i].begin());ip!=ipsv[i].end();++ip) {
+			ZT1_Node_PhysicalAddress *ipb = &(nbuf->ips[nbuf->numIps++]);
+			if (ip->isV6()) {
+				ipb->type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV6;
+				memcpy(ipb->bits,ip->rawIpData(),16);
+			} else {
+				ipb->type = ZT1_Node_PhysicalAddress::ZT1_Node_PhysicalAddress_TYPE_IPV4;
+				memcpy(ipb->bits,ip->rawIpData(),4);
+			}
+			ipb->port = ip->port();
+			Utils::scopy(ipb->ascii,sizeof(ipb->ascii),ip->toIpString().c_str());
+		}
+	}
+
+	return nl;
+}
+
+void Node::freeQueryResult(void *qr)
+	throw()
+{
+	::free(qr);
+}
+
+bool Node::updateCheck()
+	throw()
+{
+	_NodeImpl *impl = (_NodeImpl *)_impl;
+	RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+	if (_r->updater) {
+		_r->updater->checkNow();
+		return true;
+	}
+	return false;
+}
+
 class _VersionStringMaker
 {
 public:

+ 55 - 100
node/Node.hpp

@@ -28,8 +28,9 @@
 #ifndef ZT_NODE_HPP
 #define ZT_NODE_HPP
 
-#include <string>
-#include <vector>
+#include <stdint.h>
+
+#include "../include/ZeroTierOne.h"
 
 namespace ZeroTier {
 
@@ -38,87 +39,10 @@ class RoutingTable;
 
 /**
  * A ZeroTier One node
- *
- * This class hides all its implementation details and all other classes in
- * preparation for ZeroTier One being made available in library form for
- * embedding in mobile apps.
  */
 class Node
 {
 public:
-	/**
-	 * Client for controlling a local ZeroTier One node
-	 *
-	 * Windows note: be sure you call WSAStartup() before using this,
-	 * otherwise it will be unable to open a local UDP socket to
-	 * communicate with the service.
-	 */
-	class NodeControlClient
-	{
-	public:
-		/**
-		 * Create a new node config client
-		 *
-		 * Initialization may fail. Call error() to check.
-		 *
-		 * @param hp Home path of ZeroTier One instance or NULL for default system home path
-		 * @param resultHandler Function to call when commands provide results
-		 * @param arg First argument to result handler
-		 * @param authToken Authentication token or NULL (default) to read from authtoken.secret in home path
-		 */
-		NodeControlClient(const char *hp,void (*resultHandler)(void *,const char *),void *arg,const char *authToken = (const char *)0)
-			throw();
-
-		~NodeControlClient();
-
-		/**
-		 * @return Initialization error or NULL if none
-		 */
-		const char *error() const
-			throw();
-
-		/**
-		 * Send a command to the local node
-		 *
-		 * Note that the returned conversation ID will never be 0. A return value
-		 * of 0 indicates a fatal error such as failure to bind to any local UDP
-		 * port.
-		 *
-		 * @param command
-		 * @return Conversation ID that will be provided to result handler when/if results are sent back
-		 */
-		void send(const char *command)
-			throw();
-		inline void send(const std::string &command)
-			throw() { return send(command.c_str()); }
-
-		/**
-		 * Split a line of results
-		 *
-		 * @param line Line to split
-		 * @return Vector of fields
-		 */
-		static std::vector<std::string> splitLine(const char *line);
-		static inline std::vector<std::string> splitLine(const std::string &line) { return splitLine(line.c_str()); }
-
-		/**
-		 * @return Default path for current user's authtoken.secret
-		 */
-		static const char *authTokenDefaultUserPath();
-
-		/**
-		 * @return Default path to system authtoken.secret
-		 */
-		static const char *authTokenDefaultSystemPath();
-
-	private:
-		// NodeControlClient is not copyable
-		NodeControlClient(const NodeControlClient&);
-		const NodeControlClient& operator=(const NodeControlClient&);
-
-		void *_impl;
-	};
-
 	/**
 	 * Returned by node main if/when it terminates
 	 */
@@ -155,7 +79,7 @@ public:
 	 *
 	 * The node is not executed until run() is called. The supplied tap factory
 	 * and routing table must not be freed until the node is no longer
-	 * executing. Node does not delete these objects, so the caller still owns
+	 * executing. Node does not delete these objects; the caller still owns
 	 * them.
 	 *
 	 * @param hp Home directory path or NULL for system-wide default for this platform
@@ -171,18 +95,12 @@ public:
 		RoutingTable *rt,
 		unsigned int udpPort,
 		unsigned int tcpPort,
-		bool resetIdentity)
-		throw();
+		bool resetIdentity) throw();
 
 	~Node();
 
 	/**
-	 * Execute node in current thread
-	 *
-	 * This does not return until the node shuts down. Shutdown may be caused
-	 * by an internally detected condition such as a new upgrade being
-	 * available or a fatal error, or it may be signaled externally using
-	 * the terminate() method.
+	 * Execute node in current thread, return on shutdown
 	 *
 	 * @return Reason for termination
 	 */
@@ -194,56 +112,93 @@ public:
 	 *
 	 * @return Reason for node termination or NULL if run() has not returned
 	 */
-	const char *reasonForTermination() const
+	const char *terminationMessage() const
 		throw();
 
 	/**
 	 * Terminate this node, causing run() to return
 	 *
 	 * @param reason Reason for termination
-	 * @param reasonText Text to be returned by reasonForTermination()
+	 * @param reasonText Text to be returned by terminationMessage()
 	 */
 	void terminate(ReasonForTermination reason,const char *reasonText)
 		throw();
 
 	/**
 	 * Forget p2p links now and resynchronize with peers
+	 *
+	 * This can be used if the containing application knows its network environment has
+	 * changed. ZeroTier itself tries to detect such changes, but is not always successful.
 	 */
 	void resync()
 		throw();
 
+	/**
+	 * @return True if we appear to be online in some viable capacity
+	 */
+	bool online()
+		throw();
+
 	/**
 	 * Join a network
 	 *
-	 * @param nwid 16-digit hex network ID
+	 * Use getNetworkStatus() to check the network's status after joining. If you
+	 * are already a member of the network, this does nothing.
+	 *
+	 * @param nwid 64-bit network ID
 	 */
-	bool join(const char *nwid)
+	void join(uint64_t nwid)
 		throw();
 
 	/**
 	 * Leave a network
 	 *
-	 * @param nwid 16-digit hex network ID
+	 * @param nwid 64-bit network ID
+	 */
+	void leave(uint64_t nwid)
+		throw();
+
+	/**
+	 * Get the status of this node
+	 *
+	 * @param status Buffer to fill with status information
 	 */
-	bool leave(const char *nwid)
+	void status(ZT1_Node_Status *status)
 		throw();
 
-	void listPeers()
+	/**
+	 * @return List of known peers or NULL on failure
+	 */
+	ZT1_Node_PeerList *listPeers()
 		throw();
 
-	void listNetworks()
+	/**
+	 * @param nwid 64-bit network ID
+	 * @return Network status or NULL if we are not a member of this network
+	 */
+	ZT1_Node_Network *getNetworkStatus(uint64_t nwid)
 		throw();
 
 	/**
-	 * Check for software updates (if enabled)
+	 * @return List of networks we've joined or NULL on failure
 	 */
-	bool updateCheck()
+	ZT1_Node_NetworkList *listNetworks()
 		throw();
 
 	/**
-	 * @return Description of last non-fatal error or empty string if none
+	 * Free a query result buffer
+	 *
+	 * Use this to free the return values of listNetworks(), listPeers(), etc.
+	 *
+	 * @param qr Query result buffer
+	 */
+	void freeQueryResult(void *qr)
+		throw();
+
+	/**
+	 * Check for software updates (if enabled) (updates will eventually get factored out of node/)
 	 */
-	const char *getLastError()
+	bool updateCheck()
 		throw();
 
 	static const char *versionString() throw();

+ 7 - 3
node/NodeConfig.cpp

@@ -52,9 +52,9 @@
 namespace ZeroTier {
 
 NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken) :
-	_r(renv),
-	_ipcListener((std::string(ZT_IPC_ENDPOINT_BASE) + renv->identity.address().toString()).c_str(),&_CBcommandHandler,this),
-	_authToken(authToken)
+	_r(renv)
+//	_ipcListener((std::string(ZT_IPC_ENDPOINT_BASE) + renv->identity.address().toString()).c_str(),&_CBcommandHandler,this),
+//	_authToken(authToken)
 {
 	{
 		Mutex::Lock _l(_localConfig_m);
@@ -91,10 +91,12 @@ NodeConfig::~NodeConfig()
 	_writeLocalConfig();
 
 	// Close any open IPC connections
+	/*
 	Mutex::Lock _l(_connections_m);
 	for(std::map< IpcConnection *,bool >::iterator c(_connections.begin());c!=_connections.end();++c)
 		delete c->first;
 	_connections.clear();
+	*/
 }
 
 void NodeConfig::putLocalConfig(const std::string &key,const char *value)
@@ -127,6 +129,7 @@ void NodeConfig::clean()
 		n->second->clean();
 }
 
+/*
 void NodeConfig::_CBcommandHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *commandLine)
 {
 	switch(event) {
@@ -310,6 +313,7 @@ void NodeConfig::_doCommand(IpcConnection *ipcc,const char *commandLine)
 
 	ipcc->printf("."ZT_EOL_S); // blank line ends response
 }
+*/
 
 void NodeConfig::_readLocalConfig()
 {

+ 36 - 2
node/NodeConfig.hpp

@@ -36,8 +36,6 @@
 #include <vector>
 #include <stdexcept>
 
-#include "IpcListener.hpp"
-#include "IpcConnection.hpp"
 #include "SharedPtr.hpp"
 #include "Network.hpp"
 #include "Utils.hpp"
@@ -104,6 +102,38 @@ public:
 		return nwlist;
 	}
 
+	/**
+	 * Join a network or return existing network if already joined
+	 *
+	 * @param nwid Network ID to join
+	 * @return New network instance
+	 */
+	inline SharedPtr<Network> join(uint64_t nwid)
+	{
+		Mutex::Lock _l(_networks_m);
+		SharedPtr<Network> &nw = _networks[nwid];
+		if (nw)
+			return nw;
+		else return (nw = Network::newInstance(_r,this,nwid));
+	}
+
+	/**
+	 * Leave a network
+	 *
+	 * @param nwid Network ID
+	 * @return True if network was left, false if we were not a member of this network
+	 */
+	inline bool leave(uint64_t nwid)
+	{
+		Mutex::Lock _l(_networks_m);
+		std::map< uint64_t,SharedPtr<Network> >::iterator n(_networks.find(nwid));
+		if (n != _networks.end()) {
+			n->second->destroy();
+			_networks.erase(n);
+			return true;
+		} else return false;
+	}
+
 	/**
 	 * Perform cleanup and possibly persist saved state
 	 */
@@ -135,18 +165,22 @@ public:
 	}
 
 private:
+	/*
 	static void _CBcommandHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *commandLine);
 	void _doCommand(IpcConnection *ipcc,const char *commandLine);
+	*/
 
 	void _readLocalConfig();
 	void _writeLocalConfig();
 
 	const RuntimeEnvironment *_r;
 
+	/*
 	IpcListener _ipcListener;
 	std::string _authToken;
 	std::map< IpcConnection *,bool > _connections;
 	Mutex _connections_m;
+	*/
 
 	Dictionary _localConfig; // persisted as local.conf
 	Mutex _localConfig_m;

+ 4 - 12
node/Peer.hpp

@@ -363,18 +363,10 @@ public:
 		_vRevision = vrev;
 	}
 
-	/**
-	 * @return Remote version in string form or '?' if unknown
-	 */
-	inline std::string remoteVersion() const
-	{
-		if ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)) {
-			char tmp[32];
-			Utils::snprintf(tmp,sizeof(tmp),"%u.%u.%u",_vMajor,_vMinor,_vRevision);
-			return std::string(tmp);
-		}
-		return std::string("?.?.?");
-	}
+	inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
+	inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
+	inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
+	inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
 
 	/**
 	 * Get most recently active UDP path addresses for IPv4 and/or IPv6

+ 9 - 0
node/Topology.hpp

@@ -125,6 +125,15 @@ public:
 		return _supernodePeers;
 	}
 
+	/**
+	 * @return Number of supernodes
+	 */
+	inline unsigned int numSupernodes() const
+	{
+		Mutex::Lock _l(_supernodes_m);
+		return _supernodePeers.size();
+	}
+
 	/**
 	 * Get the current favorite supernode
 	 * 

+ 3 - 2
objects.mk

@@ -1,5 +1,8 @@
 OBJS=\
 	ext/lz4/lz4.o \
+	ipc/IpcConnection.o \
+	ipc/IpcListener.o \
+	ipc/NodeControlClient.o \
 	node/C25519.o \
 	node/CertificateOfMembership.o \
 	node/Defaults.o \
@@ -7,8 +10,6 @@ OBJS=\
 	node/HttpClient.o \
 	node/Identity.o \
 	node/InetAddress.o \
-	node/IpcConnection.o \
-	node/IpcListener.o \
 	node/Logger.o \
 	node/Multicaster.o \
 	node/Network.o \