/*
* 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_SOFTWAREUPDATER_HPP
#define ZT_SOFTWAREUPDATER_HPP
#include
#include
#include "Constants.hpp"
#include "Mutex.hpp"
#include "Utils.hpp"
#include "HttpClient.hpp"
#include "Defaults.hpp"
#include "Address.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);
}
}
}
}
/**
* Check for updates now regardless of last check time or version
*
* This only starts a check if one is not in progress. Otherwise it does
* nothing.
*/
inline void checkNow()
{
Mutex::Lock _l(_lock);
if (_status == UPDATE_STATUS_IDLE) {
_lastUpdateAttempt = Utils::now();
_status = UPDATE_STATUS_GETTING_NFO;
HttpClient::GET(ZT_DEFAULTS.updateLatestNfoURL,HttpClient::NO_HEADERS,ZT_UPDATE_HTTP_TIMEOUT,&_cbHandleGetLatestVersionInfo,this);
}
}
/**
* Check for updates now if it's been longer than ZT_UPDATE_MAX_INTERVAL
*
* This is called periodically from the main loop.
*/
inline void checkIfMaxIntervalExceeded(uint64_t now)
{
if ((now - _lastUpdateAttempt) >= ZT_UPDATE_MAX_INTERVAL)
checkNow();
}
/**
* 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)
* @return Version packed into an easily comparable 64-bit integer
*/
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) );
}
/**
* Parse NFO data from .nfo file on software update site
*
* The first argument is the NFO data, and all the remaining arguments are
* result parameters to be filled with results. If an error is returned the
* results in the parameters should be considered undefined.
*
* @param nfo NFO data
* @param vMajor Result: major version
* @param vMinor Result: minor version
* @param vRevision Result: revision number
* @param signedBy Result: signing identity
* @param signature Result: Ed25519 signature data
* @param url Result: URL of update binary
* @return NULL on success or error message on failure
*/
static const char *parseNfo(
const char *nfoText,
unsigned int &vMajor,
unsigned int &vMinor,
unsigned int &vRevision,
Address &signedBy,
std::string &signature,
std::string &url);
/**
* Validate an update once downloaded
*
* This obtains the identity corresponding to the address from the compiled-in
* list of valid signing identities.
*
* @param data Update data
* @param len Length of update data
* @param signedBy Signing authority address
* @param signature Signing authority signature
* @return True on validation success, false if rejected
*/
static bool validateUpdate(
const void *data,
unsigned int len,
const Address &signedBy,
const std::string &signature);
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