diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h new file mode 100644 index 000000000..2a00f5421 --- /dev/null +++ b/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 . + * + * -- + * + * 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 + +/* ------------------------------------------------------------------------ */ +/* 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 diff --git a/node/IpcConnection.cpp b/ipc/IpcConnection.cpp similarity index 100% rename from node/IpcConnection.cpp rename to ipc/IpcConnection.cpp diff --git a/node/IpcConnection.hpp b/ipc/IpcConnection.hpp similarity index 100% rename from node/IpcConnection.hpp rename to ipc/IpcConnection.hpp diff --git a/node/IpcListener.cpp b/ipc/IpcListener.cpp similarity index 100% rename from node/IpcListener.cpp rename to ipc/IpcListener.cpp diff --git a/node/IpcListener.hpp b/ipc/IpcListener.hpp similarity index 100% rename from node/IpcListener.hpp rename to ipc/IpcListener.hpp diff --git a/ipc/NodeControlClient.cpp b/ipc/NodeControlClient.cpp new file mode 100644 index 000000000..71ee3cda4 --- /dev/null +++ b/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 . + * + * -- + * + * 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 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 diff --git a/ipc/NodeControlClient.hpp b/ipc/NodeControlClient.hpp new file mode 100644 index 000000000..8060b20c0 --- /dev/null +++ b/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 . + * + * -- + * + * 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 +#include + +#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 splitLine(const char *line); + static inline std::vector 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 diff --git a/node/Address.hpp b/node/Address.hpp index c63d41d93..728862daa 100644 --- a/node/Address.hpp +++ b/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 */ diff --git a/node/EthernetTapFactory.hpp b/node/EthernetTapFactory.hpp index d0f5e9b34..c037f451a 100644 --- a/node/EthernetTapFactory.hpp +++ b/node/EthernetTapFactory.hpp @@ -30,10 +30,6 @@ #include -#include -#include -#include - #include "MAC.hpp" #include "NonCopyable.hpp" #include "Buffer.hpp" diff --git a/node/MAC.hpp b/node/MAC.hpp index e4f69aa6a..5be78f029 100644 --- a/node/MAC.hpp +++ b/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]); } /** diff --git a/node/Network.hpp b/node/Network.hpp index cab41411f..a24f161c9 100644 --- a/node/Network.hpp +++ b/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) }; /** diff --git a/node/Node.cpp b/node/Node.cpp index 62a4d9aee..9fe7a3cb2 100644 --- a/node/Node.cpp +++ b/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 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 > snp(_r->topology->supernodePeers()); + for(std::vector< SharedPtr >::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 &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,std::vector > > data; + inline void operator()(Topology &t,const SharedPtr &p) { data.push_back(std::pair< SharedPtr,std::vector >(p,p->paths())); } +}; +struct SortPeersAndPathsInAscendingAddressOrder +{ + inline bool operator()(const std::pair< SharedPtr,std::vector > &a,const std::pair< SharedPtr,std::vector > &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,std::vector > >::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,std::vector > >::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::iterator pi(p->second.begin());pi!=p->second.end();++pi) { + ZT1_Node_PhysicalPath *path = &(prec->paths[prec->numPaths++]); + path->type = static_casttype)>(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,const SharedPtr &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_caststatus)>(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(_r->nc->network(nwid)); + if (!network) + return (ZT1_Node_Network *)0; + SharedPtr nconf(network->config2()); + std::set 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::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 > networks(_r->nc->networks()); + std::vector< SharedPtr > nconfs(networks.size()); + std::vector< std::set > ipsv(networks.size()); + + unsigned long returnBufSize = sizeof(ZT1_Node_NetworkList); + for(unsigned long i=0;iconfig2(); + 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;inetworks[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::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: diff --git a/node/Node.hpp b/node/Node.hpp index 179afbea5..4e4a8d910 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -28,8 +28,9 @@ #ifndef ZT_NODE_HPP #define ZT_NODE_HPP -#include -#include +#include + +#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 splitLine(const char *line); - static inline std::vector 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,58 +112,95 @@ 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 */ - bool leave(const char *nwid) - throw(); - - void listPeers() - throw(); - - void listNetworks() + void leave(uint64_t nwid) throw(); /** - * Check for software updates (if enabled) + * Get the status of this node + * + * @param status Buffer to fill with status information + */ + void status(ZT1_Node_Status *status) + throw(); + + /** + * @return List of known peers or NULL on failure + */ + ZT1_Node_PeerList *listPeers() + throw(); + + /** + * @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(); + + /** + * @return List of networks we've joined or NULL on failure + */ + ZT1_Node_NetworkList *listNetworks() + throw(); + + /** + * 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/) */ bool updateCheck() throw(); - /** - * @return Description of last non-fatal error or empty string if none - */ - const char *getLastError() - throw(); - static const char *versionString() throw(); static unsigned int versionMajor() throw(); static unsigned int versionMinor() throw(); diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 732d41a32..996163d0c 100644 --- a/node/NodeConfig.cpp +++ b/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() { diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index b9bc8f5f4..d374eee6d 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -36,8 +36,6 @@ #include #include -#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 join(uint64_t nwid) + { + Mutex::Lock _l(_networks_m); + SharedPtr &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 >::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; diff --git a/node/Peer.hpp b/node/Peer.hpp index 05005a301..5204cb84a 100644 --- a/node/Peer.hpp +++ b/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 diff --git a/node/Topology.hpp b/node/Topology.hpp index 6349fe877..6bbbc2974 100644 --- a/node/Topology.hpp +++ b/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 * diff --git a/objects.mk b/objects.mk index 1dd619152..24231c684 100644 --- a/objects.mk +++ b/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 \