mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-19 13:07:55 +00:00
File transfer work, add identities for validation of updates.
This commit is contained in:
parent
ac4e657aaa
commit
6c63bfce69
@ -98,12 +98,37 @@ static inline std::string _mkDefaultHomePath()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline std::map< Address,Identity > _mkUpdateAuth()
|
||||||
|
{
|
||||||
|
std::map< Address,Identity > ua;
|
||||||
|
|
||||||
|
{ // 0001
|
||||||
|
Identity id("e9bc3707b5:0:c4cef17bde99eadf9748c4fd11b9b06dc5cd8eb429227811d2c336e6b96a8d329e8abd0a4f45e47fe1bcebf878c004c822d952ff77fc2833af4c74e65985c435");
|
||||||
|
ua[id.address()] = id;
|
||||||
|
}
|
||||||
|
{ // 0002
|
||||||
|
Identity id("56520eaf93:0:7d858b47988b34399a9a31136de07b46104d7edb4a98fa1d6da3e583d3a33e48be531532b886f0b12cd16794a66ab9220749ec5112cbe96296b18fe0cc79ca05");
|
||||||
|
ua[id.address()] = id;
|
||||||
|
}
|
||||||
|
{ // 0003
|
||||||
|
Identity id("7c195de2e0:0:9f659071c960f9b0f0b96f9f9ecdaa27c7295feed9c79b7db6eedcc11feb705e6dd85c70fa21655204d24c897865b99eb946b753a2bbcf2be5f5e006ae618c54");
|
||||||
|
ua[id.address()] = id;
|
||||||
|
}
|
||||||
|
{ // 0004
|
||||||
|
Identity id("415f4cfde7:0:54118e87777b0ea5d922c10b337c4f4bd1db7141845bd54004b3255551a6e356ba6b9e1e85357dbfafc45630b8faa2ebf992f31479e9005f0472685f2d8cbd6e");
|
||||||
|
ua[id.address()] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ua;
|
||||||
|
}
|
||||||
|
|
||||||
Defaults::Defaults() :
|
Defaults::Defaults() :
|
||||||
#ifdef ZT_TRACE_MULTICAST
|
#ifdef ZT_TRACE_MULTICAST
|
||||||
multicastTraceWatcher(ZT_TRACE_MULTICAST),
|
multicastTraceWatcher(ZT_TRACE_MULTICAST),
|
||||||
#endif
|
#endif
|
||||||
defaultHomePath(_mkDefaultHomePath()),
|
defaultHomePath(_mkDefaultHomePath()),
|
||||||
supernodes(_mkSupernodeMap())
|
supernodes(_mkSupernodeMap()),
|
||||||
|
updateAuthorities(_mkUpdateAuth())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,12 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Identities permitted to sign software updates
|
* Identities permitted to sign software updates
|
||||||
|
*
|
||||||
|
* ZTN can keep multiple signing identities and rotate them, keeping some in
|
||||||
|
* "cold storage" and obsoleting others gradually.
|
||||||
|
*
|
||||||
|
* If you don't build with ZT_OFFICIAL_BUILD, this isn't used since your
|
||||||
|
* build will not auto-update.
|
||||||
*/
|
*/
|
||||||
const std::map< Address,Identity > updateAuthorities;
|
const std::map< Address,Identity > updateAuthorities;
|
||||||
};
|
};
|
||||||
|
@ -619,12 +619,12 @@ public:
|
|||||||
|
|
||||||
/* Request information about a shared file (for software updates):
|
/* Request information about a shared file (for software updates):
|
||||||
* <[1] flags, currently unused and must be 0>
|
* <[1] flags, currently unused and must be 0>
|
||||||
* <[2] 16-bit length of filename>
|
* <[1] 8-bit length of filename>
|
||||||
* <[...] name of file being requested>
|
* <[...] name of file being requested>
|
||||||
*
|
*
|
||||||
* OK response payload (indicates that we have and will share):
|
* OK response payload (indicates that we have and will share):
|
||||||
* <[1] flags, currently unused and must be 0>
|
* <[1] flags, currently unused and must be 0>
|
||||||
* <[2] 16-bit length of filename>
|
* <[1] 8-bit length of filename>
|
||||||
* <[...] name of file being requested>
|
* <[...] name of file being requested>
|
||||||
* <[64] full length SHA-512 hash of file contents>
|
* <[64] full length SHA-512 hash of file contents>
|
||||||
* <[4] 32-bit length of file in bytes>
|
* <[4] 32-bit length of file in bytes>
|
||||||
@ -636,6 +636,10 @@ public:
|
|||||||
* <[2] 16-bit length of filename>
|
* <[2] 16-bit length of filename>
|
||||||
* <[...] name of file being requested>
|
* <[...] name of file being requested>
|
||||||
*
|
*
|
||||||
|
* This is used for distribution of software updates and in the future may
|
||||||
|
* be used for anything else that needs to be globally distributed. It
|
||||||
|
* is not designed for end-user use for other purposes.
|
||||||
|
*
|
||||||
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
|
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
|
||||||
* not supported or enabled.
|
* not supported or enabled.
|
||||||
*/
|
*/
|
||||||
@ -657,6 +661,10 @@ public:
|
|||||||
* <[4] 32-bit index of desired chunk>
|
* <[4] 32-bit index of desired chunk>
|
||||||
* <[2] 16-bit length of desired chunk>
|
* <[2] 16-bit length of desired chunk>
|
||||||
*
|
*
|
||||||
|
* This is used for distribution of software updates and in the future may
|
||||||
|
* be used for anything else that needs to be globally distributed. It
|
||||||
|
* is not designed for end-user use for other purposes.
|
||||||
|
*
|
||||||
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
|
* Support is optional. Nodes should return UNSUPPORTED_OPERATION if
|
||||||
* not supported or enabled.
|
* not supported or enabled.
|
||||||
*/
|
*/
|
||||||
|
216
node/Updater.cpp
216
node/Updater.cpp
@ -31,6 +31,8 @@
|
|||||||
#include "Defaults.hpp"
|
#include "Defaults.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Topology.hpp"
|
#include "Topology.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
#include "SHA512.hpp"
|
||||||
|
|
||||||
#include "../version.h"
|
#include "../version.h"
|
||||||
|
|
||||||
@ -69,8 +71,9 @@ void Updater::refreshShared()
|
|||||||
if (Utils::readFile(nfoPath.c_str(),buf)) {
|
if (Utils::readFile(nfoPath.c_str(),buf)) {
|
||||||
Dictionary nfo(buf);
|
Dictionary nfo(buf);
|
||||||
|
|
||||||
_Shared shared;
|
SharedUpdate shared;
|
||||||
shared.filename = fullPath;
|
shared.fullPath = fullPath;
|
||||||
|
shared.filename = u->first;
|
||||||
|
|
||||||
std::string sha512(Utils::unhex(nfo.get("sha512",std::string())));
|
std::string sha512(Utils::unhex(nfo.get("sha512",std::string())));
|
||||||
if (sha512.length() < sizeof(shared.sha512)) {
|
if (sha512.length() < sizeof(shared.sha512)) {
|
||||||
@ -104,9 +107,7 @@ void Updater::refreshShared()
|
|||||||
}
|
}
|
||||||
shared.size = (unsigned long)fs;
|
shared.size = (unsigned long)fs;
|
||||||
|
|
||||||
Array<unsigned char,16> first16Bytes;
|
_sharedUpdates.push_back(shared);
|
||||||
memcpy(first16Bytes.data,sha512.data(),16);
|
|
||||||
_sharedUpdates[first16Bytes] = shared;
|
|
||||||
} else {
|
} else {
|
||||||
TRACE("skipped shareable update due to missing companion .nfo: %s",fullPath.c_str());
|
TRACE("skipped shareable update due to missing companion .nfo: %s",fullPath.c_str());
|
||||||
continue;
|
continue;
|
||||||
@ -127,9 +128,9 @@ void Updater::getUpdateIfThisIsNewer(unsigned int vMajor,unsigned int vMinor,uns
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string updateFilename(generateUpdateFilename());
|
std::string updateFilename(generateUpdateFilename(vMajor,vMinor,revision));
|
||||||
if (!updateFilename.length()) {
|
if (!updateFilename.length()) {
|
||||||
TRACE("a new update to %u.%u.%u is available, but this platform doesn't support auto updates",vMajor,vMinor,revision);
|
TRACE("an update to %u.%u.%u is available, but this platform or build doesn't support auto-update",vMajor,vMinor,revision);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,11 +139,8 @@ void Updater::getUpdateIfThisIsNewer(unsigned int vMajor,unsigned int vMinor,uns
|
|||||||
|
|
||||||
TRACE("new update available to %u.%u.%u, looking for %s from %u peers",vMajor,vMinor,revision,updateFilename.c_str(),(unsigned int)peers.size());
|
TRACE("new update available to %u.%u.%u, looking for %s from %u peers",vMajor,vMinor,revision,updateFilename.c_str(),(unsigned int)peers.size());
|
||||||
|
|
||||||
if (!peers.size())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for(std::vector< SharedPtr<Peer> >::iterator p(peers.begin());p!=peers.end();++p) {
|
for(std::vector< SharedPtr<Peer> >::iterator p(peers.begin());p!=peers.end();++p) {
|
||||||
Packet outp(p->address(),_r->identity.address(),Packet::VERB_FILE_INFO_REQUEST);
|
Packet outp((*p)->address(),_r->identity.address(),Packet::VERB_FILE_INFO_REQUEST);
|
||||||
outp.append((unsigned char)0);
|
outp.append((unsigned char)0);
|
||||||
outp.append((uint16_t)updateFilename.length());
|
outp.append((uint16_t)updateFilename.length());
|
||||||
outp.append(updateFilename.data(),updateFilename.length());
|
outp.append(updateFilename.data(),updateFilename.length());
|
||||||
@ -152,14 +150,167 @@ void Updater::getUpdateIfThisIsNewer(unsigned int vMajor,unsigned int vMinor,uns
|
|||||||
|
|
||||||
void Updater::retryIfNeeded()
|
void Updater::retryIfNeeded()
|
||||||
{
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
|
if (_download) {
|
||||||
|
uint64_t elapsed = Utils::now() - _download->lastChunkReceivedAt;
|
||||||
|
if ((elapsed >= ZT_UPDATER_PEER_TIMEOUT)||(!_download->currentlyReceivingFrom)) {
|
||||||
|
if (_download->peersThatHave.empty()) {
|
||||||
|
// Search for more sources if we have no more possibilities queued
|
||||||
|
_download->currentlyReceivingFrom.zero();
|
||||||
|
|
||||||
|
std::vector< SharedPtr<Peer> > peers;
|
||||||
|
_r->topology->eachPeer(Topology::CollectPeersWithActiveDirectPath(peers,Utils::now()));
|
||||||
|
|
||||||
|
for(std::vector< SharedPtr<Peer> >::iterator p(peers.begin());p!=peers.end();++p) {
|
||||||
|
Packet outp((*p)->address(),_r->identity.address(),Packet::VERB_FILE_INFO_REQUEST);
|
||||||
|
outp.append((unsigned char)0);
|
||||||
|
outp.append((uint16_t)_download->filename.length());
|
||||||
|
outp.append(_download->filename.data(),_download->filename.length());
|
||||||
|
_r->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If that peer isn't answering, try the next queued source
|
||||||
|
_download->currentlyReceivingFrom = _download->peersThatHave.front();
|
||||||
|
_download->peersThatHave.pop_front();
|
||||||
|
}
|
||||||
|
} else if (elapsed >= ZT_UPDATER_RETRY_TIMEOUT) {
|
||||||
|
// Re-request next chunk we don't have from current source
|
||||||
|
_requestNextChunk();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Updater::handleChunk(const void *sha512First16,unsigned long at,const void *chunk,unsigned long len)
|
void Updater::handleChunk(const Address &from,const void *sha512,unsigned int shalen,unsigned long at,const void *chunk,unsigned long len)
|
||||||
{
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
|
if (!_download) {
|
||||||
|
TRACE("got chunk from %s while no download is in progress, ignored",from.toString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(_download->sha512,sha512,(shalen > 64) ? 64 : shalen)) {
|
||||||
|
TRACE("got chunk from %s for wrong download (SHA mismatch), ignored",from.toString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long whichChunk = at / ZT_UPDATER_CHUNK_SIZE;
|
||||||
|
|
||||||
|
if (at != (ZT_UPDATER_CHUNK_SIZE * whichChunk))
|
||||||
|
return; // not at chunk boundary
|
||||||
|
if (whichChunk >= _download->haveChunks.size())
|
||||||
|
return; // overflow
|
||||||
|
if ((whichChunk == (_download->haveChunks.size() - 1))&&(len != _download->lastChunkSize))
|
||||||
|
return; // last chunk, size wrong
|
||||||
|
else if (len != ZT_UPDATER_CHUNK_SIZE)
|
||||||
|
return; // chunk size wrong
|
||||||
|
|
||||||
|
for(unsigned long i=0;i<len;++i)
|
||||||
|
_download->data[at + i] = ((const char *)chunk)[i];
|
||||||
|
|
||||||
|
_download->haveChunks[whichChunk] = true;
|
||||||
|
_download->lastChunkReceivedAt = Utils::now();
|
||||||
|
|
||||||
|
_requestNextChunk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Updater::handleAvailable(const Address &from,const char *filename,const void *sha512,unsigned long filesize,const Address &signedBy,const void *signature,unsigned int siglen)
|
||||||
|
{
|
||||||
|
unsigned int vMajor = 0,vMinor = 0,revision = 0;
|
||||||
|
if (!parseUpdateFilename(filename,vMajor,vMinor,revision)) {
|
||||||
|
TRACE("rejected offer of %s from %s: could not parse version information",filename,from.toString().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filesize > ZT_UPDATER_MAX_SUPPORTED_SIZE) {
|
||||||
|
TRACE("rejected offer of %s from %s: file too large (%u)",filename,from.toString().c_str(),(unsigned int)filesize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vMajor < ZEROTIER_ONE_VERSION_MAJOR)
|
||||||
|
return;
|
||||||
|
else if (vMajor == ZEROTIER_ONE_VERSION_MAJOR) {
|
||||||
|
if (vMinor < ZEROTIER_ONE_VERSION_MINOR)
|
||||||
|
return;
|
||||||
|
else if (vMinor == ZEROTIER_ONE_VERSION_MINOR) {
|
||||||
|
if (revision <= ZEROTIER_ONE_VERSION_REVISION)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
|
if (_download) {
|
||||||
|
// If a download is in progress, only accept this as another source if
|
||||||
|
// it matches the size, hash, and version. Also check if this is a newer
|
||||||
|
// version and if so replace download with this.
|
||||||
|
} else {
|
||||||
|
// If there is no download in progress, create one provided the signature
|
||||||
|
// for the SHA-512 hash verifies as being from a valid signer.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Updater::findSharedUpdate(const char *filename,SharedUpdate &update) const
|
||||||
|
{
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
for(std::list<SharedUpdate>::const_iterator u(_sharedUpdates.begin());u!=_sharedUpdates.end();++u) {
|
||||||
|
if (u->filename == filename) {
|
||||||
|
update = *u;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Updater::findSharedUpdate(const void *sha512,unsigned int shalen,SharedUpdate &update) const
|
||||||
|
{
|
||||||
|
if (!shalen)
|
||||||
|
return false;
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
for(std::list<SharedUpdate>::const_iterator u(_sharedUpdates.begin());u!=_sharedUpdates.end();++u) {
|
||||||
|
if (!memcmp(u->sha512,sha512,(shalen > 64) ? 64 : shalen)) {
|
||||||
|
update = *u;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Updater::getSharedChunk(const void *sha512,unsigned int shalen,unsigned long at,void *chunk,unsigned long chunklen) const
|
||||||
|
{
|
||||||
|
if (!chunklen)
|
||||||
|
return true;
|
||||||
|
if (!shalen)
|
||||||
|
return false;
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
for(std::list<SharedUpdate>::const_iterator u(_sharedUpdates.begin());u!=_sharedUpdates.end();++u) {
|
||||||
|
if (!memcmp(u->sha512,sha512,(shalen > 64) ? 64 : shalen)) {
|
||||||
|
FILE *f = fopen(u->fullPath.c_str(),"rb");
|
||||||
|
if (!f)
|
||||||
|
return false;
|
||||||
|
if (!fseek(f,(long)at,SEEK_SET)) {
|
||||||
|
fclose(f);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fread(chunk,chunklen,1,f) != 1) {
|
||||||
|
fclose(f);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Updater::generateUpdateFilename(unsigned int vMajor,unsigned int vMinor,unsigned int revision)
|
std::string Updater::generateUpdateFilename(unsigned int vMajor,unsigned int vMinor,unsigned int revision)
|
||||||
{
|
{
|
||||||
|
// Defining ZT_OFFICIAL_BUILD enables this cascade of macros, which will
|
||||||
|
// make your build auto-update itself if it's for an officially supported
|
||||||
|
// architecture. The signing identity for auto-updates is in Defaults.
|
||||||
|
#ifdef ZT_OFFICIAL_BUILD
|
||||||
|
|
||||||
// Not supported... yet? Get it first cause it might identify as Linux too.
|
// Not supported... yet? Get it first cause it might identify as Linux too.
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
#define _updSupported 1
|
#define _updSupported 1
|
||||||
@ -202,6 +353,10 @@ std::string Updater::generateUpdateFilename(unsigned int vMajor,unsigned int vMi
|
|||||||
#ifndef _updSupported
|
#ifndef _updSupported
|
||||||
return std::string();
|
return std::string();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
return std::string();
|
||||||
|
#endif // ZT_OFFICIAL_BUILD
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Updater::parseUpdateFilename(const char *filename,unsigned int &vMajor,unsigned int &vMinor,unsigned int &revision)
|
bool Updater::parseUpdateFilename(const char *filename,unsigned int &vMajor,unsigned int &vMinor,unsigned int &revision)
|
||||||
@ -218,5 +373,42 @@ bool Updater::parseUpdateFilename(const char *filename,unsigned int &vMajor,unsi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Updater::_requestNextChunk()
|
||||||
|
{
|
||||||
|
// assumes _lock is locked
|
||||||
|
|
||||||
|
if (!_download)
|
||||||
|
return;
|
||||||
|
|
||||||
|
unsigned long whichChunk = 0;
|
||||||
|
std::vector<bool>::iterator ptr(std::find(_download->haveChunks.begin(),_download->haveChunks.end(),false));
|
||||||
|
if (ptr == _download->haveChunks.end()) {
|
||||||
|
unsigned char digest[64];
|
||||||
|
SHA512::hash(digest,_download->data.data(),_download->data.length());
|
||||||
|
if (memcmp(digest,_download->sha512,64)) {
|
||||||
|
LOG("retrying download of %s -- SHA-512 mismatch, file corrupt!",_download->filename.c_str());
|
||||||
|
std::fill(_download->haveChunks.begin(),_download->haveChunks.end(),false);
|
||||||
|
whichChunk = 0;
|
||||||
|
} else {
|
||||||
|
LOG("successfully downloaded and authenticated %s, launching update...",_download->filename.c_str());
|
||||||
|
delete _download;
|
||||||
|
_download = (_Download *)0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
whichChunk = std::distance(_download->haveChunks.begin(),ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("requesting chunk %u/%u of %s from %s",(unsigned int)whichChunk,(unsigned int)_download->haveChunks.size(),_download->filename.c_str()_download->currentlyReceivingFrom.toString().c_str());
|
||||||
|
|
||||||
|
Packet outp(_download->currentlyReceivingFrom,_r->identity.address(),Packet::VERB_FILE_BLOCK_REQUEST);
|
||||||
|
outp.append(_download->sha512,16);
|
||||||
|
outp.append((uint32_t)(whichChunk * ZT_UPDATER_CHUNK_SIZE));
|
||||||
|
if (whichChunk == (_download->haveChunks.size() - 1))
|
||||||
|
outp.append((uint16_t)_download->lastChunkSize);
|
||||||
|
else outp.append((uint16_t)ZT_UPDATER_CHUNK_SIZE);
|
||||||
|
_r->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
||||||
|
138
node/Updater.hpp
138
node/Updater.hpp
@ -38,6 +38,7 @@
|
|||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
@ -55,10 +56,10 @@
|
|||||||
#define ZT_UPDATER_MAX_SUPPORTED_SIZE (1024 * 1024 * 16)
|
#define ZT_UPDATER_MAX_SUPPORTED_SIZE (1024 * 1024 * 16)
|
||||||
|
|
||||||
// Retry timeout in ms.
|
// Retry timeout in ms.
|
||||||
#define ZT_UPDATER_RETRY_TIMEOUT 30000
|
#define ZT_UPDATER_RETRY_TIMEOUT 15000
|
||||||
|
|
||||||
// After this long, look for a new set of peers that have the download shared.
|
// After this long, look for a new peer to download from
|
||||||
#define ZT_UPDATER_REPOLL_TIMEOUT 60000
|
#define ZT_UPDATER_PEER_TIMEOUT 65000
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
@ -67,10 +68,12 @@ class RuntimeEnvironment;
|
|||||||
/**
|
/**
|
||||||
* Software update downloader and executer
|
* Software update downloader and executer
|
||||||
*
|
*
|
||||||
* FYI: downloads occur via the protocol rather than out of band via http so
|
* Downloads occur via the ZT1 protocol rather than out of band via http so
|
||||||
* that ZeroTier One can be run in secure jailed environments where it is the
|
* that ZeroTier One can be run in secure jailed environments where it is the
|
||||||
* only protocol permitted over the "real" Internet. This is required for a
|
* only protocol permitted over the "real" Internet. This is wanted for a
|
||||||
* number of potentially popular use cases.
|
* number of potentially popular use cases, like private LANs that connect
|
||||||
|
* nodes in hostile environments or playing attack/defend on the future CTF
|
||||||
|
* network.
|
||||||
*
|
*
|
||||||
* The protocol is a simple chunk-pulling "trivial FTP" like thing that should
|
* The protocol is a simple chunk-pulling "trivial FTP" like thing that should
|
||||||
* be suitable for core engine software updates. Software updates themselves
|
* be suitable for core engine software updates. Software updates themselves
|
||||||
@ -84,6 +87,19 @@ class RuntimeEnvironment;
|
|||||||
class Updater
|
class Updater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Contains information about a shared update available to other peers
|
||||||
|
*/
|
||||||
|
struct SharedUpdate
|
||||||
|
{
|
||||||
|
std::string fullPath;
|
||||||
|
std::string filename;
|
||||||
|
unsigned char sha512[64];
|
||||||
|
C25519::Signature sig;
|
||||||
|
Address signedBy;
|
||||||
|
unsigned long size;
|
||||||
|
};
|
||||||
|
|
||||||
Updater(const RuntimeEnvironment *renv);
|
Updater(const RuntimeEnvironment *renv);
|
||||||
~Updater();
|
~Updater();
|
||||||
|
|
||||||
@ -108,18 +124,72 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called periodically from main loop
|
* Called periodically from main loop
|
||||||
|
*
|
||||||
|
* This retries downloads if they're stalled and performs other cleanup.
|
||||||
*/
|
*/
|
||||||
void retryIfNeeded();
|
void retryIfNeeded();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a chunk is received
|
* Called when a chunk is received
|
||||||
*
|
*
|
||||||
* @param sha512First16 First 16 bytes of SHA-512 hash
|
* If the chunk is a final chunk and we now have an update, this may result
|
||||||
|
* in the commencement of the update process and the shutdown of ZT1.
|
||||||
|
*
|
||||||
|
* @param from Originating peer
|
||||||
|
* @param sha512 Up to 64 bytes of hash to match
|
||||||
|
* @param shalen Length of sha512[]
|
||||||
* @param at Position of chunk
|
* @param at Position of chunk
|
||||||
* @param chunk Chunk data
|
* @param chunk Chunk data
|
||||||
* @param len Length of chunk
|
* @param len Length of chunk
|
||||||
*/
|
*/
|
||||||
void handleChunk(const void *sha512First16,unsigned long at,const void *chunk,unsigned long len);
|
void handleChunk(const Address &from,const void *sha512,unsigned int shalen,unsigned long at,const void *chunk,unsigned long len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a reply to a search for an update is received
|
||||||
|
*
|
||||||
|
* This checks SHA-512 hash signature and version as parsed from filename
|
||||||
|
* before starting the transfer.
|
||||||
|
*
|
||||||
|
* @param from Node that sent reply saying it has the file
|
||||||
|
* @param filename Name of file (can be parsed for version info)
|
||||||
|
* @param sha512 64-byte SHA-512 hash of file's contents
|
||||||
|
* @param filesize Size of file in bytes
|
||||||
|
* @param signedBy Address of signer of hash
|
||||||
|
* @param signature Signature (currently must be Ed25519)
|
||||||
|
* @param siglen Length of signature in bytes
|
||||||
|
*/
|
||||||
|
void handleAvailable(const Address &from,const char *filename,const void *sha512,unsigned long filesize,const Address &signedBy,const void *signature,unsigned int siglen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data about a shared update if found
|
||||||
|
*
|
||||||
|
* @param filename File name
|
||||||
|
* @param update Empty structure to be filled with update info
|
||||||
|
* @return True if found (if false, 'update' is unmodified)
|
||||||
|
*/
|
||||||
|
bool findSharedUpdate(const char *filename,SharedUpdate &update) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data about a shared update if found
|
||||||
|
*
|
||||||
|
* @param sha512 Up to 64 bytes of hash to match
|
||||||
|
* @param shalen Length of sha512[]
|
||||||
|
* @param update Empty structure to be filled with update info
|
||||||
|
* @return True if found (if false, 'update' is unmodified)
|
||||||
|
*/
|
||||||
|
bool findSharedUpdate(const void *sha512,unsigned int shalen,SharedUpdate &update) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a chunk of a shared update
|
||||||
|
*
|
||||||
|
* @param sha512 Up to 64 bytes of hash to match
|
||||||
|
* @param shalen Length of sha512[]
|
||||||
|
* @param at Position in file
|
||||||
|
* @param chunk Buffer to store data
|
||||||
|
* @param chunklen Number of bytes to get
|
||||||
|
* @return True if chunk[] was successfully filled, false if not found or other error
|
||||||
|
*/
|
||||||
|
bool getSharedChunk(const void *sha512,unsigned int shalen,unsigned long at,void *chunk,unsigned long chunklen) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Canonical update filename for this platform or empty string if unsupported
|
* @return Canonical update filename for this platform or empty string if unsupported
|
||||||
@ -135,48 +205,13 @@ public:
|
|||||||
static bool parseUpdateFilename(const char *filename,unsigned int &vMajor,unsigned int &vMinor,unsigned int &revision);
|
static bool parseUpdateFilename(const char *filename,unsigned int &vMajor,unsigned int &vMinor,unsigned int &revision);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void _requestNextChunk();
|
||||||
|
|
||||||
struct _Download
|
struct _Download
|
||||||
{
|
{
|
||||||
_Download(const void *s512,const std::string &fn,unsigned long len,unsigned int vMajor,unsigned int vMinor,unsigned int rev)
|
|
||||||
{
|
|
||||||
data.resize(len);
|
|
||||||
haveChunks.resize((len / ZT_UPDATER_CHUNK_SIZE) + 1,false);
|
|
||||||
filename = fn;
|
|
||||||
memcpy(sha512,s512,64);
|
|
||||||
lastChunkSize = len % ZT_UPDATER_CHUNK_SIZE;
|
|
||||||
versionMajor = vMajor;
|
|
||||||
versionMinor = vMinor;
|
|
||||||
revision = rev;
|
|
||||||
}
|
|
||||||
|
|
||||||
long nextChunk() const
|
|
||||||
{
|
|
||||||
std::vector<bool>::const_iterator ptr(std::find(haveChunks.begin(),haveChunks.end(),false));
|
|
||||||
if (ptr != haveChunks.end())
|
|
||||||
return std::distance(haveChunks.begin(),ptr);
|
|
||||||
else return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool gotChunk(unsigned long at,const void *chunk,unsigned long len)
|
|
||||||
{
|
|
||||||
unsigned long whichChunk = at / ZT_UPDATER_CHUNK_SIZE;
|
|
||||||
if (at != (ZT_UPDATER_CHUNK_SIZE * whichChunk))
|
|
||||||
return false; // not at chunk boundary
|
|
||||||
if (whichChunk >= haveChunks.size())
|
|
||||||
return false; // overflow
|
|
||||||
if ((whichChunk == (haveChunks.size() - 1))&&(len != lastChunkSize))
|
|
||||||
return false; // last chunk, size wrong
|
|
||||||
else if (len != ZT_UPDATER_CHUNK_SIZE)
|
|
||||||
return false; // chunk size wrong
|
|
||||||
for(unsigned long i=0;i<len;++i)
|
|
||||||
data[at + i] = ((const char *)chunk)[i];
|
|
||||||
haveChunks[whichChunk] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string data;
|
std::string data;
|
||||||
std::vector<bool> haveChunks;
|
std::vector<bool> haveChunks;
|
||||||
std::vector<Address> peersThatHave;
|
std::list<Address> peersThatHave; // excluding current
|
||||||
std::string filename;
|
std::string filename;
|
||||||
unsigned char sha512[64];
|
unsigned char sha512[64];
|
||||||
Address currentlyReceivingFrom;
|
Address currentlyReceivingFrom;
|
||||||
@ -185,18 +220,9 @@ private:
|
|||||||
unsigned int versionMajor,versionMinor,revision;
|
unsigned int versionMajor,versionMinor,revision;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _Shared
|
|
||||||
{
|
|
||||||
std::string filename;
|
|
||||||
unsigned char sha512[64];
|
|
||||||
C25519::Signature sig;
|
|
||||||
Address signedBy;
|
|
||||||
unsigned long size;
|
|
||||||
};
|
|
||||||
|
|
||||||
const RuntimeEnvironment *_r;
|
const RuntimeEnvironment *_r;
|
||||||
_Download *_download;
|
_Download *_download;
|
||||||
std::map< Array<unsigned char,16>,_Shared > _sharedUpdates;
|
std::list<SharedUpdate> _sharedUpdates; // usually not more than 1 or 2 of these
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -265,7 +265,7 @@ uint64_t Utils::getLastModified(const char *path)
|
|||||||
return (((uint64_t)s.st_mtime) * 1000ULL);
|
return (((uint64_t)s.st_mtime) * 1000ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t getFileSize(const char *path)
|
int64_t Utils::getFileSize(const char *path)
|
||||||
{
|
{
|
||||||
struct stat s;
|
struct stat s;
|
||||||
if (stat(path,&s))
|
if (stat(path,&s))
|
||||||
|
Loading…
Reference in New Issue
Block a user