diff --git a/node/Peer.cpp b/node/Peer.cpp index 986e52ef7..127f222cf 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -369,7 +369,7 @@ void Peer::tryMemorizedPath(void *tPtr,uint64_t now) _lastTriedMemorizedPath = now; InetAddress mp; if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp)) - attemptToContactAt(tPtr,InetAddress(),mp,now,true,0); + attemptToContactAt(tPtr,-1,mp,now,true,0); } } diff --git a/node/Peer.hpp b/node/Peer.hpp index c6423a59c..af9163a59 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -439,6 +439,90 @@ public: return false; } + /** + * Serialize a peer for storage in local cache + * + * This does not serialize everything, just identity and addresses where the peer + * may be reached. + */ + template + inline void serialize(Buffer &b) const + { + b.append((uint8_t)0); + + _id.serialize(b); + + b.append(_lastReceive); + b.append(_lastNontrivialReceive); + b.append(_lastTriedMemorizedPath); + b.append(_lastDirectPathPushSent); + b.append(_lastDirectPathPushReceive); + b.append(_lastCredentialRequestSent); + b.append(_lastWhoisRequestReceived); + b.append(_lastEchoRequestReceived); + b.append(_lastComRequestReceived); + b.append(_lastComRequestSent); + b.append(_lastCredentialsReceived); + b.append(_lastTrustEstablishedPacketReceived); + + b.append((uint16_t)_vProto); + b.append((uint16_t)_vMajor); + b.append((uint16_t)_vMinor); + b.append((uint16_t)_vRevision); + + { + Mutex::Lock _l(_paths_m); + unsigned int pcount = 0; + if (_v4Path.p) ++pcount; + if (_v6Path.p) ++pcount; + b.append((uint8_t)pcount); + if (_v4Path.p) _v4Path.p->address().serialize(b); + if (_v6Path.p) _v6Path.p->address().serialize(b); + } + + b.append((uint16_t)0); + } + + template + inline static SharedPtr deserializeFromCache(uint64_t now,void *tPtr,Buffer &b,const RuntimeEnvironment *renv) + { + try { + unsigned int ptr = 0; + if (b[ptr++] != 0) + return SharedPtr(); + + Identity id; + ptr += id.deserialize(b,ptr); + if (!id) + return SharedPtr(); + + SharedPtr p(new Peer(renv,renv->identity,id)); + + ptr += 12 * 8; // skip deserializing ephemeral state in this case + + p->_vProto = b.template at(ptr); ptr += 2; + p->_vMajor = b.template at(ptr); ptr += 2; + p->_vMinor = b.template at(ptr); ptr += 2; + p->_vRevision = b.template at(ptr); ptr += 2; + + const unsigned int pcount = (unsigned int)b[ptr++]; + for(unsigned int i=0;iattemptToContactAt(tPtr,-1,inaddr,now,true,0); + } catch ( ... ) { + break; + } + } + + return p; + } catch ( ... ) { + return SharedPtr(); + } + } + private: struct _PeerPath { diff --git a/node/Topology.cpp b/node/Topology.cpp index edca01802..aeca59a74 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -88,6 +88,15 @@ Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) : addWorld(tPtr,defaultPlanet,false); } +Topology::~Topology() +{ + Hashtable< Address,SharedPtr >::Iterator i(_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) + _savePeer((void *)0,*p); +} + SharedPtr Topology::addPeer(void *tPtr,const SharedPtr &peer) { SharedPtr np; @@ -113,23 +122,21 @@ SharedPtr Topology::getPeer(void *tPtr,const Address &zta) return *ap; } - /* try { - char buf[ZT_PEER_MAX_SERIALIZED_STATE_SIZE]; + Buffer buf; uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0; - int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf,(unsigned int)sizeof(buf)); + int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf.unsafeData(),ZT_PEER_MAX_SERIALIZED_STATE_SIZE); if (len > 0) { Mutex::Lock _l(_peers_m); SharedPtr &ap = _peers[zta]; if (ap) return ap; - ap = Peer::createFromStateUpdate(RR,tPtr,buf,len); + ap = Peer::deserializeFromCache(RR->node->now(),tPtr,buf,RR); if (!ap) _peers.erase(zta); return ap; } } catch ( ... ) {} // ignore invalid identities or other strage failures - */ return SharedPtr(); } @@ -383,8 +390,10 @@ void Topology::doPeriodicTasks(void *tPtr,uint64_t now) Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { - if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) + if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) ) { + _savePeer(tPtr,*p); _peers.erase(*a); + } } } @@ -440,4 +449,14 @@ void Topology::_memoizeUpstreams(void *tPtr) _cor.sign(RR->identity,RR->node->now()); } +void Topology::_savePeer(void *tPtr,const SharedPtr &peer) +{ + try { + Buffer buf; + peer->serialize(buf); + uint64_t tmpid[2]; tmpid[0] = peer->address().toInt(); tmpid[1] = 0; + RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER,tmpid,buf.data(),buf.size()); + } catch ( ... ) {} // sanity check, discard invalid entries +} + } // namespace ZeroTier diff --git a/node/Topology.hpp b/node/Topology.hpp index 30e58abc0..04dfb1cc1 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -59,6 +59,7 @@ class Topology { public: Topology(const RuntimeEnvironment *renv,void *tPtr); + ~Topology(); /** * Add a peer to database @@ -419,6 +420,7 @@ public: private: Identity _getIdentity(void *tPtr,const Address &zta); void _memoizeUpstreams(void *tPtr); + void _savePeer(void *tPtr,const SharedPtr &peer); const RuntimeEnvironment *const RR; diff --git a/service/OneService.cpp b/service/OneService.cpp index f5f8700a0..cd33e399f 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -2047,6 +2047,8 @@ public: char p[1024]; FILE *f; bool secure = false; + char dirname[1024]; + dirname[0] = 0; switch(type) { case ZT_STATE_OBJECT_IDENTITY_PUBLIC: @@ -2060,12 +2062,18 @@ public: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; case ZT_STATE_OBJECT_MOON: - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id[0]); + OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "moons.d",_homePath.c_str()); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.moon",dirname,(unsigned long long)id[0]); break; case ZT_STATE_OBJECT_NETWORK_CONFIG: - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]); + OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str()); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]); secure = true; break; + case ZT_STATE_OBJECT_PEER: + OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str()); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]); + break; default: return; } @@ -2084,6 +2092,10 @@ public: } f = fopen(p,"w"); + if ((!f)&&(dirname[0])) { // create subdirectory if it does not exist + OSUtils::mkdir(dirname); + f = fopen(p,"w"); + } if (f) { if (fwrite(data,len,1,f) != 1) fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p); @@ -2108,15 +2120,18 @@ public: case ZT_STATE_OBJECT_IDENTITY_SECRET: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str()); break; - case ZT_STATE_OBJECT_NETWORK_CONFIG: - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id); - break; case ZT_STATE_OBJECT_PLANET: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; case ZT_STATE_OBJECT_MOON: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id); break; + case ZT_STATE_OBJECT_NETWORK_CONFIG: + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id); + break; + case ZT_STATE_OBJECT_PEER: + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d/%.10llx.conf",_homePath.c_str(),(unsigned long long)id[0]); + break; default: return -1; }