From 2e85cf18c19ce86363de636ff30827fe232aa80b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 17 Jul 2013 14:39:34 -0400 Subject: Cleanup and build fixes. --- node/Node.cpp | 96 +++++++++++++++++------------------------------------------ 1 file changed, 27 insertions(+), 69 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 3b18d7ba..5dbc5bb6 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -37,15 +37,15 @@ #include #include -#ifndef _WIN32 +#ifdef _WIN32 +#include +#else #include #include #include #include #endif -#include - #include "Condition.hpp" #include "Node.hpp" #include "Topology.hpp" @@ -78,7 +78,6 @@ struct _NodeImpl Node::ReasonForTermination reasonForTermination; volatile bool started; volatile bool running; - volatile bool updateStatusNow; volatile bool terminateNow; // Helper used to rapidly terminate from run() @@ -94,20 +93,17 @@ struct _NodeImpl } }; -Node::Node(const char *hp,const char *urlPrefix,const char *configAuthorityIdentity) +Node::Node(const char *hp) throw() : _impl(new _NodeImpl) { _NodeImpl *impl = (_NodeImpl *)_impl; impl->renv.homePath = hp; - impl->renv.autoconfUrlPrefix = urlPrefix; - impl->renv.configAuthorityIdentityStr = configAuthorityIdentity; impl->reasonForTermination = Node::NODE_RUNNING; impl->started = false; impl->running = false; - impl->updateStatusNow = false; impl->terminateNow = false; } @@ -155,11 +151,9 @@ Node::ReasonForTermination Node::run() TRACE("initializing..."); + // Create non-crypto PRNG right away in case other code in init wants to use it _r->prng = new CMWC4096(); - if (!_r->configAuthority.fromString(_r->configAuthorityIdentityStr)) - return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"configuration authority identity is not valid"); - bool gotId = false; std::string identitySecretPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.secret"); std::string identityPublicPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.public"); @@ -188,37 +182,35 @@ Node::ReasonForTermination Node::run() } Utils::lockDownFile(identitySecretPath.c_str(),false); - // Generate ownership verification secret, which can be presented to - // a controlling web site (like ours) to prove ownership of a node and - // permit its configuration to be centrally modified. When ZeroTier One - // requests its config it sends a hash of this secret, and so the - // config server can verify this hash to determine if the secret the - // user presents is correct. - std::string ovsPath(_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine"); - if (((Utils::now() - Utils::getLastModified(ovsPath.c_str())) >= ZT_OVS_GENERATE_NEW_IF_OLDER_THAN)||(!Utils::readFile(ovsPath.c_str(),_r->ownershipVerificationSecret))) { - _r->ownershipVerificationSecret = ""; - unsigned int securern = 0; + // Clean up some obsolete files if present -- this will be removed later + unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "status").c_str()); + unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine").c_str()); + + // Load or generate config authentication secret + std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret"); + std::string configAuthToken; + if (!Utils::readFile(configAuthTokenPath.c_str(),configAuthToken)) { + configAuthToken = ""; + unsigned int sr = 0; for(unsigned int i=0;i<24;++i) { - Utils::getSecureRandom(&securern,sizeof(securern)); - _r->ownershipVerificationSecret.push_back("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[securern % 62]); + Utils::getSecureRandom(&sr,sizeof(sr)); + configAuthToken.push_back("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[sr % 62]); } - _r->ownershipVerificationSecret.append(ZT_EOL_S); - if (!Utils::writeFile(ovsPath.c_str(),_r->ownershipVerificationSecret)) - return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write 'thisdeviceismine' (home path not writable?)"); + if (!Utils::writeFile(configAuthTokenPath.c_str(),configAuthToken)) + return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write authtoken.secret (home path not writable?)"); } - Utils::lockDownFile(ovsPath.c_str(),false); - _r->ownershipVerificationSecret = Utils::trim(_r->ownershipVerificationSecret); // trim off CR file is saved with - unsigned char ovsDig[32]; - SHA256_CTX sha; - SHA256_Init(&sha); - SHA256_Update(&sha,_r->ownershipVerificationSecret.data(),_r->ownershipVerificationSecret.length()); - SHA256_Final(ovsDig,&sha); - _r->ownershipVerificationSecretHash = Utils::base64Encode(ovsDig,32); + Utils::lockDownFile(configAuthTokenPath.c_str(),false); // Create the core objects in RuntimeEnvironment: node config, demarcation // point, switch, network topology database, and system environment // watcher. - _r->nc = new NodeConfig(_r,_r->autoconfUrlPrefix + _r->identity.address().toString()); + try { + _r->nc = new NodeConfig(_r,configAuthToken.c_str()); + } catch ( ... ) { + // An exception here currently means that another instance of ZeroTier + // One is running. + return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"another instance of ZeroTier One appears to be running, or local control UDP port cannot be bound"); + } _r->demarc = new Demarc(_r); _r->multicaster = new Multicaster(); _r->sw = new Switch(_r); @@ -248,8 +240,6 @@ Node::ReasonForTermination Node::run() } try { - std::string statusPath(_r->homePath + ZT_PATH_SEPARATOR_S + "status"); - uint64_t lastPingCheck = 0; uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately uint64_t lastNetworkFingerprintCheck = 0; @@ -257,7 +247,6 @@ Node::ReasonForTermination Node::run() uint64_t networkConfigurationFingerprint = _r->sysEnv->getNetworkConfigurationFingerprint(); uint64_t lastMulticastCheck = 0; uint64_t lastMulticastAnnounceAll = 0; - uint64_t lastStatusUpdate = 0; long lastDelayDelta = 0; LOG("%s starting version %s",_r->identity.address().toString().c_str(),versionString()); @@ -292,16 +281,6 @@ Node::ReasonForTermination Node::run() } } - if ((now - lastAutoconfigureCheck) >= ZT_AUTOCONFIGURE_CHECK_DELAY) { - // It seems odd to only do this simple check every so often, but the purpose is to - // delay between calls to refreshConfiguration() enough that the previous attempt - // has time to either succeed or fail. Otherwise we'll block the whole loop, since - // config update is guarded by a Mutex. - lastAutoconfigureCheck = now; - if ((now - _r->nc->lastAutoconfigure()) >= ZT_AUTOCONFIGURE_INTERVAL) - _r->nc->refreshConfiguration(); // happens in background - } - // Periodically check for changes in our local multicast subscriptions and broadcast // those changes to peers. if ((now - lastMulticastCheck) >= ZT_MULTICAST_LOCAL_POLL_PERIOD) { @@ -389,20 +368,6 @@ Node::ReasonForTermination Node::run() _r->topology->clean(); // happens in background } - if (((now - lastStatusUpdate) >= ZT_STATUS_OUTPUT_PERIOD)||(impl->updateStatusNow)) { - lastStatusUpdate = now; - impl->updateStatusNow = false; - FILE *statusf = ::fopen(statusPath.c_str(),"w"); - if (statusf) { - try { - _r->topology->eachPeer(Topology::DumpPeerStatistics(statusf)); - } catch ( ... ) { - TRACE("unexpected exception updating status dump"); - } - ::fclose(statusf); - } - } - try { unsigned long delay = std::min((unsigned long)ZT_MIN_SERVICE_LOOP_INTERVAL,_r->sw->doTimerTasks()); uint64_t start = Utils::now(); @@ -436,13 +401,6 @@ void Node::terminate() ((_NodeImpl *)_impl)->renv.mainLoopWaitCondition.signal(); } -void Node::updateStatusNow() - throw() -{ - ((_NodeImpl *)_impl)->updateStatusNow = true; - ((_NodeImpl *)_impl)->renv.mainLoopWaitCondition.signal(); -} - class _VersionStringMaker { public: -- cgit v1.2.3 From a677597b44ff94bf1f642f7ef81f926e09439ffd Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 18 Jul 2013 11:43:46 -0400 Subject: Better encode/decode code for control bus. --- node/Node.cpp | 65 ++++++++++++++++++++++++++ node/Node.hpp | 43 ++++++++++++++++++ node/NodeConfig.cpp | 129 +++++++++++++++++++++++++++++----------------------- node/NodeConfig.hpp | 43 +++++++++++++++--- 4 files changed, 217 insertions(+), 63 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 5dbc5bb6..57bb8abc 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -57,6 +57,8 @@ #include "Constants.hpp" #include "InetAddress.hpp" #include "Pack.hpp" +#include "Salsa20.hpp" +#include "HMAC.hpp" #include "RuntimeEnvironment.hpp" #include "NodeConfig.hpp" #include "Defaults.hpp" @@ -71,6 +73,69 @@ namespace ZeroTier { +struct _LocalClientImpl +{ + unsigned char key[32]; + UdpSocket *sock; + void (*resultHandler)(void *,unsigned long,const char *); + void *arg; + Mutex inUseLock; +}; + +static void _CBlocalClientHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) +{ + _LocalClientImpl *impl = (_LocalClientImpl *)arg; + Mutex::Lock _l(impl->inUseLock); +} + +Node::LocalClient::LocalClient(const char *authToken,void (*resultHandler)(void *,unsigned long,const char *),void *arg) + throw() : + _impl((void *)0) +{ + _LocalClientImpl *impl = new _LocalClientImpl; + + UdpSocket *sock = (UdpSocket *)0; + for(unsigned int i=0;i<5000;++i) { + try { + sock = new UdpSocket(true,32768 + (rand() % 20000),false,&_CBlocalClientHandler,impl); + break; + } catch ( ... ) { + sock = (UdpSocket *)0; + } + } + + // If socket fails to bind, there's a big problem like missing IPv4 stack + if (sock) { + SHA256_CTX sha; + SHA256_Init(&sha); + SHA256_Update(&sha,authToken,strlen(authToken)); + SHA256_Final(impl->key,&sha); + + impl->sock = sock; + impl->resultHandler = resultHandler; + impl->arg = arg; + _impl = impl; + } else delete impl; +} + +Node::LocalClient::~LocalClient() +{ + if (_impl) { + ((_LocalClientImpl *)_impl)->inUseLock.lock(); + delete ((_LocalClientImpl *)_impl)->sock; + ((_LocalClientImpl *)_impl)->inUseLock.unlock(); + delete ((_LocalClientImpl *)_impl); + } +} + +unsigned long Node::LocalClient::send(const char *command) + throw() +{ + uint32_t convId = (uint32_t)rand(); + + return convId; +} + struct _NodeImpl { RuntimeEnvironment renv; diff --git a/node/Node.hpp b/node/Node.hpp index f4e2d423..bddced58 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -40,6 +40,45 @@ namespace ZeroTier { class Node { public: + /** + * Client for controlling a local ZeroTier One node + */ + class LocalClient + { + public: + /** + * Create a new node config client + * + * The result handler will be called from a different thread. Its + * arguments are the request ID generated by send() and each line + * of output. It may be called more than once per request result + * if the command generates more than one line of output. + * + * @param authToken Authentication token + * @param resultHandler Function to call when commands provide results + */ + LocalClient(const char *authToken,void (*resultHandler)(void *,unsigned long,const char *),void *arg) + throw(); + + ~LocalClient(); + + /** + * Send a command to the local node + * + * @param command + * @return Request ID that will be provided to result handler when/if results are sent back + */ + unsigned long send(const char *command) + throw(); + + private: + // LocalClient is not copyable + LocalClient(const LocalClient&); + const LocalClient& operator=(const LocalClient&); + + void *_impl; + }; + /** * Returned by node main if/when it terminates */ @@ -108,6 +147,10 @@ public: static unsigned int versionRevision() throw(); private: + // Nodes are not copyable + Node(const Node&); + const Node& operator=(const Node&); + void *const _impl; // private implementation }; diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 0daa9ebe..fca53942 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -52,19 +52,12 @@ namespace ZeroTier { NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken) throw(std::runtime_error) : _r(renv), - _authToken(authToken), _controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this) { SHA256_CTX sha; - - SHA256_Init(&sha); - SHA256_Update(&sha,_authToken.data(),_authToken.length()); - SHA256_Final(_keys,&sha); // first 32 bytes of keys[]: Salsa20 key - SHA256_Init(&sha); - SHA256_Update(&sha,_keys,32); - SHA256_Update(&sha,_authToken.data(),_authToken.length()); - SHA256_Final(_keys + 32,&sha); // second 32 bytes of keys[]: HMAC key + SHA256_Update(&sha,authToken,strlen(authToken)); + SHA256_Final(_controlSocketKey,&sha); } NodeConfig::~NodeConfig() @@ -146,64 +139,86 @@ std::vector NodeConfig::execute(const char *command) return r; } -void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) +std::vector< Buffer > NodeConfig::encodeControlMessage(const void *key,unsigned long conversationId,const std::vector &payload) + throw(std::out_of_range) { - char hmacKey[32]; char hmac[32]; - char buf[131072]; - NodeConfig *nc = (NodeConfig *)arg; - const RuntimeEnvironment *_r = nc->_r; + char keytmp[32]; + std::vector< Buffer > packets; + Buffer packet; - try { - // Minimum length - if (len < 28) - return; - if (len >= sizeof(buf)) // only up to len - 28 bytes are used on receive/decrypt - return; - - // Compare first 16 bytes of HMAC, which is after IV in packet - memcpy(hmacKey,nc->_keys + 32,32); - *((uint64_t *)hmacKey) ^= *((const uint64_t *)data); // include IV in HMAC - HMAC::sha256(hmacKey,32,((const unsigned char *)data) + 28,len - 28,hmac); - if (memcmp(hmac,((const unsigned char *)data) + 8,16)) - return; - - // Decrypt payload if we passed HMAC - Salsa20 s20(nc->_keys,256,data); // first 64 bits of data are IV - s20.decrypt(((const unsigned char *)data) + 28,buf,len - 28); - - // Null-terminate string for execute() - buf[len - 28] = (char)0; - - // Execute command - std::vector r(nc->execute(buf)); - - // Result packet contains a series of null-terminated results - unsigned int resultLen = 28; - for(std::vector::iterator i(r.begin());i!=r.end();++i) { - if ((resultLen + i->length() + 1) >= sizeof(buf)) - return; // result too long - memcpy(buf + resultLen,i->c_str(),i->length() + 1); - resultLen += i->length() + 1; + packet.setSize(16); // HMAC and IV + packet.append((uint32_t)(conversationId & 0xffffffff)); + for(unsigned int i=0;i= payload.size())||((packet.size() + payload[i + 1].length() + 1) >= packet.capacity())) { + Utils::getSecureRandom(packet.field(8,8),8); + + memcpy(keytmp,key,32); + for(unsigned int i=0;i<32;++i) + keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20 + HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac); + memcpy(packet.field(0,8),hmac,8); + + Salsa20 s20(key,256,packet.field(8,8)); + s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16); + + packets.push_back(packet); + + packet.setSize(16); // HMAC and IV + packet.append((uint32_t)(conversationId & 0xffffffff)); } + } - // Generate result packet IV - Utils::getSecureRandom(buf,8); + return packets; +} - // Generate result packet HMAC - memcpy(hmacKey,nc->_keys + 32,32); - *((uint64_t *)hmacKey) ^= *((const uint64_t *)buf); // include IV in HMAC - HMAC::sha256(hmacKey,32,((const unsigned char *)buf) + 28,resultLen - 28,hmac); - memcpy(buf + 8,hmac,16); +bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector &payload) +{ + char hmac[32]; + char keytmp[32]; - // Copy arbitrary tag from original packet - memcpy(buf + 24,((const unsigned char *)data) + 24,4); + try { + if (len < 20) + return false; + + Buffer packet(data,len); + + memcpy(keytmp,key,32); + for(unsigned int i=0;i<32;++i) + keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20 + HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac); + if (memcmp(packet.field(0,8),hmac,8)) + return false; + + Salsa20 s20(key,256,packet.field(8,8)); + s20.decrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16); + + conversationId = packet.at(16); + + const char *pl = ((const char *)packet.data()) + 20; + unsigned int pll = packet.size() - 20; + payload.clear(); + for(unsigned int i=0;i i) { + payload.push_back(std::string(pl + i,eos - i)); + i = eos + 1; + } else break; + } - // Send encrypted result back to requester - sock->send(remoteAddr,buf,resultLen,-1); + return true; } catch ( ... ) { - TRACE("unexpected exception parsing control packet or generating response"); + return false; } } +void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) +{ +} + } // namespace ZeroTier diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index 66c99448..d284062d 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -40,17 +40,25 @@ #include "Utils.hpp" #include "Http.hpp" #include "UdpSocket.hpp" +#include "Buffer.hpp" namespace ZeroTier { class RuntimeEnvironment; +/** + * Maximum size of a packet for node configuration + */ +#define ZT_NODECONFIG_MAX_PACKET_SIZE 4096 + /** * Node configuration endpoint * * Packet format for local UDP configuration packets: - * [8] random initialization vector * [16] first 16 bytes of HMAC-SHA-256 of payload + * [ -- begin HMAC'ed envelope -- ] + * [8] random initialization vector + * [ -- begin cryptographic envelope -- ] * [4] arbitrary tag, echoed in response * [...] payload * @@ -58,8 +66,6 @@ class RuntimeEnvironment; * responses, the payload consists of one or more response lines delimited * by NULL (0) characters. The tag field is replicated in the result * packet. - * - * TODO: further document use of keys, encryption... */ class NodeConfig { @@ -132,14 +138,39 @@ public: */ std::vector execute(const char *command); + /** + * Armor payload for control bus + * + * Note that no single element of payload can be longer than the max packet + * size. If this occurs out_of_range is thrown. + * + * @param key 32 byte key + * @param conversationId 32-bit conversation ID (bits beyond 32 are ignored) + * @param payload One or more strings to encode in packet + * @return One or more transport armored packets (if payload too big) + * @throws std::out_of_range An element of payload is too big + */ + static std::vector< Buffer > encodeControlMessage(const void *key,unsigned long conversationId,const std::vector &payload) + throw(std::out_of_range); + + /** + * Decode a packet from the control bus + * + * @param key 32 byte key + * @param data Packet data + * @param len Packet length + * @param conversationId Result parameter filled with conversation ID on success + * @param payload Result parameter filled with payload on success + * @return True on success, false on invalid packet or packet that failed authentication + */ + static bool decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector &payload); + private: static void _CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len); const RuntimeEnvironment *_r; - const std::string _authToken; - unsigned char _keys[64]; // Salsa20 key, HMAC key - + unsigned char _controlSocketKey[32]; UdpSocket _controlSocket; std::map< uint64_t,SharedPtr > _networks; Mutex _networks_m; -- cgit v1.2.3 From 5f4eb1ebc60abdd762bc77ef5b1120fe528ccc8f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 18 Jul 2013 16:35:52 -0400 Subject: Command line interface. --- Makefile.mac | 6 ++- cli.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++ node/Node.cpp | 38 +++++++++++++++- node/Node.hpp | 11 +++-- node/NodeConfig.cpp | 14 ++++++ 5 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 cli.cpp (limited to 'node/Node.cpp') diff --git a/Makefile.mac b/Makefile.mac index 89203243..bace24b2 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -20,12 +20,16 @@ LIBS=ext/bin/libcrypto/mac-x86_combined/libcrypto.a include objects.mk -all: one launcher mac-tap +all: one cli launcher mac-tap one: $(OBJS) $(CXX) $(CXXFLAGS) -o zerotier-one main.cpp $(OBJS) $(LIBS) $(STRIP) zerotier-one +cli: $(OBJS) + $(CXX) $(CXXFLAGS) -o zerotier-cli cli.cpp $(OBJS) $(LIBS) + $(STRIP) zerotier-cli + selftest: $(OBJS) $(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.cpp $(OBJS) $(LIBS) $(STRIP) zerotier-selftest diff --git a/cli.cpp b/cli.cpp new file mode 100644 index 00000000..a72a0890 --- /dev/null +++ b/cli.cpp @@ -0,0 +1,124 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 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 +#include + +#ifndef __WINDOWS__ +#include +#endif + +#include "node/Node.hpp" +#include "node/Constants.hpp" +#include "node/Utils.hpp" +#include "node/Thread.hpp" + +using namespace ZeroTier; + +static void printHelp(FILE *out,const char *exename) +{ + fprintf(out,"Usage: %s [-switches] "ZT_EOL_S,exename); + fprintf(out,ZT_EOL_S); + fprintf(out,"Switches:"ZT_EOL_S); + fprintf(out," -t - Specify token on command line"ZT_EOL_S); + fprintf(out," -T - Read token from file"ZT_EOL_S); + fprintf(out,ZT_EOL_S); + fprintf(out,"Use the 'help' command to get help from ZeroTier One itself."ZT_EOL_S); +} + +static volatile uint64_t lastResultTime = 0ULL; +static volatile unsigned int numResults = 0; + +static void resultHandler(void *arg,unsigned long id,const char *line) +{ + lastResultTime = Utils::now(); + ++numResults; + fprintf(stdout,"%s"ZT_EOL_S,line); +} + +int main(int argc,char **argv) +{ + if (argc <= 1) { + printHelp(stdout,argv[0]); + return -1; + } + + std::string authToken; + + for(int i=1;iresultHandler) + return; // sanity check Mutex::Lock _l(impl->inUseLock); + + try { + unsigned long convId = 0; + std::vector results; + if (!NodeConfig::decodeControlMessagePacket(impl->key,data,len,convId,results)) + return; + for(std::vector::iterator r(results.begin());r!=results.end();++r) + impl->resultHandler(impl->arg,convId,r->c_str()); + } catch ( ... ) {} } Node::LocalClient::LocalClient(const char *authToken,void (*resultHandler)(void *,unsigned long,const char *),void *arg) @@ -114,6 +128,8 @@ Node::LocalClient::LocalClient(const char *authToken,void (*resultHandler)(void impl->sock = sock; impl->resultHandler = resultHandler; impl->arg = arg; + impl->localDestAddr = InetAddress::LO4; + impl->localDestAddr.setPort(ZT_CONTROL_UDP_PORT); _impl = impl; } else delete impl; } @@ -131,9 +147,27 @@ Node::LocalClient::~LocalClient() unsigned long Node::LocalClient::send(const char *command) throw() { - uint32_t convId = (uint32_t)rand(); + if (!_impl) + return 0; + _LocalClientImpl *impl = (_LocalClientImpl *)_impl; + Mutex::Lock _l(impl->inUseLock); + + try { + uint32_t convId = (uint32_t)rand(); + if (!convId) + convId = 1; - return convId; + std::vector tmp; + tmp.push_back(std::string(command)); + std::vector< Buffer > packets(NodeConfig::encodeControlMessage(impl->key,convId,tmp)); + + for(std::vector< Buffer >::iterator p(packets.begin());p!=packets.end();++p) + impl->sock->send(impl->localDestAddr,p->data(),p->size(),-1); + + return convId; + } catch ( ... ) { + return 0; + } } struct _NodeImpl diff --git a/node/Node.hpp b/node/Node.hpp index bddced58..b716b556 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -49,11 +49,6 @@ public: /** * Create a new node config client * - * The result handler will be called from a different thread. Its - * arguments are the request ID generated by send() and each line - * of output. It may be called more than once per request result - * if the command generates more than one line of output. - * * @param authToken Authentication token * @param resultHandler Function to call when commands provide results */ @@ -65,8 +60,12 @@ public: /** * 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 Request ID that will be provided to result handler when/if results are sent back + * @return Conversation ID that will be provided to result handler when/if results are sent back */ unsigned long send(const char *command) throw(); diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 381bbd62..21ed5188 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -218,6 +218,20 @@ bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,uns void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len) { + NodeConfig *nc = (NodeConfig *)arg; + try { + unsigned long convId = 0; + std::vector commands; + + if (!decodeControlMessagePacket(nc->_controlSocketKey,data,len,convId,commands)) + return; + + for(std::vector::iterator c(commands.begin());c!=commands.end();++c) { + std::vector< Buffer > resultPackets(encodeControlMessage(nc->_controlSocketKey,convId,nc->execute(c->c_str()))); + for(std::vector< Buffer >::iterator p(resultPackets.begin());p!=resultPackets.end();++p) + sock->send(remoteAddr,p->data(),p->size(),-1); + } + } catch ( ... ) {} } } // namespace ZeroTier -- cgit v1.2.3 From 0c7f8e247cc1469e7f8206693e4fe4909f8e0a38 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 23 Jul 2013 10:23:55 -0700 Subject: Add amSupernode to make code clearer in the check-if-self-is-supernode case. --- node/Node.cpp | 8 +++----- node/PacketDecoder.cpp | 17 +++++++++++++---- node/Topology.cpp | 7 ++++++- node/Topology.hpp | 8 ++++++++ 4 files changed, 30 insertions(+), 10 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index f8de7908..08b08fc1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -415,11 +415,9 @@ Node::ReasonForTermination Node::run() if ((now - lastPingCheck) >= ZT_PING_CHECK_DELAY) { lastPingCheck = now; try { - if (_r->topology->isSupernode(_r->identity.address())) { - // The only difference in how supernodes behave is here: they only - // actively ping each other and only passively listen for pings - // from anyone else. They also don't send firewall openers, since - // they're never firewalled. + if (_r->topology->amSupernode()) { + // Supernodes do not ping anyone but each other. They also don't + // send firewall openers, since they aren't ever firewalled. std::vector< SharedPtr > sns(_r->topology->supernodePeers()); for(std::vector< SharedPtr >::const_iterator p(sns.begin());p!=sns.end();++p) { if ((now - (*p)->lastDirectSend()) > ZT_PEER_DIRECT_PING_DELAY) diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index ba1a9001..810e30a7 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -450,19 +450,28 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared // Technically should not happen, since the original submitter is // excluded from consideration as a propagation recipient. TRACE("dropped boomerang MULTICAST_FRAME received from %s(%s)",source().toString().c_str(),_remoteAddress.toString().c_str()); - } else if ((!isDuplicate)||(_r->topology->isSupernode(_r->identity.address()))) { + } else if ((!isDuplicate)||(_r->topology->amSupernode())) { + // // If I am a supernode, I will repeatedly propagate duplicates. That's // because supernodes are used to bridge sparse multicast groups. Non- // supernodes will ignore duplicates completely. + // + // TODO: supernodes should keep a local bloom filter too and OR it with + // the bloom from the packet in order to pick different recipients each + // time a multicast returns to them for repropagation. + // + SharedPtr originalSubmitter(_r->topology->getPeer(originalSubmitterAddress)); if (!originalSubmitter) { TRACE("requesting WHOIS on original multicast frame submitter %s",originalSubmitterAddress.toString().c_str()); _r->sw->requestWhois(originalSubmitterAddress); _step = DECODE_STEP_WAITING_FOR_ORIGINAL_SUBMITTER_LOOKUP; - return false; + return false; // try again if/when we get OK(WHOIS) } else if (Multicaster::verifyMulticastPacket(originalSubmitter->identity(),network->id(),fromMac,mg,etherType,dataAndSignature,datalen,dataAndSignature + datalen,signaturelen)) { _r->multicaster->addToDedupHistory(mccrc,now); + // Even if we are a supernode, we still don't repeatedly inject + // duplicates into our own tap. if (!isDuplicate) network->tap().put(fromMac,mg.mac(),etherType,dataAndSignature,datalen); @@ -494,7 +503,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared compress(); for(unsigned int i=0;i %s",originalSubmitterAddress.toString().c_str(),upstream.toString().c_str(),propPeers[i]->address().toString().c_str()); + //TRACE("propagating multicast from original node %s: %s -> %s",originalSubmitterAddress.toString().c_str(),upstream.toString().c_str(),propPeers[i]->address().toString().c_str()); // Re-use this packet to re-send multicast frame to everyone // downstream from us. newInitializationVector(); @@ -504,7 +513,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } else { - TRACE("terminating MULTICAST_FRAME propagation from %s(%s): max depth reached",source().toString().c_str(),_remoteAddress.toString().c_str()); + //TRACE("terminating MULTICAST_FRAME propagation from %s(%s): max depth reached",source().toString().c_str(),_remoteAddress.toString().c_str()); } } else { LOG("rejected MULTICAST_FRAME from %s(%s) due to failed signature check (claims original sender %s)",source().toString().c_str(),_remoteAddress.toString().c_str(),originalSubmitterAddress.toString().c_str()); diff --git a/node/Topology.cpp b/node/Topology.cpp index e627e767..43c5303e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -39,7 +39,8 @@ namespace ZeroTier { Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath) throw(std::runtime_error) : Thread(), - _r(renv) + _r(renv), + _amSupernode(false) { if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWCREAT,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) { if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) @@ -77,9 +78,11 @@ Topology::~Topology() void Topology::setSupernodes(const std::map< Identity,std::vector > &sn) { Mutex::Lock _l(_supernodes_m); + _supernodes = sn; _supernodeAddresses.clear(); _supernodePeers.clear(); + for(std::map< Identity,std::vector >::const_iterator i(sn.begin());i!=sn.end();++i) { if (i->first != _r->identity) { SharedPtr p(getPeer(i->first.address())); @@ -93,6 +96,8 @@ void Topology::setSupernodes(const std::map< Identity,std::vector > } _supernodeAddresses.insert(i->first.address()); } + + _amSupernode = (_supernodes.find(_r->identity) != _supernodes.end()); } void Topology::addPeer(const SharedPtr &candidate,void (*callback)(void *,const SharedPtr &,Topology::PeerVerifyResult),void *arg) diff --git a/node/Topology.hpp b/node/Topology.hpp index 2a977aa8..104ad344 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -162,6 +162,11 @@ public: return (_supernodeAddresses.count(zta) > 0); } + /** + * @return True if this node's identity is in the supernode set + */ + inline bool amSupernode() const { return _amSupernode; } + /** * Clean and flush database now (runs in the background) */ @@ -305,6 +310,9 @@ private: std::vector< SharedPtr > _supernodePeers; Mutex _supernodes_m; + // Set to true if my identity is in _supernodes + volatile bool _amSupernode; + KISSDB _dbm; Mutex _dbm_m; }; -- cgit v1.2.3 From 57d8730f1b3da3f9fe23a9fa02c8557b80bf6716 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 25 Jul 2013 17:53:57 -0400 Subject: Wire up RPC plugin loading to Node. --- node/Node.cpp | 22 ++++++++++++++++++++++ node/RPC.cpp | 22 ++++++++++++++++++++++ node/RPC.hpp | 12 ++++++++++++ node/RuntimeEnvironment.hpp | 9 ++++++++- node/Utils.cpp | 29 +++++++++++++++++++++++++++-- node/Utils.hpp | 11 +++++++++++ 6 files changed, 102 insertions(+), 3 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 08b08fc1..b5c23ed9 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -68,6 +68,7 @@ #include "Mutex.hpp" #include "Multicaster.hpp" #include "CMWC4096.hpp" +#include "RPC.hpp" #include "../version.h" @@ -210,6 +211,7 @@ Node::~Node() { _NodeImpl *impl = (_NodeImpl *)_impl; + delete impl->renv.rpc; delete impl->renv.sysEnv; delete impl->renv.topology; delete impl->renv.sw; @@ -315,6 +317,7 @@ Node::ReasonForTermination Node::run() _r->sw = new Switch(_r); _r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str()); _r->sysEnv = new SysEnv(_r); + _r->rpc = new RPC(_r); // TODO: make configurable bool boundPort = false; @@ -338,6 +341,25 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization"); } + try { + std::map pluginsd(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d").c_str())); + for(std::map::iterator ppath(pluginsd.begin());ppath!=pluginsd.end();++ppath) { + if (!ppath->second) { + try { + std::string funcName(ppath->first.substr(0,ppath->first.rfind('.'))); + LOG("loading plugins.d/%s as RPC function %s",ppath->first.c_str(),funcName.c_str()); + _r->rpc->loadLocal(funcName.c_str(),(_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d" + ZT_PATH_SEPARATOR_S + ppath->first).c_str()); + } catch (std::exception &exc) { + LOG("failed to load plugin plugins.d/%s: %s",ppath->first.c_str(),exc.what()); + } catch ( ... ) { + LOG("failed to load plugin plugins.d/%s: (unknown exception)",ppath->first.c_str()); + } + } + } + } catch ( ... ) { + TRACE("unknown exception attempting to enumerate and load plugins"); + } + try { uint64_t lastPingCheck = 0; uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately diff --git a/node/RPC.cpp b/node/RPC.cpp index e6591d7d..5a9cd719 100644 --- a/node/RPC.cpp +++ b/node/RPC.cpp @@ -37,6 +37,8 @@ namespace ZeroTier { +#ifndef __WINDOWS__ + RPC::LocalService::LocalService(const char *dllPath) throw(std::invalid_argument) : _handle((void *)0), @@ -111,6 +113,8 @@ std::pair< int,std::vector > RPC::LocalService::operator()(const st return std::pair< int,std::vector >(rcount,results); } +#endif // __WINDOWS__ + RPC::RPC(const RuntimeEnvironment *renv) : _r(renv) { @@ -123,17 +127,35 @@ RPC::~RPC() co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_CANCELLED,std::vector()); } +#ifndef __WINDOWS__ for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) delete s->second; +#endif } std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) { +#ifdef __WINDOWS__ + return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); +#else Mutex::Lock _l(_rpcServices_m); std::map::iterator s(_rpcServices.find(name)); if (s == _rpcServices.end()) return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); return ((*(s->second))(args)); +#endif +} + +void RPC::loadLocal(const char *name,const char *path) + throw(std::invalid_argument) +{ +#ifdef __WINDOWS__ + throw std::invalid_argument("RPC plugins not supported on Windows (yet?)"); +#else + LocalService *s = new LocalService(path); + Mutex::Lock _l(_rpcServices_m); + _rpcServices[std::string(name)] = s; +#endif } uint64_t RPC::callRemote( diff --git a/node/RPC.hpp b/node/RPC.hpp index e31b5f6d..669bf5ae 100644 --- a/node/RPC.hpp +++ b/node/RPC.hpp @@ -139,6 +139,16 @@ public: */ std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); + /** + * Load a plugin + * + * @param name Name of RPC function + * @param path Path to plugin DLL + * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL + */ + void loadLocal(const char *name,const char *path) + throw(std::invalid_argument); + /** * Call a remote service * @@ -165,8 +175,10 @@ public: private: const RuntimeEnvironment *_r; +#ifndef __WINDOWS__ std::map _rpcServices; Mutex _rpcServices_m; +#endif struct RemoteCallOutstanding { diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index bc63543a..35c2de3f 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -42,6 +42,7 @@ class Topology; class SysEnv; class Multicaster; class CMWC4096; +class RPC; /** * Holds global state for an instance of ZeroTier::Node @@ -65,7 +66,9 @@ public: demarc((Demarc *)0), multicaster((Multicaster *)0), sw((Switch *)0), - topology((Topology *)0) + topology((Topology *)0), + sysEnv((SysEnv *)0), + rpc((RPC *)0) { } @@ -76,6 +79,9 @@ public: Identity identity; + // Order matters a bit here. These are constructed in this order + // and then deleted in the opposite order on Node exit. + Logger *log; // may be null CMWC4096 *prng; NodeConfig *nc; @@ -84,6 +90,7 @@ public: Switch *sw; Topology *topology; SysEnv *sysEnv; + RPC *rpc; }; } // namespace ZeroTier diff --git a/node/Utils.cpp b/node/Utils.cpp index 7b2a17af..7a4d51ba 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -29,14 +29,13 @@ #include #include #include -#include "Utils.hpp" -#include "Mutex.hpp" #if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) #include #include #include #include +#include #endif #ifdef _WIN32 @@ -46,6 +45,9 @@ #include #include +#include "Utils.hpp" +#include "Mutex.hpp" + namespace ZeroTier { const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; @@ -214,6 +216,29 @@ const char Utils::base64DecMap[128] = { static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; +std::map Utils::listDirectory(const char *path) +{ + struct dirent de; + struct dirent *dptr; + std::map r; + + DIR *d = opendir(path); + if (!d) + return r; + + dptr = (struct dirent *)0; + for(;;) { + if (readdir_r(d,&de,&dptr)) + break; + if (dptr) { + if ((!strcmp(dptr->d_name,"."))&&(!strcmp(dptr->d_name,".."))) + r[std::string(dptr->d_name)] = (dptr->d_type == DT_DIR); + } else break; + } + + return r; +} + std::string Utils::base64Encode(const void *data,unsigned int len) { if (!len) diff --git a/node/Utils.hpp b/node/Utils.hpp index b1917b27..fbabe3d7 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -38,6 +38,7 @@ #include #include #include +#include #include "../ext/lz4/lz4.h" #include "../ext/lz4/lz4hc.h" @@ -58,6 +59,16 @@ namespace ZeroTier { class Utils { public: + /** + * List a directory's contents + * + * @param path Path to list + * @param files Set to fill with files + * @param directories Set to fill with directories + * @return Map of entries and whether or not they are also directories (empty on failure) + */ + static std::map listDirectory(const char *path); + /** * @param data Data to convert to hex * @param len Length of data -- cgit v1.2.3 From b0a83093ce698390a4e2d266ba1c63ee241cd7ad Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 27 Jul 2013 13:36:27 -0400 Subject: Back out of RPC... blech. Have a better idea. --- node/Constants.hpp | 5 -- node/Network.hpp | 8 -- node/Node.cpp | 22 ----- node/Packet.cpp | 1 - node/Packet.hpp | 21 +---- node/PacketDecoder.cpp | 6 -- node/PacketDecoder.hpp | 1 - node/RPC.cpp | 212 -------------------------------------------- node/RPC.hpp | 196 ---------------------------------------- node/RuntimeEnvironment.hpp | 5 +- objects.mk | 1 - 11 files changed, 2 insertions(+), 476 deletions(-) delete mode 100644 node/RPC.cpp delete mode 100644 node/RPC.hpp (limited to 'node/Node.cpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index 9f856948..b8dc9ebf 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,9 +324,4 @@ error_no_ZT_ARCH_defined; */ #define ZT_RENDEZVOUS_NAT_T_DELAY 500 -/** - * Timeout for remote RPC calls - */ -#define ZT_RPC_TIMEOUT 10000 - #endif diff --git a/node/Network.hpp b/node/Network.hpp index 8429cf84..a95ae869 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -50,14 +50,6 @@ class NodeConfig; /** * Local membership to a network - * - * Networks configure themselves via RPC by accessing the function - * com.zerotier.one.Network.bootstrap at any supernode. This returns - * a series of key/value pairs that includes the IP address - * information for this node on the network and -- for closed - * networks -- a URL to retreive the network's membership list. - * A SHA-256 hash is also included to verify the return from this - * URL query. */ class Network : NonCopyable { diff --git a/node/Node.cpp b/node/Node.cpp index b5c23ed9..08b08fc1 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -68,7 +68,6 @@ #include "Mutex.hpp" #include "Multicaster.hpp" #include "CMWC4096.hpp" -#include "RPC.hpp" #include "../version.h" @@ -211,7 +210,6 @@ Node::~Node() { _NodeImpl *impl = (_NodeImpl *)_impl; - delete impl->renv.rpc; delete impl->renv.sysEnv; delete impl->renv.topology; delete impl->renv.sw; @@ -317,7 +315,6 @@ Node::ReasonForTermination Node::run() _r->sw = new Switch(_r); _r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str()); _r->sysEnv = new SysEnv(_r); - _r->rpc = new RPC(_r); // TODO: make configurable bool boundPort = false; @@ -341,25 +338,6 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization"); } - try { - std::map pluginsd(Utils::listDirectory((_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d").c_str())); - for(std::map::iterator ppath(pluginsd.begin());ppath!=pluginsd.end();++ppath) { - if (!ppath->second) { - try { - std::string funcName(ppath->first.substr(0,ppath->first.rfind('.'))); - LOG("loading plugins.d/%s as RPC function %s",ppath->first.c_str(),funcName.c_str()); - _r->rpc->loadLocal(funcName.c_str(),(_r->homePath + ZT_PATH_SEPARATOR_S + "plugins.d" + ZT_PATH_SEPARATOR_S + ppath->first).c_str()); - } catch (std::exception &exc) { - LOG("failed to load plugin plugins.d/%s: %s",ppath->first.c_str(),exc.what()); - } catch ( ... ) { - LOG("failed to load plugin plugins.d/%s: (unknown exception)",ppath->first.c_str()); - } - } - } - } catch ( ... ) { - TRACE("unknown exception attempting to enumerate and load plugins"); - } - try { uint64_t lastPingCheck = 0; uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately diff --git a/node/Packet.cpp b/node/Packet.cpp index bce80bf1..d12f396d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,7 +42,6 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; - case VERB_RPC: return "RPC"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 8b06d1a5..5ccfae45 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -463,26 +463,7 @@ public: * * No OK or ERROR is generated. */ - VERB_MULTICAST_FRAME = 9, - - /* Call a plugin via RPC: - * <[2] length of function name> - * <[...] function name> - * [<[2] length of argument>] - * [<[...] argument>] - * [... additional length/argument pairs ...] - * - * This generates ERROR_NOT_FOUND if the specified function was not - * found. Otherwise it generates an OK message. The OK message has - * the same format as the request, except arguments are replaced - * by results. - * - * This can be used to implement simple RPC. No retransmission or - * other guarantees are made, so it behaves like UDP-based RPC - * protocols. Plugins are typically run by supernodes and are used - * to implement network lookup and other services. - */ - VERB_RPC = 10 + VERB_MULTICAST_FRAME = 9 }; /** diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index e081fa8e..810e30a7 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -102,8 +102,6 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) return _doMULTICAST_LIKE(_r,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(_r,peer); - case Packet::VERB_RPC: - return _doRPC(_r,peer); default: // This might be something from a new or old version of the protocol. // Technically it passed HMAC so the packet is still valid, but we @@ -540,8 +538,4 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared return true; } -bool PacketDecoder::_doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer) -{ -} - } // namespace ZeroTier diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index 82bf9aee..e595d326 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -122,7 +122,6 @@ private: bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr &peer); - bool _doRPC(const RuntimeEnvironment *_r,const SharedPtr &peer); uint64_t _receiveTime; Demarc::Port _localPort; diff --git a/node/RPC.cpp b/node/RPC.cpp deleted file mode 100644 index 5a9cd719..00000000 --- a/node/RPC.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 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/ - */ - -#ifndef __WINDOWS__ -#include -#endif - -#include "Utils.hpp" -#include "RuntimeEnvironment.hpp" -#include "RPC.hpp" -#include "Switch.hpp" -#include "Topology.hpp" - -namespace ZeroTier { - -#ifndef __WINDOWS__ - -RPC::LocalService::LocalService(const char *dllPath) - throw(std::invalid_argument) : - _handle((void *)0), - _init((void *)0), - _do((void *)0), - _free((void *)0), - _destroy((void *)0) -{ - _handle = dlopen(dllPath,RTLD_LAZY|RTLD_LOCAL); - if (!_handle) - throw std::invalid_argument("Unable to load DLL: dlopen() failed"); - - _init = dlsym(_handle,"ZeroTierPluginInit"); - if (!_init) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginInit in DLL"); - } - _do = dlsym(_handle,"ZeroTierPluginDo"); - if (!_do) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDo in DLL"); - } - _free = dlsym(_handle,"ZeroTierPluginFree"); - if (!_free) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginFree in DLL"); - } - _destroy = dlsym(_handle,"ZeroTierPluginDestroy"); - if (!_destroy) { - dlclose(_handle); - throw std::invalid_argument("Unable to resolve symbol ZeroTierPluginDestroy in DLL"); - } - - if (((int (*)())_init)() < 0) { - dlclose(_handle); - throw std::invalid_argument("ZeroTierPluginInit() returned error"); - } -} - -RPC::LocalService::~LocalService() -{ - if (_handle) { - if (_destroy) - ((void (*)())_destroy)(); - dlclose(_handle); - } -} - -std::pair< int,std::vector > RPC::LocalService::operator()(const std::vector &args) -{ - unsigned int alengths[4096]; - const void *argptrs[4096]; - const unsigned int *rlengths = (const unsigned int *)0; - const void **resultptrs = (const void **)0; - std::vector results; - - if (args.size() > 4096) - throw std::runtime_error("args[] too long"); - - for(unsigned int i=0;i >(rcount,results); -} - -#endif // __WINDOWS__ - -RPC::RPC(const RuntimeEnvironment *renv) : - _r(renv) -{ -} - -RPC::~RPC() -{ - for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();++co) { - if (co->second.handler) - co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_CANCELLED,std::vector()); - } - -#ifndef __WINDOWS__ - for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) - delete s->second; -#endif -} - -std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) -{ -#ifdef __WINDOWS__ - return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); -#else - Mutex::Lock _l(_rpcServices_m); - std::map::iterator s(_rpcServices.find(name)); - if (s == _rpcServices.end()) - return std::pair< int,std::vector >(ZT_RPC_ERROR_NOT_FOUND,std::vector()); - return ((*(s->second))(args)); -#endif -} - -void RPC::loadLocal(const char *name,const char *path) - throw(std::invalid_argument) -{ -#ifdef __WINDOWS__ - throw std::invalid_argument("RPC plugins not supported on Windows (yet?)"); -#else - LocalService *s = new LocalService(path); - Mutex::Lock _l(_rpcServices_m); - _rpcServices[std::string(name)] = s; -#endif -} - -uint64_t RPC::callRemote( - const Address &peer, - const std::string &name, - const std::vector &args, - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), - void *arg) - throw(std::invalid_argument,std::out_of_range) -{ - Packet outp(peer,_r->identity.address(),Packet::VERB_RPC); - - if (name.length() > 0xffff) - throw std::invalid_argument("function name too long"); - outp.append((uint16_t)name.length()); - outp.append(name); - for(std::vector::const_iterator a(args.begin());a!=args.end();++a) { - if (a->length() > 0xffff) - throw std::invalid_argument("argument too long"); - outp.append((uint16_t)a->length()); - outp.append(*a); - } - outp.compress(); - - uint64_t id = outp.packetId(); - - { - Mutex::Lock _l(_remoteCallsOutstanding_m); - RemoteCallOutstanding &rc = _remoteCallsOutstanding[id]; - rc.callTime = Utils::now(); - rc.peer = peer; - rc.handler = handler; - rc.arg = arg; - } - - _r->sw->send(outp,true); - - return id; -} - -void RPC::clean() -{ - Mutex::Lock _l(_remoteCallsOutstanding_m); - uint64_t now = Utils::now(); - for(std::map::iterator co(_remoteCallsOutstanding.begin());co!=_remoteCallsOutstanding.end();) { - if ((now - co->second.callTime) >= ZT_RPC_TIMEOUT) { - if (co->second.handler) - co->second.handler(co->second.arg,co->first,co->second.peer,ZT_RPC_ERROR_EXPIRED_NO_RESPONSE,std::vector()); - _remoteCallsOutstanding.erase(co++); - } else ++co; - } -} - -} // namespace ZeroTier diff --git a/node/RPC.hpp b/node/RPC.hpp deleted file mode 100644 index 669bf5ae..00000000 --- a/node/RPC.hpp +++ /dev/null @@ -1,196 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 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/ - */ - -#ifndef _ZT_RPC_HPP -#define _ZT_RPC_HPP - -#include - -#include -#include -#include -#include - -#include "Constants.hpp" -#include "NonCopyable.hpp" -#include "Mutex.hpp" -#include "Address.hpp" - -namespace ZeroTier { - -class RuntimeEnvironment; - -/** - * Peer or method not found - */ -#define ZT_RPC_ERROR_NOT_FOUND -1 - -/** - * A runtime error occurred - */ -#define ZT_RPC_ERROR_RUNTIME -2 - -/** - * Call was expired without response from target - */ -#define ZT_RPC_ERROR_EXPIRED_NO_RESPONSE -3 - -/** - * Call was cancelled (or RPC is shutting down) - */ -#define ZT_RPC_ERROR_CANCELLED -4 - -/** - * RPC request and result handler - */ -class RPC : NonCopyable -{ -public: -#ifndef __WINDOWS__ - /** - * A local service accessible by RPC, non-Windows only for now - * - * Each service DLL must export these functions: - * - * int ZeroTierPluginInit(); - * int ZeroTierPluginDo(unsigned int,const unsigned int *,const void **,const unsigned int **,const void ***); - * void ZeroTierPluginFree(int,const unsigned int *,const void **); - * void ZeroTierPluginDestroy(); - * - * Init is called on library load, Destroy on unload. Do() may - * be called from multiple threads concurrently, so any locking - * is the responsibility of the library. These must have C - * function signatures (extern "C" in C++). - * - * Do's arguments are: the number of paramters, the size of each parameter in bytes, - * and each parameter's contents. The last two arguments are result parameters. The - * first result parameter must be set to an array of integers describing the size of - * each result. The second is set to an array of pointers to actual results. The number - * of results (size of both arrays) is returned. If Do() returns zero or negative, - * these result paremeters are not used by the caller and don't need to be set. - * - * After the caller is done with Do()'s results, it calls ZeroTierPluginFree() to - * free them. This may also be called concurrently. Free() takes the number of - * results, the array of result sizes, and the result array. - */ - class LocalService : NonCopyable - { - public: - /** - * @param dllPath Path to DLL/shared object - * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL - */ - LocalService(const char *dllPath) - throw(std::invalid_argument); - - ~LocalService(); - - /** - * Call the DLL, return result - * - * @param args Input arguments - * @return Results from DLL - * @throws std::runtime_error Error calling DLL - */ - std::pair< int,std::vector > operator()(const std::vector &args); - - private: - void *_handle; - void *_init; - void *_do; - void *_free; - void *_destroy; - }; -#endif - - RPC(const RuntimeEnvironment *renv); - ~RPC(); - - /** - * Used by PacketDecoder to call local RPC methods - * - * @param name Name of locally loaded method to call - * @param args Arguments to method - * @return Return value of method, and results (negative first item and empty vector means error) - */ - std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); - - /** - * Load a plugin - * - * @param name Name of RPC function - * @param path Path to plugin DLL - * @throws std::invalid_argument Unable to properly load or resolve symbol(s) in DLL - */ - void loadLocal(const char *name,const char *path) - throw(std::invalid_argument); - - /** - * Call a remote service - * - * @param peer Peer to call on - * @param name Name of remote function - * @param args Arguments to remote function - * @param handler Handler to call on result - * @param arg First argument to handler - * @return Call ID (packet ID of sent packet) - */ - uint64_t callRemote( - const Address &peer, - const std::string &name, - const std::vector &args, - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), - void *arg) - throw(std::invalid_argument,std::out_of_range); - - /** - * Periodically called to clean up, such as by expiring remote calls - */ - void clean(); - -private: - const RuntimeEnvironment *_r; - -#ifndef __WINDOWS__ - std::map _rpcServices; - Mutex _rpcServices_m; -#endif - - struct RemoteCallOutstanding - { - uint64_t callTime; - Address peer; - void (*handler)(void *,uint64_t,const Address &,int,const std::vector &); - void *arg; - }; - std::map _remoteCallsOutstanding; - Mutex _remoteCallsOutstanding_m; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 35c2de3f..7797e651 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -42,7 +42,6 @@ class Topology; class SysEnv; class Multicaster; class CMWC4096; -class RPC; /** * Holds global state for an instance of ZeroTier::Node @@ -67,8 +66,7 @@ public: multicaster((Multicaster *)0), sw((Switch *)0), topology((Topology *)0), - sysEnv((SysEnv *)0), - rpc((RPC *)0) + sysEnv((SysEnv *)0) { } @@ -90,7 +88,6 @@ public: Switch *sw; Topology *topology; SysEnv *sysEnv; - RPC *rpc; }; } // namespace ZeroTier diff --git a/objects.mk b/objects.mk index d7811a2e..f5b88719 100644 --- a/objects.mk +++ b/objects.mk @@ -21,7 +21,6 @@ OBJS=\ node/PacketDecoder.o \ node/Pack.o \ node/Peer.o \ - node/RPC.o \ node/Salsa20.o \ node/Switch.o \ node/SysEnv.o \ -- cgit v1.2.3 From 7a17f6ca80e3df9e1509dc99d0acdd00f12686e0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 27 Jul 2013 16:20:08 -0400 Subject: Add skeleton of certificate-based private network authentication. Also remove some old code. --- node/BlobArray.hpp | 94 ------------------------------- node/Network.hpp | 15 ++++- node/Node.cpp | 1 - node/Pack.cpp | 159 ----------------------------------------------------- node/Pack.hpp | 141 ----------------------------------------------- node/Packet.cpp | 3 + node/Packet.hpp | 25 ++++++++- objects.mk | 1 - 8 files changed, 40 insertions(+), 399 deletions(-) delete mode 100644 node/BlobArray.hpp delete mode 100644 node/Pack.cpp delete mode 100644 node/Pack.hpp (limited to 'node/Node.cpp') diff --git a/node/BlobArray.hpp b/node/BlobArray.hpp deleted file mode 100644 index d78bcffa..00000000 --- a/node/BlobArray.hpp +++ /dev/null @@ -1,94 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 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/ - */ - -#ifndef _ZT_BLOBARRAY_HPP -#define _ZT_BLOBARRAY_HPP - -#include -#include -#include - -namespace ZeroTier { - -/** - * A vector of binary strings serializable in a packed format - * - * The format uses variable-length integers to indicate the length of each - * field. Each byte of the length has another byte with seven more significant - * bits if its 8th bit is set. Fields can be up to 2^28 in length. - */ -class BlobArray : public std::vector -{ -public: - inline std::string serialize() const - { - std::string r; - for(BlobArray::const_iterator i=begin();i!=end();++i) { - unsigned int flen = (unsigned int)i->length(); - do { - unsigned char flenb = (unsigned char)(flen & 0x7f); - flen >>= 7; - flenb |= (flen) ? 0x80 : 0; - r.push_back((char)flenb); - } while (flen); - r.append(*i); - } - return r; - } - - /** - * Deserialize, replacing the current contents of this array - * - * @param data Serialized binary data - * @param len Length of serialized data - */ - inline void deserialize(const void *data,unsigned int len) - { - clear(); - for(unsigned int i=0;i. - * - * -- - * - * 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 -#include -#include "Pack.hpp" -#include "BlobArray.hpp" -#include "Utils.hpp" - -#include - -namespace ZeroTier { - -std::vector Pack::getAll() const -{ - std::vector v; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) - v.push_back(&(e->second)); - return v; -} - -const Pack::Entry *Pack::get(const std::string &name) const -{ - std::map::const_iterator e(_entries.find(name)); - return ((e == _entries.end()) ? (const Entry *)0 : &(e->second)); -} - -const Pack::Entry *Pack::put(const std::string &name,const std::string &content) -{ - SHA256_CTX sha; - - Pack::Entry &e = _entries[name]; - e.name = name; - e.content = content; - - SHA256_Init(&sha); - SHA256_Update(&sha,content.data(),content.length()); - SHA256_Final(e.sha256,&sha); - - e.signedBy = 0; - e.signature.assign((const char *)0,0); - - return &e; -} - -void Pack::clear() -{ - _entries.clear(); -} - -std::string Pack::serialize() const -{ - BlobArray archive; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) { - BlobArray entry; - entry.push_back(e->second.name); - entry.push_back(e->second.content); - entry.push_back(std::string((const char *)e->second.sha256,sizeof(e->second.sha256))); - entry.push_back(e->second.signedBy.toBinaryString()); - entry.push_back(e->second.signature); - archive.push_back(entry.serialize()); - } - - std::string ser(archive.serialize()); - std::string comp; - Utils::compress(ser.begin(),ser.end(),Utils::StringAppendOutput(comp)); - return comp; -} - -bool Pack::deserialize(const void *sd,unsigned int sdlen) -{ - unsigned char dig[32]; - SHA256_CTX sha; - - std::string decomp; - if (!Utils::decompress(((const char *)sd),((const char *)sd) + sdlen,Utils::StringAppendOutput(decomp))) - return false; - - BlobArray archive; - archive.deserialize(decomp.data(),decomp.length()); - clear(); - for(BlobArray::const_iterator i=archive.begin();i!=archive.end();++i) { - BlobArray entry; - entry.deserialize(i->data(),i->length()); - - if (entry.size() != 5) return false; - if (entry[2].length() != 32) return false; // SHA-256 - if (entry[3].length() != ZT_ADDRESS_LENGTH) return false; // Address - - Pack::Entry &e = _entries[entry[0]]; - e.name = entry[0]; - e.content = entry[1]; - - SHA256_Init(&sha); - SHA256_Update(&sha,e.content.data(),e.content.length()); - SHA256_Final(dig,&sha); - if (memcmp(dig,entry[2].data(),32)) return false; // integrity check failed - memcpy(e.sha256,dig,32); - - if (entry[3].length() == ZT_ADDRESS_LENGTH) - e.signedBy.setTo(entry[3].data()); - else e.signedBy = 0; - e.signature = entry[4]; - } - return true; -} - -bool Pack::signAll(const Identity &id) -{ - for(std::map::iterator e=_entries.begin();e!=_entries.end();++e) { - e->second.signedBy = id.address(); - e->second.signature = id.sign(e->second.sha256); - if (!e->second.signature.length()) - return false; - } - return true; -} - -std::vector Pack::verifyAll(const Identity &id,bool mandatory) const -{ - std::vector bad; - for(std::map::const_iterator e=_entries.begin();e!=_entries.end();++e) { - if ((e->second.signedBy)&&(e->second.signature.length())) { - if (id.address() != e->second.signedBy) - bad.push_back(&(e->second)); - else if (!id.verifySignature(e->second.sha256,e->second.signature.data(),e->second.signature.length())) - bad.push_back(&(e->second)); - } else if (mandatory) - bad.push_back(&(e->second)); - } - return bad; -} - -} // namespace ZeroTier diff --git a/node/Pack.hpp b/node/Pack.hpp deleted file mode 100644 index a0aecd6e..00000000 --- a/node/Pack.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2013 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/ - */ - -#ifndef _ZT_PACK_HPP -#define _ZT_PACK_HPP - -#include -#include -#include -#include -#include "Address.hpp" -#include "Identity.hpp" - -namespace ZeroTier { - -/** - * A very simple archive format for distributing packs of files or resources - * - * This is used for things like the auto-updater. It's not suitable for huge - * files, since at present it must work in memory. Packs support signing with - * identities and signature verification. - */ -class Pack -{ -public: - /** - * Pack entry structure for looking up deserialized entries - */ - struct Entry - { - std::string name; - std::string content; - unsigned char sha256[32]; - Address signedBy; - std::string signature; - }; - - Pack() {} - ~Pack() {} - - /** - * @return Vector of all entries - */ - std::vector getAll() const; - - /** - * Look up an entry - * - * @param name Name to look up - * @return Pointer to entry if it exists or NULL if not found - */ - const Entry *get(const std::string &name) const; - - /** - * Add an entry to this pack - * - * @param name Entry to add - * @param content Entry's contents - * @return The new entry - */ - const Entry *put(const std::string &name,const std::string &content); - - /** - * Remove all entries - */ - void clear(); - - /** - * @return Number of entries in pack - */ - inline unsigned int numEntries() const { return (unsigned int)_entries.size(); } - - /** - * Serialize this pack - * - * @return Serialized form (compressed with LZ4) - */ - std::string serialize() const; - - /** - * Deserialize this pack - * - * Any current contents are lost. This does not verify signatures, - * but does check SHA256 hashes for entry integrity. If the return - * value is false, the pack's contents are undefined. - * - * @param sd Serialized data - * @param sdlen Length of serialized data - * @return True on success, false on deserialization error - */ - bool deserialize(const void *sd,unsigned int sdlen); - inline bool deserialize(const std::string &sd) { return deserialize(sd.data(),sd.length()); } - - /** - * Sign all entries in this pack with a given identity - * - * @param id Identity to sign with - * @return True on signature success, false if error - */ - bool signAll(const Identity &id); - - /** - * Verify all signed entries - * - * @param id Identity to verify against - * @param mandatory If true, require that all entries be signed and fail if no signature - * @return Vector of entries that failed verification or empty vector if all passed - */ - std::vector verifyAll(const Identity &id,bool mandatory) const; - -private: - std::map _entries; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/Packet.cpp b/node/Packet.cpp index d12f396d..4728609d 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -42,6 +42,7 @@ const char *Packet::verbString(Verb v) case VERB_FRAME: return "FRAME"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; + case VERB_NETWORK_PERMISSION_CERTIFICATE: return "NETWORK_PERMISSION_CERTIFICATE"; } return "(unknown)"; } @@ -57,6 +58,8 @@ const char *Packet::errorString(ErrorCode e) case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION"; case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; + case ERROR_NO_NETWORK_CERTIFICATE_ON_FILE: return "NO_NETWORK_CERTIFICATE_ON_FILE"; + case ERROR_OBJECT_EXPIRED: return "OBJECT_EXPIRED"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 5ccfae45..86d94e1d 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -463,7 +463,22 @@ public: * * No OK or ERROR is generated. */ - VERB_MULTICAST_FRAME = 9 + VERB_MULTICAST_FRAME = 9, + + /* Network permission certificate: + * <[8] 64-bit network ID> + * <[1] flags (currently unused, must be 0)> + * <[8] certificate timestamp> + * <[8] 16-bit length of signature> + * <[...] ECDSA signature of my binary serialized identity and timestamp> + * + * This message is used to send ahead of time a certificate proving + * this node has permission to communicate on a private network. + * + * OK is generated on acceptance. ERROR is returned on failure. In both + * cases the payload is the network ID. + */ + VERB_NETWORK_PERMISSION_CERTIFICATE = 10 }; /** @@ -490,7 +505,13 @@ public: ERROR_IDENTITY_INVALID = 5, /* Verb or use case not supported/enabled by this node */ - ERROR_UNSUPPORTED_OPERATION = 6 + ERROR_UNSUPPORTED_OPERATION = 6, + + /* Message to private network rejected -- no unexpired certificate on file */ + ERROR_NO_NETWORK_CERTIFICATE_ON_FILE = 7, + + /* Object is expired (e.g. network certificate) */ + ERROR_OBJECT_EXPIRED = 8 }; /** diff --git a/objects.mk b/objects.mk index 49312994..c6fab0b6 100644 --- a/objects.mk +++ b/objects.mk @@ -18,7 +18,6 @@ OBJS=\ node/NodeConfig.o \ node/Packet.o \ node/PacketDecoder.o \ - node/Pack.o \ node/Peer.o \ node/Salsa20.o \ node/Switch.o \ -- cgit v1.2.3 From 7e156b262283985cbfe560317ec3d69d1bf19511 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 30 Jul 2013 11:14:53 -0400 Subject: Call clean on all networks periodically, generalize Topology clean cycle to an overall clean cycle. --- node/Constants.hpp | 4 ++-- node/Network.cpp | 13 +++++++++++-- node/Network.hpp | 11 ++++++++++- node/Node.cpp | 9 +++++---- node/NodeConfig.cpp | 7 +++++++ node/NodeConfig.hpp | 5 +++++ 6 files changed, 40 insertions(+), 9 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index b8dc9ebf..36973b38 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -183,9 +183,9 @@ error_no_ZT_ARCH_defined; #define ZT_MAC_FIRST_OCTET 0x32 /** - * How often Topology::clean() is called in ms + * How often Topology::clean() and Network::clean() are called in ms */ -#define ZT_TOPOLOGY_CLEAN_PERIOD 300000 +#define ZT_DB_CLEAN_PERIOD 300000 /** * Delay between WHOIS retries in ms diff --git a/node/Network.cpp b/node/Network.cpp index 5878a281..a50d56dc 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -34,6 +34,7 @@ #include "NodeConfig.hpp" #include "Network.hpp" #include "Switch.hpp" +#include "Packet.hpp" namespace ZeroTier { @@ -103,6 +104,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t id) throw(std::runtime_error) : _r(renv), _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this), + _lastConfigUpdate(0), _id(id) { } @@ -114,16 +116,23 @@ Network::~Network() void Network::setConfiguration(const Network::Config &conf) { Mutex::Lock _l(_lock); - _configuration = conf; - _myCertificate = conf.certificateOfMembership(); + if ((conf.networkId() == _id)&&(conf.peerAddress() == _r->identity.address())) { // sanity check + _configuration = conf; + _myCertificate = conf.certificateOfMembership(); + _lastConfigUpdate = Utils::now(); + } } void Network::requestConfiguration() { + Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append((uint64_t)_id); + _r->sw->send(outp,true); } bool Network::isAllowed(const Address &peer) const { + // Exceptions can occur if we do not yet have *our* configuration. try { Mutex::Lock _l(_lock); if (_configuration.isOpen()) diff --git a/node/Network.hpp b/node/Network.hpp index e553cd3a..62c0e978 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -392,6 +392,15 @@ public: */ void clean(); + /** + * @return Time of last updated configuration or 0 if none + */ + inline uint64_t lastConfigUpdate() const + throw() + { + return _lastConfigUpdate; + } + private: static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data); @@ -402,7 +411,7 @@ private: std::map _membershipCertificates; Config _configuration; Certificate _myCertificate; - uint64_t _lastCertificateUpdate; + uint64_t _lastConfigUpdate; uint64_t _id; Mutex _lock; diff --git a/node/Node.cpp b/node/Node.cpp index 827af23b..9c748b4a 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -339,7 +339,7 @@ Node::ReasonForTermination Node::run() try { uint64_t lastPingCheck = 0; - uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately + uint64_t lastClean = Utils::now(); // don't need to do this immediately uint64_t lastNetworkFingerprintCheck = 0; uint64_t lastAutoconfigureCheck = 0; uint64_t networkConfigurationFingerprint = _r->sysEnv->getNetworkConfigurationFingerprint(); @@ -459,9 +459,10 @@ Node::ReasonForTermination Node::run() } } - if ((now - lastTopologyClean) >= ZT_TOPOLOGY_CLEAN_PERIOD) { - lastTopologyClean = now; - _r->topology->clean(); // happens in background + if ((now - lastClean) >= ZT_DB_CLEAN_PERIOD) { + lastClean = now; + _r->topology->clean(); + _r->nc->cleanAllNetworks(); } try { diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 214c06b7..4a174535 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -72,6 +72,13 @@ void NodeConfig::whackAllTaps() n->second->tap().whack(); } +void NodeConfig::cleanAllNetworks() +{ + Mutex::Lock _l(_networks_m); + for(std::map< uint64_t,SharedPtr >::const_iterator n(_networks.begin());n!=_networks.end();++n) + n->second->clean(); +} + // Macro used in execute() #undef _P #define _P(f,...) { r.push_back(std::string()); Utils::stdsprintf(r.back(),(f),##__VA_ARGS__); } diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index 98678944..62b23609 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -107,6 +107,11 @@ public: */ void whackAllTaps(); + /** + * Call clean() on all networks + */ + void cleanAllNetworks(); + /** * @param nwid Network ID * @return True if this network exists -- cgit v1.2.3 From 741642ba531e487c18c8139c4a2e9510eed0466d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 1 Aug 2013 17:32:37 -0400 Subject: netconf service work --- netconf-service/netconf-test.cpp | 66 ++++++++++++++++++++++++++++++++++++++++ node/Node.cpp | 21 +++++++++++++ node/RuntimeEnvironment.hpp | 9 ++++++ node/Service.hpp | 6 +++- node/Utils.hpp | 9 ++++++ 5 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 netconf-service/netconf-test.cpp (limited to 'node/Node.cpp') diff --git a/netconf-service/netconf-test.cpp b/netconf-service/netconf-test.cpp new file mode 100644 index 00000000..aadaa51d --- /dev/null +++ b/netconf-service/netconf-test.cpp @@ -0,0 +1,66 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2012-2013 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/ + */ + +/* Self-tester that makes both new and repeated requests to netconf */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../node/Dictionary.hpp" +#include "../node/Service.hpp" +#include "../node/Identity.hpp" +#include "../node/RuntimeEnvironment.hpp" +#include "../node/Logger.hpp" + +using namespace ZeroTier; + +static void svcHandler(void *arg,Service &svc,const Dictionary &msg) +{ +} + +int main(int argc,char **argv) +{ + RuntimeEnvironment renv; + renv.log = new Logger((const char *)0,(const char *)0,0); + Service svc(&renv,"netconf","./netconf.service",&svcHandler,(void *)0); + + srand(time(0)); + + std::vector population; + for(;;) { + if ((population.empty())||(rand() < (RAND_MAX / 4))) { + } else { + } + } +} diff --git a/node/Node.cpp b/node/Node.cpp index 9c748b4a..e67dd526 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -67,6 +67,7 @@ #include "Mutex.hpp" #include "Multicaster.hpp" #include "CMWC4096.hpp" +#include "Service.hpp" #include "../version.h" @@ -191,6 +192,10 @@ struct _NodeImpl } }; +static void _netconfServiceMessageHandler(void *renv,Service &svc,const Dictionary &msg) +{ +} + Node::Node(const char *hp) throw() : _impl(new _NodeImpl) @@ -209,6 +214,10 @@ Node::~Node() { _NodeImpl *impl = (_NodeImpl *)_impl; +#ifndef __WINDOWS__ + delete impl->renv.netconfService; +#endif + delete impl->renv.sysEnv; delete impl->renv.topology; delete impl->renv.sw; @@ -337,6 +346,18 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization"); } +#ifndef __WINDOWS__ + try { + std::string netconfServicePath(_r->homePath + ZT_PATH_SEPARATOR_S + "services.d" + ZT_PATH_SEPARATOR_S + "netconf.service"); + if (Utils::fileExists(netconfServicePath.c_str())) { + LOG("netconf.d/netconfi.service appears to exist, starting..."); + _r->netconfService = new Service(_r,"netconf",netconfServicePath.c_str(),&_netconfServiceMessageHandler,_r); + } + } catch ( ... ) { + LOG("unexpected exception attempting to start services"); + } +#endif + try { uint64_t lastPingCheck = 0; uint64_t lastClean = Utils::now(); // don't need to do this immediately diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 7797e651..00d393af 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -29,6 +29,7 @@ #define _ZT_RUNTIMEENVIRONMENT_HPP #include +#include "Constants.hpp" #include "Identity.hpp" #include "Condition.hpp" @@ -42,6 +43,7 @@ class Topology; class SysEnv; class Multicaster; class CMWC4096; +class Service; /** * Holds global state for an instance of ZeroTier::Node @@ -67,6 +69,9 @@ public: sw((Switch *)0), topology((Topology *)0), sysEnv((SysEnv *)0) +#ifndef __WINDOWS__ + ,netconfService((Service *)0) +#endif { } @@ -88,6 +93,10 @@ public: Switch *sw; Topology *topology; SysEnv *sysEnv; + +#ifndef __WINDOWS__ + Service *netconfService; // may be null +#endif }; } // namespace ZeroTier diff --git a/node/Service.hpp b/node/Service.hpp index 8b9407e7..c8b729c7 100644 --- a/node/Service.hpp +++ b/node/Service.hpp @@ -72,7 +72,11 @@ public: * @param handler Handler function to call when service generates output * @param arg First argument to service */ - Service(const RuntimeEnvironment *renv,const char *name,const char *path,void (*handler)(void *,Service &,const Dictionary &),void *arg); + Service(const RuntimeEnvironment *renv, + const char *name, + const char *path, + void (*handler)(void *,Service &,const Dictionary &), + void *arg); virtual ~Service(); diff --git a/node/Utils.hpp b/node/Utils.hpp index de7acf0e..a222ca4f 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -119,6 +119,15 @@ public: */ static uint64_t getLastModified(const char *path); + /** + * @param path Path to check + * @return True if file or directory exists at path location + */ + static inline bool fileExists(const char *path) + { + return (getLastModified(path) != 0); + } + /** * @param t64 Time in ms since epoch * @return RFC1123 date string -- cgit v1.2.3 From 80d8b7d0ae56f1dce8b5b25ab7930df436755daf Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 2 Aug 2013 17:17:34 -0400 Subject: Netconf wired up, ready to test. --- netconf-service/netconf.cpp | 16 ++++++++++++++ node/Node.cpp | 46 +++++++++++++++++++++++++++++++++++++++ node/Packet.hpp | 13 ++++++++++++ node/PacketDecoder.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) (limited to 'node/Node.cpp') diff --git a/netconf-service/netconf.cpp b/netconf-service/netconf.cpp index 01f33120..af6ed4e6 100644 --- a/netconf-service/netconf.cpp +++ b/netconf-service/netconf.cpp @@ -219,6 +219,22 @@ int main(int argc,char **argv) StoreQueryResult rs = q.store(); if (rs.num_rows() > 0) isOpen = ((int)rs[0]["isOpen"] > 0); + else { + Dictionary response; + response["peer"] = peerIdentity.address().toString(); + response["nwid"] = request.get("nwid"); + response["type"] = "netconf-response"; + response["requestId"] = request.get("requestId"); + response["error"] = "NOT_FOUND"; + std::string respm = response.toString(); + uint32_t respml = (uint32_t)htonl((uint32_t)respm.length()); + + stdoutWriteLock.lock(); + write(STDOUT_FILENO,&respml,4); + write(STDOUT_FILENO,respm.data(),respm.length()); + stdoutWriteLock.unlock(); + continue; + } } Dictionary netconf; diff --git a/node/Node.cpp b/node/Node.cpp index e67dd526..8079e801 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -50,6 +50,7 @@ #include "Node.hpp" #include "Topology.hpp" #include "Demarc.hpp" +#include "Packet.hpp" #include "Switch.hpp" #include "Utils.hpp" #include "EthernetTap.hpp" @@ -192,9 +193,54 @@ struct _NodeImpl } }; +#ifndef __WINDOWS__ static void _netconfServiceMessageHandler(void *renv,Service &svc,const Dictionary &msg) { + if (!renv) + return; // sanity check + const RuntimeEnvironment *_r = (const RuntimeEnvironment *)renv; + + try { + const std::string &type = msg.get("type"); + if (type == "netconf-response") { + uint64_t inRePacketId = strtoull(msg.get("requestId").c_str(),(char **)0,16); + SharedPtr network = _r->nc->network(strtoull(msg.get("nwid").c_str(),(char **)0,16)); + Address peerAddress(msg.get("peer").c_str()); + + if ((network)&&(peerAddress)) { + if (msg.contains("error")) { + Packet::ErrorCode errCode = Packet::ERROR_INVALID_REQUEST; + const std::string &err = msg.get("error"); + if (err == "NOT_FOUND") + errCode = Packet::ERROR_NOT_FOUND; + + Packet outp(peerAddress,_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(inRePacketId); + outp.append((unsigned char)errCode); + outp.append(network->id()); + _r->sw->send(outp,true); + } else if (msg.contains("netconf")) { + const std::string &netconf = msg.get("netconf"); + if (netconf.length() < 2048) { // sanity check + Packet outp(peerAddress,_r->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(inRePacketId); + outp.append(network->id()); + outp.append((uint16_t)netconf.length()); + outp.append(netconf.data(),netconf.length()); + _r->sw->send(outp,true); + } + } + } + } + } catch (std::exception &exc) { + LOG("unexpected exception parsing response from netconf service: %s",exc.what()); + } catch ( ... ) { + LOG("unexpected exception parsing response from netconf service: unknown exception"); + } } +#endif // !__WINDOWS__ Node::Node(const char *hp) throw() : diff --git a/node/Packet.hpp b/node/Packet.hpp index 1d63929b..0e7ccea3 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -132,27 +132,34 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES 64 // Field incides for parsing verbs + #define ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_REVISION (ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION + 1) #define ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP (ZT_PROTO_VERB_HELLO_IDX_REVISION + 2) #define ZT_PROTO_VERB_HELLO_IDX_IDENTITY (ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP + 8) + #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB + 1) #define ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE (ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID + 8) #define ZT_PROTO_VERB_ERROR_IDX_PAYLOAD (ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE + 1) + #define ZT_PROTO_VERB_OK_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_OK_IDX_IN_RE_VERB + 1) #define ZT_PROTO_VERB_OK_IDX_PAYLOAD (ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID + 8) + #define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) + #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2) #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1) + #define ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID + 8) #define ZT_PROTO_VERB_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE + 2) + #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS (ZT_PACKET_IDX_PAYLOAD) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID + 8) @@ -166,6 +173,12 @@ #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD_LENGTH + 2) #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH + 2) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8) +#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2) + +#define ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD) + // Field indices for parsing OK and ERROR payloads of replies #define ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP (ZT_PROTO_VERB_OK_IDX_PAYLOAD) #define ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY (ZT_PROTO_VERB_OK_IDX_PAYLOAD) diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 0353625c..518ed9e7 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -25,6 +25,7 @@ * LLC. Start here: http://www.zerotier.com/ */ +#include "Constants.hpp" #include "RuntimeEnvironment.hpp" #include "Topology.hpp" #include "PacketDecoder.hpp" @@ -32,6 +33,7 @@ #include "Peer.hpp" #include "NodeConfig.hpp" #include "Filter.hpp" +#include "Service.hpp" namespace ZeroTier { @@ -546,14 +548,64 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr &peer) { + // TODO: not implemented yet, will be needed for private networks. + + return true; } bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr &peer) { + char tmp[128]; + try { + uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); +#ifndef __WINDOWS__ + if (_r->netconfService) { + unsigned int dictLen = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN); + std::string dict((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,dictLen),dictLen); + + Dictionary request; + request["type"] = "netconf-request"; + request["peerId"] = peer->identity().toString(false); + sprintf(tmp,"%llx",(unsigned long long)nwid); + request["nwid"] = tmp; + sprintf(tmp,"%llx",(unsigned long long)packetId()); + request["requestId"] = tmp; + _r->netconfService->send(request); + } else { +#endif // !__WINDOWS__ + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); + outp.append(nwid); + outp.encrypt(peer->cryptKey()); + outp.hmacSet(peer->macKey()); + _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1); + TRACE("sent ERROR(NETWORK_CONFIG_REQUEST,UNSUPPORTED_OPERATION) to %s(%s)",peer->address().toString().c_str(),_remoteAddress.toString().c_str()); +#ifndef __WINDOWS__ + } +#endif // !__WINDOWS__ + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; } bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr &peer) { + try { + uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID); + SharedPtr nw(_r->nc->network(nwid)); + if ((nw)&&(source() == nw->controller())) // only respond to requests from controller + nw->requestConfiguration(); + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; } } // namespace ZeroTier -- cgit v1.2.3