From af8fcac0fcfd64600a442dc4d633601e29e611ea Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 25 Jul 2013 15:19:35 -0400 Subject: [PATCH] RPC infrastructure work in progress. --- Makefile.linux | 2 +- Makefile.mac | 2 +- node/Constants.hpp | 5 ++ node/Packet.hpp | 2 +- node/RPC.cpp | 159 +++++++++++++++++++++++++++++++++++++++++++++ node/RPC.hpp | 21 +++--- objects.mk | 1 + 7 files changed, 179 insertions(+), 13 deletions(-) diff --git a/Makefile.linux b/Makefile.linux index 773272825..21fc615ce 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -20,7 +20,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti # separate binaries for the RedHat and Debian universes to distribute via # auto-update. This way we get one Linux binary for all systems of a given # architecture. -LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a +LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -ldl include objects.mk diff --git a/Makefile.mac b/Makefile.mac index 5f6283ba1..4f85cc5a3 100644 --- a/Makefile.mac +++ b/Makefile.mac @@ -13,7 +13,7 @@ STRIP=strip #STRIP=echo CXXFLAGS=$(CFLAGS) -fno-rtti -LIBS=-lcrypto +LIBS=-lcrypto -ldl include objects.mk diff --git a/node/Constants.hpp b/node/Constants.hpp index b8dc9ebfd..9f856948c 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -324,4 +324,9 @@ 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/Packet.hpp b/node/Packet.hpp index 133614970..8b06d1a52 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -466,7 +466,7 @@ public: VERB_MULTICAST_FRAME = 9, /* Call a plugin via RPC: - * <[1] length of function name> + * <[2] length of function name> * <[...] function name> * [<[2] length of argument>] * [<[...] argument>] diff --git a/node/RPC.cpp b/node/RPC.cpp index 2c69033ee..e6591d7d4 100644 --- a/node/RPC.cpp +++ b/node/RPC.cpp @@ -25,7 +25,166 @@ * 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 { + +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); +} + +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()); + } + + for(std::map::iterator s(_rpcServices.begin());s!=_rpcServices.end();++s) + delete s->second; +} + +std::pair< int,std::vector > RPC::callLocal(const std::string &name,const std::vector &args) +{ + 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)); +} + +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 index ac41dd6ac..e31b5f6de 100644 --- a/node/RPC.hpp +++ b/node/RPC.hpp @@ -35,6 +35,7 @@ #include #include +#include "Constants.hpp" #include "NonCopyable.hpp" #include "Mutex.hpp" #include "Address.hpp" @@ -69,13 +70,13 @@ class RuntimeEnvironment; class RPC : NonCopyable { public: -#ifndef _WIN32 +#ifndef __WINDOWS__ /** * A local service accessible by RPC, non-Windows only for now * * Each service DLL must export these functions: * - * void ZeroTierPluginInit(); + * 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(); @@ -115,11 +116,14 @@ public: * @return Results from DLL * @throws std::runtime_error Error calling DLL */ - std::vector operator()(const std::vector &args) - throw(std::runtime_error); + std::pair< int,std::vector > operator()(const std::vector &args); private: - void *_dlhandle; + void *_handle; + void *_init; + void *_do; + void *_free; + void *_destroy; }; #endif @@ -133,8 +137,7 @@ public: * @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) - throw(); + std::pair< int,std::vector > callLocal(const std::string &name,const std::vector &args); /** * Call a remote service @@ -152,7 +155,7 @@ public: const std::vector &args, void (*handler)(void *,uint64_t,const Address &,int,const std::vector &), void *arg) - throw(std::invalid_argument); + throw(std::invalid_argument,std::out_of_range); /** * Periodically called to clean up, such as by expiring remote calls @@ -169,8 +172,6 @@ private: { uint64_t callTime; Address peer; - std::string name; - std::vector &args; void (*handler)(void *,uint64_t,const Address &,int,const std::vector &); void *arg; }; diff --git a/objects.mk b/objects.mk index f5b887194..d7811a2ee 100644 --- a/objects.mk +++ b/objects.mk @@ -21,6 +21,7 @@ OBJS=\ node/PacketDecoder.o \ node/Pack.o \ node/Peer.o \ + node/RPC.o \ node/Salsa20.o \ node/Switch.o \ node/SysEnv.o \