mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-02-20 17:52:46 +00:00
Rest of software updater, ready to test...
This commit is contained in:
parent
612c17240a
commit
bf0da9f2f7
17
main.cpp
17
main.cpp
@ -44,6 +44,7 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
@ -473,13 +474,21 @@ int main(int argc,char **argv)
|
||||
|
||||
try {
|
||||
node = new Node(homeDir,port,controlPort);
|
||||
const char *termReason = (char *)0;
|
||||
switch(node->run()) {
|
||||
case Node::NODE_UNRECOVERABLE_ERROR:
|
||||
case Node::NODE_NODE_RESTART_FOR_UPGRADE: {
|
||||
#ifdef __UNIX_LIKE__
|
||||
const char *upgPath = node->reasonForTermination();
|
||||
if (upgPath)
|
||||
execl(upgPath,upgPath,"-s",(char *)0); // -s = (re)start after install/upgrade
|
||||
exitCode = -1;
|
||||
termReason = node->reasonForTermination();
|
||||
fprintf(stderr,"%s: abnormal termination: unable to execute update at %s",argv[0],(upgPath) ? upgPath : "(unknown path)");
|
||||
#endif
|
||||
} break;
|
||||
case Node::NODE_UNRECOVERABLE_ERROR: {
|
||||
exitCode = -1;
|
||||
const char *termReason = node->reasonForTermination();
|
||||
fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
|
||||
break;
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -330,4 +330,14 @@ error_no_byte_order_defined;
|
||||
*/
|
||||
#define ZT_RENDEZVOUS_NAT_T_DELAY 500
|
||||
|
||||
/**
|
||||
* Minimum interval between attempts to do a software update
|
||||
*/
|
||||
#define ZT_UPDATE_MIN_INTERVAL 120000
|
||||
|
||||
/**
|
||||
* Update HTTP timeout in seconds
|
||||
*/
|
||||
#define ZT_UPDATE_HTTP_TIMEOUT 30
|
||||
|
||||
#endif
|
||||
|
@ -122,13 +122,18 @@ static inline std::map< Address,Identity > _mkUpdateAuth()
|
||||
return ua;
|
||||
}
|
||||
|
||||
static inline std::string _mkUpdateUrl()
|
||||
{
|
||||
}
|
||||
|
||||
Defaults::Defaults() :
|
||||
#ifdef ZT_TRACE_MULTICAST
|
||||
multicastTraceWatcher(ZT_TRACE_MULTICAST),
|
||||
#endif
|
||||
defaultHomePath(_mkDefaultHomePath()),
|
||||
supernodes(_mkSupernodeMap()),
|
||||
updateAuthorities(_mkUpdateAuth())
|
||||
updateAuthorities(_mkUpdateAuth()),
|
||||
updateLatestNfoURL(_mkUpdateUrl())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,11 @@ public:
|
||||
* build will not auto-update.
|
||||
*/
|
||||
const std::map< Address,Identity > updateAuthorities;
|
||||
|
||||
/**
|
||||
* URL to latest .nfo for software updates
|
||||
*/
|
||||
const std::string updateLatestNfoURL;
|
||||
};
|
||||
|
||||
extern const Defaults ZT_DEFAULTS;
|
||||
|
@ -112,6 +112,12 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_url.length()) {
|
||||
_handler(_arg,-1,_url,false,"cannot fetch empty URL");
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
curlArgs[0] = const_cast <char *>(curlPath.c_str());
|
||||
curlArgs[1] = const_cast <char *>("-D");
|
||||
curlArgs[2] = const_cast <char *>("-"); // append headers before output
|
||||
@ -171,9 +177,11 @@ public:
|
||||
|
||||
if (FD_ISSET(curlStdout[0],&readfds)) {
|
||||
int n = (int)::read(curlStdout[0],buf,sizeof(buf));
|
||||
if (n > 0)
|
||||
if (n > 0) {
|
||||
_body.append(buf,n);
|
||||
else if (n < 0)
|
||||
// Reset timeout when data is read...
|
||||
timesOutAt = Utils::now() + ((unsigned long long)_timeout * 1000ULL);
|
||||
} else if (n < 0)
|
||||
break;
|
||||
if (_body.length() > CURL_MAX_MESSAGE_LENGTH) {
|
||||
::kill(pid,SIGKILL);
|
||||
|
@ -97,9 +97,24 @@ public:
|
||||
*/
|
||||
enum ReasonForTermination
|
||||
{
|
||||
/**
|
||||
* Node is currently in run()
|
||||
*/
|
||||
NODE_RUNNING = 0,
|
||||
|
||||
/**
|
||||
* Node is shutting down for normal reasons, including a signal
|
||||
*/
|
||||
NODE_NORMAL_TERMINATION = 1,
|
||||
NODE_RESTART_FOR_RECONFIGURATION = 2,
|
||||
|
||||
/**
|
||||
* An upgrade is available. Its path is in reasonForTermination().
|
||||
*/
|
||||
NODE_RESTART_FOR_UPGRADE = 2,
|
||||
|
||||
/**
|
||||
* A serious unrecoverable error has occurred.
|
||||
*/
|
||||
NODE_UNRECOVERABLE_ERROR = 3
|
||||
};
|
||||
|
||||
|
@ -46,7 +46,7 @@ class CMWC4096;
|
||||
class Service;
|
||||
class Node;
|
||||
class Multicaster;
|
||||
class Updater;
|
||||
class SoftwareUpdater;
|
||||
|
||||
/**
|
||||
* Holds global state for an instance of ZeroTier::Node
|
||||
@ -73,7 +73,7 @@ public:
|
||||
topology((Topology *)0),
|
||||
sysEnv((SysEnv *)0),
|
||||
nc((NodeConfig *)0),
|
||||
updater((Updater *)0)
|
||||
updater((SoftwareUpdater *)0)
|
||||
#ifndef __WINDOWS__
|
||||
,netconfService((Service *)0)
|
||||
#endif
|
||||
@ -110,7 +110,7 @@ public:
|
||||
SysEnv *sysEnv;
|
||||
NodeConfig *nc;
|
||||
Node *node;
|
||||
Updater *updater; // null if auto-updates are disabled
|
||||
SoftwareUpdater *updater; // null if software updates are not enabled
|
||||
#ifndef __WINDOWS__
|
||||
Service *netconfService; // null if no netconf service running
|
||||
#endif
|
||||
|
187
node/SoftwareUpdater.cpp
Normal file
187
node/SoftwareUpdater.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../version.h"
|
||||
|
||||
#include "SoftwareUpdater.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Logger.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "Node.hpp"
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
SoftwareUpdater::SoftwareUpdater(const RuntimeEnvironment *renv) :
|
||||
_r(renv),
|
||||
_myVersion(packVersion(ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION)),
|
||||
_lastUpdateAttempt(0),
|
||||
_status(UPDATE_STATUS_IDLE),
|
||||
_die(false),
|
||||
_lock()
|
||||
{
|
||||
}
|
||||
|
||||
SoftwareUpdater::~SoftwareUpdater()
|
||||
{
|
||||
_die = true;
|
||||
for(;;) {
|
||||
_lock.lock();
|
||||
bool ip = (_status != UPDATE_STATUS_IDLE);
|
||||
_lock.unlock();
|
||||
if (ip)
|
||||
Thread::sleep(500);
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareUpdater::_cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,bool onDisk,const std::string &body)
|
||||
{
|
||||
SoftwareUpdater *upd = (SoftwareUpdater *)arg;
|
||||
const RuntimeEnvironment *_r = (const RuntimeEnvironment *)upd->_r;
|
||||
Mutex::Lock _l(upd->_lock);
|
||||
|
||||
if ((upd->_die)||(upd->_status != UPDATE_STATUS_GETTING_NFO)) {
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (code != 200) {
|
||||
LOG("unable to check for software updates, response code %d (%s)",code,body.c_str());
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Dictionary nfo(body);
|
||||
const unsigned int vMajor = Utils::strToUInt(nfo.get("vMajor").c_str());
|
||||
const unsigned int vMinor = Utils::strToUInt(nfo.get("vMinor").c_str());
|
||||
const unsigned int vRevision = Utils::strToUInt(nfo.get("vRevision").c_str());
|
||||
const Address signedBy(nfo.get("signedBy"));
|
||||
const std::string signature(Utils::unhex(nfo.get("ed25519")));
|
||||
const std::string &url = nfo.get("url");
|
||||
|
||||
if (signature.length() != ZT_C25519_SIGNATURE_LEN) {
|
||||
LOG("software update aborted: .nfo file invalid: bad Ed25519 signature");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
if ((url.length() <= 7)||(url.substr(0,7) != "http://")) {
|
||||
LOG("software update aborted: .nfo file invalid: update URL must begin with http://");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
if (packVersion(vMajor,vMinor,vRevision) <= upd->_myVersion) {
|
||||
LOG("software update aborted: .nfo file invalid: version on web site <= my version");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ZT_DEFAULTS.updateAuthorities.count(signedBy)) {
|
||||
LOG("software update aborted: .nfo file specifies unknown signing authority");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
upd->_status = UPDATE_STATUS_GETTING_FILE;
|
||||
upd->_signedBy = signedBy;
|
||||
upd->_signature = signature;
|
||||
|
||||
HttpClient::GET(url,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionBinary,arg);
|
||||
} catch ( ... ) {
|
||||
LOG("software update check failed: .nfo file invalid: fields missing or invalid dictionary format");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void SoftwareUpdater::_cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,bool onDisk,const std::string &body)
|
||||
{
|
||||
SoftwareUpdater *upd = (SoftwareUpdater *)arg;
|
||||
const RuntimeEnvironment *_r = (const RuntimeEnvironment *)upd->_r;
|
||||
Mutex::Lock _l(upd->_lock);
|
||||
|
||||
std::map< Address,Identity >::const_iterator updateAuthority = ZT_DEFAULTS.updateAuthorities.find(upd->_signedBy);
|
||||
if (updateAuthority == ZT_DEFAULTS.updateAuthorities.end()) { // sanity check, shouldn't happen
|
||||
LOG("software update aborted: .nfo file specifies unknown signing authority");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
// The all-important authenticity check... :)
|
||||
if (!updateAuthority->second.verify(body.data(),body.length(),upd->_signature.data(),upd->_signature.length())) {
|
||||
LOG("software update aborted: update fetched from '%s' failed certificate check against signer %s",url.c_str(),updateAuthority->first.toString().c_str());
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __UNIX_LIKE__
|
||||
size_t lastSlash = url.rfind('/');
|
||||
if (lastSlash == std::string::npos) { // sanity check, shouldn't happen
|
||||
LOG("software update aborted: invalid URL");
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
std::string updatesDir(_r->homePath + ZT_PATH_SEPARATOR_S + "updates.d");
|
||||
std::string updatePath(updatesDir + ZT_PATH_SEPARATOR_S + url.substr(lastSlash + 1));
|
||||
mkdir(updatesDir.c_str(),0755);
|
||||
|
||||
int fd = ::open(updatePath.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0755);
|
||||
if (fd <= 0) {
|
||||
LOG("software update aborted: unable to open %s for writing",updatePath.c_str());
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
if ((long)::write(fd,body.data(),body.length()) != (long)body.length()) {
|
||||
LOG("software update aborted: unable to write to %s",updatePath.c_str());
|
||||
upd->_status = UPDATE_STATUS_IDLE;
|
||||
return;
|
||||
}
|
||||
::close(fd);
|
||||
::chmod(updatePath.c_str(),0755);
|
||||
|
||||
_r->node->terminate(Node::NODE_RESTART_FOR_UPGRADE,updatePath.c_str());
|
||||
#endif
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
todo;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
110
node/SoftwareUpdater.hpp
Normal file
110
node/SoftwareUpdater.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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_SOFTWAREUPDATER_HPP
|
||||
#define ZT_SOFTWAREUPDATER_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "HttpClient.hpp"
|
||||
#include "Defaults.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Software updater
|
||||
*/
|
||||
class SoftwareUpdater
|
||||
{
|
||||
public:
|
||||
SoftwareUpdater(const RuntimeEnvironment *renv);
|
||||
~SoftwareUpdater();
|
||||
|
||||
/**
|
||||
* Called on each version message from a peer
|
||||
*
|
||||
* If a peer has a newer version, that causes an update to be started.
|
||||
*
|
||||
* @param vmaj Peer's major version
|
||||
* @param vmin Peer's minor version
|
||||
* @param rev Peer's revision
|
||||
*/
|
||||
inline void sawRemoteVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev)
|
||||
{
|
||||
const uint64_t tmp = packVersion(vmaj,vmin,rev);
|
||||
if (tmp > _myVersion) {
|
||||
Mutex::Lock _l(_lock);
|
||||
if ((_status == UPDATE_STATUS_IDLE)&&(!_die)&&(ZT_DEFAULTS.updateLatestNfoURL.length())) {
|
||||
const uint64_t now = Utils::now();
|
||||
if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MIN_INTERVAL) {
|
||||
_lastUpdateAttempt = now;
|
||||
_status = UPDATE_STATUS_GETTING_NFO;
|
||||
HttpClient::GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack three-component version into a 64-bit integer
|
||||
*
|
||||
* @param vmaj Major version (0..65535)
|
||||
* @param vmin Minor version (0..65535)
|
||||
* @param rev Revision (0..65535)
|
||||
*/
|
||||
static inline uint64_t packVersion(unsigned int vmaj,unsigned int vmin,unsigned int rev)
|
||||
throw()
|
||||
{
|
||||
return ( ((uint64_t)(vmaj & 0xffff) << 32) | ((uint64_t)(vmin & 0xffff) << 16) | (uint64_t)(rev & 0xffff) );
|
||||
}
|
||||
|
||||
private:
|
||||
static void _cbHandleGetLatestVersionInfo(void *arg,int code,const std::string &url,bool onDisk,const std::string &body);
|
||||
static void _cbHandleGetLatestVersionBinary(void *arg,int code,const std::string &url,bool onDisk,const std::string &body);
|
||||
|
||||
const RuntimeEnvironment *_r;
|
||||
const uint64_t _myVersion;
|
||||
volatile uint64_t _lastUpdateAttempt;
|
||||
volatile enum {
|
||||
UPDATE_STATUS_IDLE,
|
||||
UPDATE_STATUS_GETTING_NFO,
|
||||
UPDATE_STATUS_GETTING_FILE
|
||||
} _status;
|
||||
volatile bool _die;
|
||||
Address _signedBy;
|
||||
std::string _signature;
|
||||
Mutex _lock;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
@ -21,6 +21,7 @@ OBJS=\
|
||||
node/Poly1305.o \
|
||||
node/Salsa20.o \
|
||||
node/Service.o \
|
||||
node/SoftwareUpdater.o \
|
||||
node/SHA512.o \
|
||||
node/Switch.o \
|
||||
node/SysEnv.o \
|
||||
|
Loading…
x
Reference in New Issue
Block a user