diff options
Diffstat (limited to 'node')
50 files changed, 1027 insertions, 456 deletions
diff --git a/node/Address.hpp b/node/Address.hpp index b28284b0..7247260c 100644 --- a/node/Address.hpp +++ b/node/Address.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_ADDRESS_HPP -#define _ZT_ADDRESS_HPP +#ifndef ZT_ADDRESS_HPP +#define ZT_ADDRESS_HPP #include <stdio.h> #include <stdlib.h> diff --git a/node/Array.hpp b/node/Array.hpp index d48c2f52..c31626b2 100644 --- a/node/Array.hpp +++ b/node/Array.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_ARRAY_HPP -#define _ZT_ARRAY_HPP +#ifndef ZT_ARRAY_HPP +#define ZT_ARRAY_HPP #include <string> #include <algorithm> diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp index ebc70817..1aecaa65 100644 --- a/node/AtomicCounter.hpp +++ b/node/AtomicCounter.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_ATOMICCOUNTER_HPP -#define _ZT_ATOMICCOUNTER_HPP +#ifndef ZT_ATOMICCOUNTER_HPP +#define ZT_ATOMICCOUNTER_HPP #include "Mutex.hpp" #include "NonCopyable.hpp" diff --git a/node/BandwidthAccount.hpp b/node/BandwidthAccount.hpp index be180cfc..98c7dd20 100644 --- a/node/BandwidthAccount.hpp +++ b/node/BandwidthAccount.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_BWACCOUNT_HPP -#define _ZT_BWACCOUNT_HPP +#ifndef ZT_BWACCOUNT_HPP +#define ZT_BWACCOUNT_HPP #include <stdint.h> #include <math.h> diff --git a/node/Buffer.hpp b/node/Buffer.hpp index 1767ae04..e8308306 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_BUFFER_HPP -#define _ZT_BUFFER_HPP +#ifndef ZT_BUFFER_HPP +#define ZT_BUFFER_HPP #include <string.h> #include <stdint.h> diff --git a/node/C25519.hpp b/node/C25519.hpp index 79edfa06..2a594f72 100644 --- a/node/C25519.hpp +++ b/node/C25519.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_C25519_HPP -#define _ZT_C25519_HPP +#ifndef ZT_C25519_HPP +#define ZT_C25519_HPP #include "Array.hpp" #include "Utils.hpp" diff --git a/node/CMWC4096.hpp b/node/CMWC4096.hpp index 29351861..01c57e15 100644 --- a/node/CMWC4096.hpp +++ b/node/CMWC4096.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_CMWC4096_HPP -#define _ZT_CMWC4096_HPP +#ifndef ZT_CMWC4096_HPP +#define ZT_CMWC4096_HPP #include <stdint.h> #include "Utils.hpp" diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index 76e1cfbc..6f78734e 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_CERTIFICATEOFMEMBERSHIP_HPP -#define _ZT_CERTIFICATEOFMEMBERSHIP_HPP +#ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP +#define ZT_CERTIFICATEOFMEMBERSHIP_HPP #include <stdint.h> #include <string.h> diff --git a/node/Condition.hpp b/node/Condition.hpp index 4b2d32ca..728799d9 100644 --- a/node/Condition.hpp +++ b/node/Condition.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_CONDITION_HPP -#define _ZT_CONDITION_HPP +#ifndef ZT_CONDITION_HPP +#define ZT_CONDITION_HPP #include "Constants.hpp" #include "NonCopyable.hpp" diff --git a/node/Constants.hpp b/node/Constants.hpp index 8e252f2a..21c8a0ec 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_CONSTANTS_HPP -#define _ZT_CONSTANTS_HPP +#ifndef ZT_CONSTANTS_HPP +#define ZT_CONSTANTS_HPP // // This include file also auto-detects and canonicalizes some environment @@ -54,6 +54,7 @@ // OSX and iOS are unix-like OSes far as we're concerned #ifdef __APPLE__ +#include <TargetConditionals.h> #ifndef __UNIX_LIKE__ #define __UNIX_LIKE__ #endif @@ -329,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 diff --git a/node/Defaults.cpp b/node/Defaults.cpp index 35a677f2..2588c85f 100644 --- a/node/Defaults.cpp +++ b/node/Defaults.cpp @@ -98,12 +98,60 @@ static inline std::string _mkDefaultHomePath() #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; +} + +static inline const char *_mkUpdateUrl() +{ +#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) + if (sizeof(void *) == 8) + return "http://download.zerotier.com/update/linux/x64/latest.nfo"; + else return "http://download.zerotier.com/update/linux/x86/latest.nfo"; +#define GOT_UPDATE_URL +#endif + +#ifdef __APPLE__ + // TODO: iOS? + return "http://download.zerotier.com/update/mac/combined/latest.nfo"; +#define GOT_UPDATE_URL +#endif + + // TODO: Windows + +#ifndef GOT_UPDATE_URL + return ""; +#endif +} + Defaults::Defaults() : #ifdef ZT_TRACE_MULTICAST multicastTraceWatcher(ZT_TRACE_MULTICAST), #endif defaultHomePath(_mkDefaultHomePath()), - supernodes(_mkSupernodeMap()) + supernodes(_mkSupernodeMap()), + updateAuthorities(_mkUpdateAuth()), + updateLatestNfoURL(_mkUpdateUrl()) { } diff --git a/node/Defaults.hpp b/node/Defaults.hpp index 5f870194..9d6d4bcf 100644 --- a/node/Defaults.hpp +++ b/node/Defaults.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_DEFAULTS_HPP -#define _ZT_DEFAULTS_HPP +#ifndef ZT_DEFAULTS_HPP +#define ZT_DEFAULTS_HPP #include <stdexcept> #include <string> @@ -67,6 +67,22 @@ public: * Supernodes on the ZeroTier network */ const std::map< Identity,std::vector<InetAddress> > supernodes; + + /** + * 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; + + /** + * URL to latest .nfo for software updates + */ + const std::string updateLatestNfoURL; }; extern const Defaults ZT_DEFAULTS; diff --git a/node/Demarc.hpp b/node/Demarc.hpp index fc283fef..0bbdef44 100644 --- a/node/Demarc.hpp +++ b/node/Demarc.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_DEMARC_HPP -#define _ZT_DEMARC_HPP +#ifndef ZT_DEMARC_HPP +#define ZT_DEMARC_HPP #include <stdlib.h> #include <stdint.h> diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index a0a64cec..214c0094 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_DICTIONARY_HPP -#define _ZT_DICTIONARY_HPP +#ifndef ZT_DICTIONARY_HPP +#define ZT_DICTIONARY_HPP #include <string> #include <map> diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index fadd8e50..44118c2a 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -693,7 +693,7 @@ void EthernetTap::threadMain() // data until we have at least a frame. r += n; if (r > 14) { - if (r > (_mtu + 14)) // sanity check for weird TAP behavior on some platforms + if (r > ((int)_mtu + 14)) // sanity check for weird TAP behavior on some platforms r = _mtu + 14; for(int i=0;i<6;++i) to.data[i] = (unsigned char)getBuf[i]; diff --git a/node/EthernetTap.hpp b/node/EthernetTap.hpp index 3db41392..68a365bf 100644 --- a/node/EthernetTap.hpp +++ b/node/EthernetTap.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_ETHERNETTAP_HPP -#define _ZT_ETHERNETTAP_HPP +#ifndef ZT_ETHERNETTAP_HPP +#define ZT_ETHERNETTAP_HPP #include <stdio.h> #include <stdlib.h> diff --git a/node/HttpClient.cpp b/node/HttpClient.cpp new file mode 100644 index 00000000..d4e76018 --- /dev/null +++ b/node/HttpClient.cpp @@ -0,0 +1,311 @@ +/* + * 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 <vector> +#include <utility> +#include <algorithm> + +#include "Constants.hpp" +#include "HttpClient.hpp" +#include "Thread.hpp" +#include "Utils.hpp" +#include "NonCopyable.hpp" +#include "Defaults.hpp" + +#ifdef __UNIX_LIKE__ +#include <unistd.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/select.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/wait.h> +#endif + +namespace ZeroTier { + +const std::map<std::string,std::string> HttpClient::NO_HEADERS; + +#ifdef __UNIX_LIKE__ + +// The *nix implementation calls 'curl' externally rather than linking to it. +// This makes it an optional dependency that can be avoided in tiny systems +// provided you don't want to have automatic software updates... or want to +// do them via another method. + +#ifdef __APPLE__ +// TODO: get proxy configuration +#endif + +// Paths where "curl" may be found on the system +#define NUM_CURL_PATHS 5 +static const char *CURL_PATHS[NUM_CURL_PATHS] = { "/usr/bin/curl","/bin/curl","/usr/local/bin/curl","/usr/sbin/curl","/sbin/curl" }; + +// Maximum message length +#define CURL_MAX_MESSAGE_LENGTH (1024 * 1024 * 64) + +// Internal private thread class that performs request, notifies handler, +// and then commits suicide by deleting itself. +class P_Req : NonCopyable +{ +public: + P_Req(const char *method,const std::string &url,const std::map<std::string,std::string> &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,bool,const std::string &),void *arg) : + _url(url), + _headers(headers), + _timeout(timeout), + _handler(handler), + _arg(arg) + { + _myThread = Thread::start(this); + } + + void threadMain() + { + char *curlArgs[1024]; + char buf[16384]; + fd_set readfds,writefds,errfds; + struct timeval tv; + + std::string curlPath; + for(int i=0;i<NUM_CURL_PATHS;++i) { + if (Utils::fileExists(CURL_PATHS[i])) { + curlPath = CURL_PATHS[i]; + break; + } + } + if (!curlPath.length()) { + _handler(_arg,-1,_url,false,"unable to locate 'curl' binary in /usr/bin, /bin, /usr/local/bin, /usr/sbin, or /sbin"); + delete this; + 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 + int argPtr = 3; + std::vector<std::string> headerArgs; + for(std::map<std::string,std::string>::const_iterator h(_headers.begin());h!=_headers.end();++h) { + headerArgs.push_back(h->first); + headerArgs.back().append(": "); + headerArgs.back().append(h->second); + } + for(std::vector<std::string>::iterator h(headerArgs.begin());h!=headerArgs.end();++h) { + if (argPtr >= (1024 - 4)) // leave room for terminating NULL and URL + break; + curlArgs[argPtr++] = const_cast <char *>("-H"); + curlArgs[argPtr++] = const_cast <char *>(h->c_str()); + } + curlArgs[argPtr++] = const_cast <char *>(_url.c_str()); + curlArgs[argPtr] = (char *)0; + + int curlStdout[2]; + int curlStderr[2]; + ::pipe(curlStdout); + ::pipe(curlStderr); + + long pid = (long)vfork(); + if (pid < 0) { + // fork() failed + ::close(curlStdout[0]); + ::close(curlStdout[1]); + ::close(curlStderr[0]); + ::close(curlStderr[1]); + _handler(_arg,-1,_url,false,"unable to fork()"); + delete this; + return; + } else if (pid > 0) { + // fork() succeeded, in parent process + ::close(curlStdout[1]); + ::close(curlStderr[1]); + fcntl(curlStdout[0],F_SETFL,O_NONBLOCK); + fcntl(curlStderr[0],F_SETFL,O_NONBLOCK); + + int exitCode = -1; + unsigned long long timesOutAt = Utils::now() + ((unsigned long long)_timeout * 1000ULL); + bool timedOut = false; + bool tooLong = false; + for(;;) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errfds); + FD_SET(curlStdout[0],&readfds); + FD_SET(curlStderr[0],&readfds); + FD_SET(curlStdout[0],&errfds); + FD_SET(curlStderr[0],&errfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + select(std::max(curlStdout[0],curlStderr[0])+1,&readfds,&writefds,&errfds,&tv); + + if (FD_ISSET(curlStdout[0],&readfds)) { + int n = (int)::read(curlStdout[0],buf,sizeof(buf)); + if (n > 0) { + _body.append(buf,n); + // 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); + tooLong = true; + break; + } + } + if (FD_ISSET(curlStderr[0],&readfds)) + ::read(curlStderr[0],buf,sizeof(buf)); + if (FD_ISSET(curlStdout[0],&errfds)||FD_ISSET(curlStderr[0],&errfds)) + break; + + if (Utils::now() >= timesOutAt) { + ::kill(pid,SIGKILL); + timedOut = true; + break; + } + + if (waitpid(pid,&exitCode,WNOHANG) > 0) { + for(;;) { + // Drain output... + int n = (int)::read(curlStdout[0],buf,sizeof(buf)); + if (n <= 0) + break; + else { + _body.append(buf,n); + if (_body.length() > CURL_MAX_MESSAGE_LENGTH) { + tooLong = true; + break; + } + } + } + pid = 0; + break; + } + } + + if (pid > 0) + waitpid(pid,&exitCode,0); + + ::close(curlStdout[0]); + ::close(curlStderr[0]); + + if (timedOut) + _handler(_arg,-1,_url,false,"connection timed out"); + else if (tooLong) + _handler(_arg,-1,_url,false,"response too long"); + else if (exitCode) + _handler(_arg,-1,_url,false,"connection failed (curl returned non-zero exit code)"); + else { + unsigned long idx = 0; + + // Grab status line and headers, which will prefix output on + // success and will end with an empty line. + std::vector<std::string> headers; + headers.push_back(std::string()); + while (idx < _body.length()) { + char c = _body[idx++]; + if (c == '\n') { + if (!headers.back().length()) { + headers.pop_back(); + break; + } else headers.push_back(std::string()); + } else if (c != '\r') + headers.back().push_back(c); + } + if (headers.empty()||(!headers.front().length())) { + _handler(_arg,-1,_url,false,"HTTP response empty"); + delete this; + return; + } + + // Parse first line -- HTTP status code and response + size_t scPos = headers.front().find(' '); + if (scPos == std::string::npos) { + _handler(_arg,-1,_url,false,"invalid HTTP response (no status line)"); + delete this; + return; + } + ++scPos; + unsigned int rcode = Utils::strToUInt(headers.front().substr(scPos,3).c_str()); + if ((!rcode)||(rcode > 999)) { + _handler(_arg,-1,_url,false,"invalid HTTP response (invalid response code)"); + delete this; + return; + } + + // Serve up the resulting data to the handler + if (rcode == 200) + _handler(_arg,rcode,_url,false,_body.substr(idx)); + else if ((scPos + 4) < headers.front().length()) + _handler(_arg,rcode,_url,false,headers.front().substr(scPos+4)); + else _handler(_arg,rcode,_url,false,"(no status message from server)"); + } + + delete this; + return; + } else { + // fork() succeeded, in child process + ::dup2(curlStdout[1],STDOUT_FILENO); + ::close(curlStdout[1]); + ::dup2(curlStderr[1],STDERR_FILENO); + ::close(curlStderr[1]); + ::execv(curlPath.c_str(),curlArgs); + ::exit(-1); // only reached if execv() fails + } + } + + const std::string _url; + std::string _body; + std::map<std::string,std::string> _headers; + unsigned int _timeout; + void (*_handler)(void *,int,const std::string &,bool,const std::string &); + void *_arg; + Thread _myThread; +}; + +HttpClient::Request HttpClient::_do( + const char *method, + const std::string &url, + const std::map<std::string,std::string> &headers, + unsigned int timeout, + void (*handler)(void *,int,const std::string &,bool,const std::string &), + void *arg) +{ + return (HttpClient::Request)(new P_Req(method,url,headers,timeout,handler,arg)); +} + +#endif + +} // namespace ZeroTier diff --git a/node/HttpClient.hpp b/node/HttpClient.hpp new file mode 100644 index 00000000..4f5c9bc7 --- /dev/null +++ b/node/HttpClient.hpp @@ -0,0 +1,90 @@ +/* + * 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_HTTPCLIENT_HPP +#define ZT_HTTPCLIENT_HPP + +#include <string> +#include <map> + +#include "Constants.hpp" + +namespace ZeroTier { + +/** + * HTTP client that does queries in the background + * + * The handler method takes the following arguments: an arbitrary pointer, an + * HTTP response code, the URL queried, whether or not the message body was + * stored on disk, and the message body. + * + * If stored on disk, the body string contains the path and the file must be + * moved or deleted by the receiver when it's done. If an error occurs, the + * response code will be negative and the body will be the error message. + * + * All headers in the returned headers map will have their header names + * converted to lower case, e.g. "content-type". + * + * Currently only the "http" transport is guaranteed to be supported on all + * platforms. + */ +class HttpClient +{ +public: + typedef void * Request; + + /** + * Empty map for convenience use + */ + static const std::map<std::string,std::string> NO_HEADERS; + + /** + * Request a URL using the GET method + */ + static inline Request GET( + const std::string &url, + const std::map<std::string,std::string> &headers, + unsigned int timeout, + void (*handler)(void *,int,const std::string &,bool,const std::string &), + void *arg) + { + return _do("GET",url,headers,timeout,handler,arg); + } + +private: + static Request _do( + const char *method, + const std::string &url, + const std::map<std::string,std::string> &headers, + unsigned int timeout, + void (*handler)(void *,int,const std::string &,bool,const std::string &), + void *arg); +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Identity.hpp b/node/Identity.hpp index cb911b92..f6b1f876 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_IDENTITY_HPP -#define _ZT_IDENTITY_HPP +#ifndef ZT_IDENTITY_HPP +#define ZT_IDENTITY_HPP #include <stdio.h> #include <stdlib.h> diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 54fbc395..d90574e5 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_INETADDRESS_HPP -#define _ZT_INETADDRESS_HPP +#ifndef ZT_INETADDRESS_HPP +#define ZT_INETADDRESS_HPP #include <stdlib.h> #include <string.h> diff --git a/node/Logger.hpp b/node/Logger.hpp index de71ed39..b99df392 100644 --- a/node/Logger.hpp +++ b/node/Logger.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_LOGGER_HPP -#define _ZT_LOGGER_HPP +#ifndef ZT_LOGGER_HPP +#define ZT_LOGGER_HPP #include <stdio.h> diff --git a/node/MAC.hpp b/node/MAC.hpp index 87363a44..f0bca937 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_MAC_HPP -#define _ZT_MAC_HPP +#ifndef ZT_MAC_HPP +#define ZT_MAC_HPP #include <stdio.h> #include <stdlib.h> diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 426ef048..32f8c0ed 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_MULTICASTGROUP_HPP -#define _ZT_MULTICASTGROUP_HPP +#ifndef ZT_MULTICASTGROUP_HPP +#define ZT_MULTICASTGROUP_HPP #include <stdint.h> diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index 16ae7218..164bfd79 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_MULTICASTER_HPP -#define _ZT_MULTICASTER_HPP +#ifndef ZT_MULTICASTER_HPP +#define ZT_MULTICASTER_HPP #include <stdint.h> #include <string.h> diff --git a/node/Mutex.hpp b/node/Mutex.hpp index b0130293..509b60be 100644 --- a/node/Mutex.hpp +++ b/node/Mutex.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/
*/
-#ifndef _ZT_MUTEX_HPP
-#define _ZT_MUTEX_HPP
+#ifndef ZT_MUTEX_HPP
+#define ZT_MUTEX_HPP
#include "Constants.hpp"
#include "NonCopyable.hpp"
diff --git a/node/Network.hpp b/node/Network.hpp index a219cdf2..f41e7502 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_NETWORK_HPP -#define _ZT_NETWORK_HPP +#ifndef ZT_NETWORK_HPP +#define ZT_NETWORK_HPP #include <stdint.h> diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index a833006f..823363bd 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_NETWORKCONFIG_HPP -#define _ZT_NETWORKCONFIG_HPP +#ifndef ZT_NETWORKCONFIG_HPP +#define ZT_NETWORKCONFIG_HPP #include <stdint.h> diff --git a/node/Node.cpp b/node/Node.cpp index fe8cfb18..dd0e47ed 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -68,6 +68,7 @@ #include "CMWC4096.hpp" #include "SHA512.hpp" #include "Service.hpp" +#include "SoftwareUpdater.hpp" #ifdef __WINDOWS__ #include <Windows.h> @@ -182,6 +183,11 @@ unsigned long Node::LocalClient::send(const char *command) } } +std::vector<std::string> Node::LocalClient::splitLine(const char *line) +{ + return Utils::split(line," ","\\","\""); +} + struct _NodeImpl { RuntimeEnvironment renv; @@ -205,6 +211,7 @@ struct _NodeImpl #ifndef __WINDOWS__ delete renv.netconfService; #endif + delete renv.updater; delete renv.nc; delete renv.sysEnv; delete renv.topology; @@ -424,6 +431,10 @@ Node::ReasonForTermination Node::run() return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,foo); } _r->node = this; +#ifdef ZT_AUTO_UPDATE + if (ZT_DEFAULTS.updateLatestNfoURL.length()) + _r->updater = new SoftwareUpdater(_r); +#endif // Bind local port for core I/O if (!_r->demarc->bindLocalUdp(impl->port)) { @@ -462,6 +473,7 @@ Node::ReasonForTermination Node::run() // Core I/O loop try { + std::string shutdownIfUnreadablePath(_r->homePath + ZT_PATH_SEPARATOR_S + "shutdownIfUnreadable"); uint64_t lastNetworkAutoconfCheck = Utils::now() - 5000; // check autoconf again after 5s for startup uint64_t lastPingCheck = 0; uint64_t lastClean = Utils::now(); // don't need to do this immediately @@ -471,6 +483,13 @@ Node::ReasonForTermination Node::run() long lastDelayDelta = 0; while (impl->reasonForTermination == NODE_RUNNING) { + if (Utils::fileExists(shutdownIfUnreadablePath.c_str(),false)) { + FILE *tmpf = fopen(shutdownIfUnreadablePath.c_str(),"r"); + if (!tmpf) + return impl->terminateBecause(Node::NODE_NORMAL_TERMINATION,"shutdownIfUnreadable was not readable"); + fclose(tmpf); + } + uint64_t now = Utils::now(); bool resynchronize = false; @@ -610,15 +629,6 @@ unsigned int Node::versionMajor() throw() { return ZEROTIER_ONE_VERSION_MAJOR; } unsigned int Node::versionMinor() throw() { return ZEROTIER_ONE_VERSION_MINOR; } unsigned int Node::versionRevision() throw() { return ZEROTIER_ONE_VERSION_REVISION; } -// Scanned for by loader and/or updater to determine a binary's version -const unsigned char EMBEDDED_VERSION_STAMP[20] = { - 0x6d,0xfe,0xff,0x01,0x90,0xfa,0x89,0x57,0x88,0xa1,0xaa,0xdc,0xdd,0xde,0xb0,0x33, - ZEROTIER_ONE_VERSION_MAJOR, - ZEROTIER_ONE_VERSION_MINOR, - (unsigned char)(((unsigned int)ZEROTIER_ONE_VERSION_REVISION) & 0xff), /* little-endian */ - (unsigned char)((((unsigned int)ZEROTIER_ONE_VERSION_REVISION) >> 8) & 0xff) -}; - } // namespace ZeroTier extern "C" { diff --git a/node/Node.hpp b/node/Node.hpp index 238b5fce..2736713f 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -25,8 +25,11 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_NODE_HPP -#define _ZT_NODE_HPP +#ifndef ZT_NODE_HPP +#define ZT_NODE_HPP + +#include <string> +#include <vector> namespace ZeroTier { @@ -70,6 +73,16 @@ public: */ unsigned long send(const char *command) throw(); + inline unsigned long send(const std::string &command) throw() { return send(command.c_str()); } + + /** + * Split a line of results by space + * + * @param line Line to split + * @return Vector of fields + */ + static std::vector<std::string> splitLine(const char *line); + static inline std::vector<std::string> splitLine(const std::string &line) { return splitLine(line.c_str()); } private: // LocalClient is not copyable @@ -84,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 }; @@ -140,7 +168,7 @@ public: /** * Get the ZeroTier version in major.minor.revision string format - * + * * @return Version in string form */ static const char *versionString() @@ -158,14 +186,6 @@ private: void *const _impl; // private implementation }; -/** - * An embedded version code that can be searched for in the binary - * - * This shouldn't be used by users, but is exported to make certain that - * the linker actually includes it in the image. - */ -extern const unsigned char EMBEDDED_VERSION_STAMP[20]; - } // namespace ZeroTier extern "C" { diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 027f65ce..770f1f6f 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -56,6 +56,7 @@ #include "Poly1305.hpp" #include "SHA512.hpp" #include "Node.hpp" +#include "SoftwareUpdater.hpp" namespace ZeroTier { @@ -184,6 +185,7 @@ std::vector<std::string> NodeConfig::execute(const char *command) _P("200 help join <network ID>"); _P("200 help leave <network ID>"); _P("200 help terminate [<reason>]"); + _P("200 help updatecheck"); } else if (cmd[0] == "info") { bool isOnline = false; uint64_t now = Utils::now(); @@ -200,7 +202,7 @@ std::vector<std::string> NodeConfig::execute(const char *command) _r->topology->eachPeer(_DumpPeerStatistics(r)); } else if (cmd[0] == "listnetworks") { Mutex::Lock _l(_networks_m); - _P("200 listnetworks <nwid> <status> <type> <dev> <ips>"); + _P("200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>"); for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) { std::string tmp; std::set<InetAddress> ips(nw->second->tap().ips()); @@ -211,9 +213,17 @@ std::vector<std::string> NodeConfig::execute(const char *command) } SharedPtr<NetworkConfig> nconf(nw->second->config2()); - _P("200 listnetworks %.16llx %s %s %s %s", + + long long age = (nconf) ? ((long long)Utils::now() - (long long)nconf->timestamp()) : (long long)0; + if (age < 0) + age = 0; + age /= 1000; + + _P("200 listnetworks %.16llx %s %s %lld %s %s %s", (unsigned long long)nw->first, + ((nconf) ? nconf->name().c_str() : "?"), Network::statusString(nw->second->status()), + age, ((nconf) ? (nconf->isOpen() ? "public" : "private") : "?"), nw->second->tap().deviceName().c_str(), ((tmp.length() > 0) ? tmp.c_str() : "-")); @@ -260,6 +270,13 @@ std::vector<std::string> NodeConfig::execute(const char *command) if (cmd.size() > 1) _r->node->terminate(Node::NODE_NORMAL_TERMINATION,cmd[1].c_str()); else _r->node->terminate(Node::NODE_NORMAL_TERMINATION,(const char *)0); + } else if (cmd[0] == "updatecheck") { + if (_r->updater) { + _P("200 checking for software updates now at: %s",ZT_DEFAULTS.updateLatestNfoURL.c_str()); + _r->updater->checkNow(); + } else { + _P("500 software updates are not enabled"); + } } else { _P("404 %s No such command. Use 'help' for help.",cmd[0].c_str()); } diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index 0e7e4c98..2612cf6a 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_NODECONFIG_HPP -#define _ZT_NODECONFIG_HPP +#ifndef ZT_NODECONFIG_HPP +#define ZT_NODECONFIG_HPP #include <stdint.h> diff --git a/node/NonCopyable.hpp b/node/NonCopyable.hpp index 26536a36..e39deba8 100644 --- a/node/NonCopyable.hpp +++ b/node/NonCopyable.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _NONCOPYABLE_HPP__ -#define _NONCOPYABLE_HPP__ +#ifndef ZT_NONCOPYABLE_HPP__ +#define ZT_NONCOPYABLE_HPP__ namespace ZeroTier { diff --git a/node/Packet.hpp b/node/Packet.hpp index 56920f6f..6f3f9117 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_N_PACKET_HPP -#define _ZT_N_PACKET_HPP +#ifndef ZT_N_PACKET_HPP +#define ZT_N_PACKET_HPP #include <stdint.h> #include <string.h> @@ -615,52 +615,7 @@ public: * It does not generate an OK or ERROR message, and is treated only as * a hint to refresh now. */ - VERB_NETWORK_CONFIG_REFRESH = 12, - - /* Request information about a shared file: - * <[1] flags, currently unused and must be 0> - * <[2] 16-bit length of filename> - * <[...] name of file being requested> - * - * OK response payload (indicates that we have and will share): - * <[1] flags, currently unused and must be 0> - * <[2] 16-bit length of filename> - * <[...] name of file being requested> - * <[64] full length SHA-512 hash of file contents> - * <[4] 32-bit length of file in bytes> - * <[5] Signing ZeroTier One identity address> - * <[2] 16-bit length of signature of SHA-512 hash> - * <[...] signature of SHA-512 hash> - * - * ERROR response payload: - * <[2] 16-bit length of filename> - * <[...] name of file being requested> - * - * Support is optional. Nodes should return UNSUPPORTED_OPERATION if - * not supported or enabled. - */ - VERB_FILE_INFO_REQUEST = 13, - - /* Request a piece of a shared file - * <[16] first 16 bytes of SHA-512 of file being requested> - * <[4] 32-bit index of desired chunk> - * <[2] 16-bit length of desired chunk> - * - * OK response payload: - * <[16] first 16 bytes of SHA-512 of file being requested> - * <[4] 32-bit index of desired chunk> - * <[2] 16-bit length of desired chunk> - * <[...] the chunk> - * - * ERROR response payload: - * <[16] first 16 bytes of SHA-512 of file being requested> - * <[4] 32-bit index of desired chunk> - * <[2] 16-bit length of desired chunk> - * - * Support is optional. Nodes should return UNSUPPORTED_OPERATION if - * not supported or enabled. - */ - VERB_FILE_BLOCK_REQUEST = 14 + VERB_NETWORK_CONFIG_REFRESH = 12 }; /** diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp index cb3522ff..72b05290 100644 --- a/node/PacketDecoder.hpp +++ b/node/PacketDecoder.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_PACKETDECODER_HPP -#define _ZT_PACKETDECODER_HPP +#ifndef ZT_PACKETDECODER_HPP +#define ZT_PACKETDECODER_HPP #include <stdexcept> diff --git a/node/Peer.hpp b/node/Peer.hpp index 0a8a7b57..de5df08f 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_PEER_HPP -#define _ZT_PEER_HPP +#ifndef ZT_PEER_HPP +#define ZT_PEER_HPP #include <stdint.h> diff --git a/node/Poly1305.hpp b/node/Poly1305.hpp index 94e6078d..8baa448f 100644 --- a/node/Poly1305.hpp +++ b/node/Poly1305.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_POLY1305_HPP -#define _ZT_POLY1305_HPP +#ifndef ZT_POLY1305_HPP +#define ZT_POLY1305_HPP namespace ZeroTier { diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 3d73ca56..05e10676 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_RUNTIMEENVIRONMENT_HPP -#define _ZT_RUNTIMEENVIRONMENT_HPP +#ifndef ZT_RUNTIMEENVIRONMENT_HPP +#define ZT_RUNTIMEENVIRONMENT_HPP #include <string> @@ -46,6 +46,7 @@ class CMWC4096; class Service; class Node; class Multicaster; +class SoftwareUpdater; /** * Holds global state for an instance of ZeroTier::Node @@ -71,26 +72,36 @@ public: demarc((Demarc *)0), topology((Topology *)0), sysEnv((SysEnv *)0), - nc((NodeConfig *)0) + nc((NodeConfig *)0), + updater((SoftwareUpdater *)0) #ifndef __WINDOWS__ ,netconfService((Service *)0) #endif { } + // Full path to home folder std::string homePath; - // signal() to prematurely interrupt main loop wait + // Main loop waits on this condition when it delays between runs, so + // signaling this will prematurely wake it. Condition mainLoopWaitCondition; + // This node's identity Identity identity; + // Indicates that we are shutting down -- this is hacky, want to factor out volatile bool shutdownInProgress; - // Order matters a bit here. These are constructed in this order - // and then deleted in the opposite order on Node exit. + /* + * Order matters a bit here. These are constructed in this order + * and then deleted in the opposite order on Node exit. The order ensures + * that things that are needed are there before they're needed. + * + * These are constant and never null after startup unless indicated. + */ - Logger *log; // may be null + Logger *log; // null if logging is disabled CMWC4096 *prng; Multicaster *mc; Switch *sw; @@ -99,8 +110,9 @@ public: SysEnv *sysEnv; NodeConfig *nc; Node *node; + SoftwareUpdater *updater; // null if software updates are not enabled #ifndef __WINDOWS__ - Service *netconfService; // may be null + Service *netconfService; // null if no netconf service running #endif }; diff --git a/node/SHA512.hpp b/node/SHA512.hpp index 565eb097..721933cb 100644 --- a/node/SHA512.hpp +++ b/node/SHA512.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_SHA512_HPP -#define _ZT_SHA512_HPP +#ifndef ZT_SHA512_HPP +#define ZT_SHA512_HPP #define ZT_SHA512_DIGEST_LEN 64 diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index 9f34ba78..e09e2aaa 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -4,8 +4,8 @@ * This therefore is public domain. */ -#ifndef _ZT_SALSA20_HPP -#define _ZT_SALSA20_HPP +#ifndef ZT_SALSA20_HPP +#define ZT_SALSA20_HPP #include <stdint.h> diff --git a/node/Service.hpp b/node/Service.hpp index d8467cd1..22e53d62 100644 --- a/node/Service.hpp +++ b/node/Service.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_SERVICE_HPP -#define _ZT_SERVICE_HPP +#ifndef ZT_SERVICE_HPP +#define ZT_SERVICE_HPP #include <string> #include <stdexcept> diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp index 834d0a2e..f7604c06 100644 --- a/node/SharedPtr.hpp +++ b/node/SharedPtr.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_SHAREDPTR_HPP -#define _ZT_SHAREDPTR_HPP +#ifndef ZT_SHAREDPTR_HPP +#define ZT_SHAREDPTR_HPP #include "Mutex.hpp" #include "AtomicCounter.hpp" diff --git a/node/SoftwareUpdater.cpp b/node/SoftwareUpdater.cpp new file mode 100644 index 00000000..c515d5db --- /dev/null +++ b/node/SoftwareUpdater.cpp @@ -0,0 +1,189 @@ +/* + * 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); + + upd->_status = UPDATE_STATUS_IDLE; + + _r->node->terminate(Node::NODE_RESTART_FOR_UPGRADE,updatePath.c_str()); +#endif + +#ifdef __WINDOWS__ + todo; +#endif +} + +} // namespace ZeroTier diff --git a/node/SoftwareUpdater.hpp b/node/SoftwareUpdater.hpp new file mode 100644 index 00000000..5e47bbea --- /dev/null +++ b/node/SoftwareUpdater.hpp @@ -0,0 +1,124 @@ +/* + * 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); + } + } + } + } + + /** + * Check for updates now regardless of last check time or version + */ + 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); + } + } + + /** + * 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) ); + } + +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 diff --git a/node/Switch.hpp b/node/Switch.hpp index 68e3c6c4..6b3b8e6e 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_N_SWITCH_HPP -#define _ZT_N_SWITCH_HPP +#ifndef ZT_N_SWITCH_HPP +#define ZT_N_SWITCH_HPP #include <map> #include <set> diff --git a/node/SysEnv.hpp b/node/SysEnv.hpp index 21c25713..4f4a4f16 100644 --- a/node/SysEnv.hpp +++ b/node/SysEnv.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_SYSENV_HPP -#define _ZT_SYSENV_HPP +#ifndef ZT_SYSENV_HPP +#define ZT_SYSENV_HPP #include <stdint.h> diff --git a/node/Thread.hpp b/node/Thread.hpp index d295fea3..8adf79d3 100644 --- a/node/Thread.hpp +++ b/node/Thread.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/
*/
-#ifndef _ZT_THREAD_HPP
-#define _ZT_THREAD_HPP
+#ifndef ZT_THREAD_HPP
+#define ZT_THREAD_HPP
#include <stdexcept>
diff --git a/node/Topology.hpp b/node/Topology.hpp index 09dec86e..312377bc 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_TOPOLOGY_HPP -#define _ZT_TOPOLOGY_HPP +#ifndef ZT_TOPOLOGY_HPP +#define ZT_TOPOLOGY_HPP #include <stdio.h> #include <string.h> diff --git a/node/UdpSocket.hpp b/node/UdpSocket.hpp index d8467f64..cbd9de86 100644 --- a/node/UdpSocket.hpp +++ b/node/UdpSocket.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_UDPSOCKET_HPP -#define _ZT_UDPSOCKET_HPP +#ifndef ZT_UDPSOCKET_HPP +#define ZT_UDPSOCKET_HPP #include <stdexcept> diff --git a/node/Utils.cpp b/node/Utils.cpp index c565d8c4..c0886859 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -50,9 +50,6 @@ namespace ZeroTier { const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; -static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; -static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; - std::map<std::string,bool> Utils::listDirectory(const char *path) { std::map<std::string,bool> r; @@ -62,7 +59,8 @@ std::map<std::string,bool> Utils::listDirectory(const char *path) WIN32_FIND_DATAA ffd; if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) { do { - r[std::string(ffd.cFileName)] = ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); + if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,".."))) + r[std::string(ffd.cFileName)] = ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0); } while (FindNextFileA(hFind,&ffd)); FindClose(hFind); } @@ -153,7 +151,6 @@ unsigned int Utils::unhex(const char *hex,void *buf,unsigned int len) } unsigned int Utils::unhex(const char *hex,unsigned int hexlen,void *buf,unsigned int len) - throw() { int n = 1; unsigned char c,b = 0; @@ -193,7 +190,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) Mutex::Lock _l(randomLock); - // A Salsa20 instance is used to mangle whatever our base + // A Salsa20/8 instance is used to further mangle whatever our base // random source happens to be. if (!randInitialized) { randInitialized = true; @@ -210,7 +207,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) { int fd = ::open("/dev/urandom",O_RDONLY); if (fd < 0) { - fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom: %s"ZT_EOL_S,strerror(errno)); + fprintf(stderr,"FATAL ERROR: unable to open /dev/urandom"ZT_EOL_S); exit(-1); } if ((int)::read(fd,randbuf,sizeof(randbuf)) != (int)sizeof(randbuf)) { @@ -222,17 +219,20 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) #else #ifdef __WINDOWS__ { - char ktmp[32]; - char ivtmp[8]; - for(int i=0;i<32;++i) ktmp[i] = (char)rand(); - for(int i=0;i<8;++i) ivtmp[i] = (char)rand(); - double now = Utils::nowf(); - memcpy(ktmp,&now,sizeof(now)); - DWORD tmp = GetCurrentProcessId(); - memcpy(ktmp + sizeof(now),&tmp,sizeof(tmp)); - tmp = GetTickCount(); - memcpy(ktmp + sizeof(now) + sizeof(DWORD),&tmp,sizeof(tmp)); - Salsa20 s20tmp(ktmp,256,ivtmp,8); + struct { + double nowf; + DWORD processId; + DWORD tickCount; + uint64_t nowi; + char padding[32]; + } keyMaterial; + keyMaterial.nowf = Utils::nowf(); + keyMaterial.processId = GetCurrentProcessId(); + keyMaterial.tickCount = GetTickCount(); + keyMaterial.nowi = Utils::now(); + for(int i=0;i<sizeof(keyMaterial.padding);++i) + keyMaterial.padding[i] = (char)rand(); + Salsa20 s20tmp(&keyMaterial,256,&(keyMaterial.nowi),8); s20tmp.encrypt(randbuf,randbuf,sizeof(randbuf)); } #else @@ -248,7 +248,7 @@ no getSecureRandom() implementation; void Utils::lockDownFile(const char *path,bool isDir) { -#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) +#ifdef __UNIX_LIKE__ chmod(path,isDir ? 0700 : 0600); #else #ifdef _WIN32 @@ -265,92 +265,24 @@ uint64_t Utils::getLastModified(const char *path) return (((uint64_t)s.st_mtime) * 1000ULL); } -std::string Utils::toRfc1123(uint64_t t64) +bool Utils::fileExists(const char *path,bool followLinks) { - struct tm t; - char buf[128]; - time_t utc = (time_t)(t64 / 1000ULL); -#ifdef __WINDOWS__ - gmtime_s(&t,&utc); -#else - gmtime_r(&utc,&t); + struct stat s; +#ifdef __UNIX_LIKE__ + if (!followLinks) + return (lstat(path,&s) == 0); #endif - Utils::snprintf(buf,sizeof(buf),"%3s, %02d %3s %4d %02d:%02d:%02d GMT",DAY_NAMES[t.tm_wday],t.tm_mday,MONTH_NAMES[t.tm_mon],t.tm_year + 1900,t.tm_hour,t.tm_min,t.tm_sec); - return std::string(buf); + return (stat(path,&s) == 0); } -#ifdef __WINDOWS__ -static int is_leap(unsigned y) { - y += 1900; - return (y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0); -} -static time_t timegm(struct tm *tm) { - static const unsigned ndays[2][12] = { - {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, - {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} - }; - time_t res = 0; - int i; - for (i = 70; i < tm->tm_year; ++i) - res += is_leap(i) ? 366 : 365; - - for (i = 0; i < tm->tm_mon; ++i) - res += ndays[is_leap(tm->tm_year)][i]; - res += tm->tm_mday - 1; - res *= 24; - res += tm->tm_hour; - res *= 60; - res += tm->tm_min; - res *= 60; - res += tm->tm_sec; - return res; -} -#endif - -uint64_t Utils::fromRfc1123(const char *tstr) +int64_t Utils::getFileSize(const char *path) { - struct tm t; - char wdays[128],mons[128]; - - int l = (int)strlen(tstr); - if ((l < 29)||(l > 64)) - return 0; - int assigned = sscanf(tstr,"%3s, %02d %3s %4d %02d:%02d:%02d GMT",wdays,&t.tm_mday,mons,&t.tm_year,&t.tm_hour,&t.tm_min,&t.tm_sec); - if (assigned != 7) - return 0; - - wdays[3] = '\0'; - for(t.tm_wday=0;t.tm_wday<7;++t.tm_wday) { -#ifdef __WINDOWS__ - if (!_stricmp(DAY_NAMES[t.tm_wday],wdays)) - break; -#else - if (!strcasecmp(DAY_NAMES[t.tm_wday],wdays)) - break; -#endif - } - if (t.tm_wday == 7) - return 0; - mons[3] = '\0'; - for(t.tm_mon=0;t.tm_mon<12;++t.tm_mon) { -#ifdef __WINDOWS__ - if (!_stricmp(MONTH_NAMES[t.tm_mday],mons)) - break; -#else - if (!strcasecmp(MONTH_NAMES[t.tm_mday],mons)) - break; -#endif - } - if (t.tm_mon == 12) - return 0; - - t.tm_wday = 0; // ignored by timegm - t.tm_yday = 0; // ignored by timegm - t.tm_isdst = 0; // ignored by timegm - - time_t utc = timegm(&t); - - return ((utc > 0) ? (1000ULL * (uint64_t)utc) : 0ULL); + struct stat s; + if (stat(path,&s)) + return -1; + if (S_ISREG(s.st_mode)) + return s.st_size; + return -1; } bool Utils::readFile(const char *path,std::string &buf) diff --git a/node/Utils.hpp b/node/Utils.hpp index 6a56ba9d..5be60e15 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -25,8 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ -#ifndef _ZT_UTILS_HPP -#define _ZT_UTILS_HPP +#ifndef ZT_UTILS_HPP +#define ZT_UTILS_HPP #include <stdio.h> #include <stdlib.h> @@ -41,9 +41,6 @@ #include "Constants.hpp" -#include "../ext/lz4/lz4.h" -#include "../ext/lz4/lz4hc.h" - #ifdef __WINDOWS__ #include <WinSock2.h> #include <Windows.h> @@ -53,11 +50,6 @@ #include <arpa/inet.h> #endif -/** - * Maximum compression/decompression block size (do not change) - */ -#define ZT_COMPRESSION_BLOCK_SIZE 16777216 - namespace ZeroTier { /** @@ -108,23 +100,24 @@ public: return (unlink(path) == 0); #endif } - static inline bool rm(const std::string &path) - throw() - { - return rm(path.c_str()); - } + static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); } /** * List a directory's contents + * + * Keys in returned map are filenames only and don't include the leading + * path. Pseudo-paths like . and .. are not returned. Values are true if + * the item is a directory, false if it's a file. More detailed attributes + * aren't supported since the code that uses this doesn't need them. * * @param path Path to list - * @param files Set to fill with files - * @param directories Set to fill with directories * @return Map of entries and whether or not they are also directories (empty on failure) */ static std::map<std::string,bool> listDirectory(const char *path); /** + * Convert binary data to hexadecimal + * * @param data Data to convert to hex * @param len Length of data * @return Hexadecimal string @@ -133,6 +126,11 @@ public: static inline std::string hex(const std::string &data) { return hex(data.data(),(unsigned int)data.length()); } /** + * Convert hexadecimal to binary data + * + * This ignores all non-hex characters, just stepping over them and + * continuing. Upper and lower case are supported for letters a-f. + * * @param hex Hexadecimal ASCII code (non-hex chars are ignored) * @return Binary data */ @@ -140,6 +138,11 @@ public: static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str()); } /** + * Convert hexadecimal to binary data + * + * This ignores all non-hex characters, just stepping over them and + * continuing. Upper and lower case are supported for letters a-f. + * * @param hex Hexadecimal ASCII * @param buf Buffer to fill * @param len Length of buffer @@ -149,16 +152,25 @@ public: static inline unsigned int unhex(const std::string &hex,void *buf,unsigned int len) { return unhex(hex.c_str(),buf,len); } /** + * Convert hexadecimal to binary data + * + * This ignores all non-hex characters, just stepping over them and + * continuing. Upper and lower case are supported for letters a-f. + * * @param hex Hexadecimal ASCII * @param hexlen Length of hex ASCII * @param buf Buffer to fill * @param len Length of buffer * @return Number of bytes actually written to buffer */ - static unsigned int unhex(const char *hex,unsigned int hexlen,void *buf,unsigned int len) - throw(); + static unsigned int unhex(const char *hex,unsigned int hexlen,void *buf,unsigned int len); /** + * Generate secure random bytes + * + * This will try to use whatever OS sources of entropy are available. It's + * guarded by an internal mutex so it's thread-safe. + * * @param buf Buffer to fill * @param bytes Number of random bytes to generate */ @@ -188,193 +200,16 @@ public: /** * @param path Path to check + * @param followLinks Follow links (on platforms with that concept) * @return True if file or directory exists at path location */ - static inline bool fileExists(const char *path) - { - return (getLastModified(path) != 0); - } - - /** - * @param t64 Time in ms since epoch - * @return RFC1123 date string - */ - static std::string toRfc1123(uint64_t t64); - - /** - * @param tstr Time in RFC1123 string format - * @return Time in ms since epoch - */ - static uint64_t fromRfc1123(const char *tstr); - static inline uint64_t fromRfc1123(const std::string &tstr) { return fromRfc1123(tstr.c_str()); } - - /** - * String append output function object for use with compress/decompress - */ - class StringAppendOutput - { - public: - StringAppendOutput(std::string &s) : _s(s) {} - inline void operator()(const void *data,unsigned int len) { _s.append((const char *)data,len); } - private: - std::string &_s; - }; - - /** - * STDIO FILE append output function object for compress/decompress - * - * Throws std::runtime_error on write error. - */ - class FILEAppendOutput - { - public: - FILEAppendOutput(FILE *f) : _f(f) {} - inline void operator()(const void *data,unsigned int len) - throw(std::runtime_error) - { - if ((int)fwrite(data,1,len,_f) != (int)len) - throw std::runtime_error("write failed"); - } - private: - FILE *_f; - }; + static bool fileExists(const char *path,bool followLinks = true); /** - * Compress data - * - * O must be a function or function object that takes the following - * arguments: (const void *data,unsigned int len) - * - * @param in Input iterator that reads bytes (char, uint8_t, etc.) - * @param out Output iterator that writes bytes + * @param path Path to file + * @return File size or -1 if nonexistent or other failure */ - template<typename I,typename O> - static inline void compress(I begin,I end,O out) - { - unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE); - char *buf = new char[bufLen * 2]; - char *buf2 = buf + bufLen; - - try { - I inp(begin); - for(;;) { - unsigned int readLen = 0; - while ((readLen < ZT_COMPRESSION_BLOCK_SIZE)&&(inp != end)) { - buf[readLen++] = (char)*inp; - ++inp; - } - if (!readLen) - break; - - uint32_t l = hton((uint32_t)readLen); - out((const void *)&l,4); // original size - - if (readLen < 32) { // don't bother compressing itty bitty blocks - l = 0; // stored - out((const void *)&l,4); - out((const void *)buf,readLen); - continue; - } - - int lz4CompressedLen = LZ4_compressHC(buf,buf2,(int)readLen); - if ((lz4CompressedLen <= 0)||(lz4CompressedLen >= (int)readLen)) { - l = 0; // stored - out((const void *)&l,4); - out((const void *)buf,readLen); - continue; - } - - l = hton((uint32_t)lz4CompressedLen); // lz4 only - out((const void *)&l,4); - out((const void *)buf2,(unsigned int)lz4CompressedLen); - } - - delete [] buf; - } catch ( ... ) { - delete [] buf; - throw; - } - } - - /** - * Decompress data - * - * O must be a function or function object that takes the following - * arguments: (const void *data,unsigned int len) - * - * @param in Input iterator that reads bytes (char, uint8_t, etc.) - * @param out Output iterator that writes bytes - * @return False on decompression error - */ - template<typename I,typename O> - static inline bool decompress(I begin,I end,O out) - { - volatile char i32c[4]; - void *const i32cp = (void *)i32c; - unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE); - char *buf = new char[bufLen * 2]; - char *buf2 = buf + bufLen; - - try { - I inp(begin); - while (inp != end) { - i32c[0] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[1] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[2] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[3] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - unsigned int originalSize = ntoh(*((const uint32_t *)i32cp)); - i32c[0] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[1] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[2] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - i32c[3] = (char)*inp; if (++inp == end) { delete [] buf; return false; } - uint32_t _compressedSize = ntoh(*((const uint32_t *)i32cp)); - unsigned int compressedSize = _compressedSize & 0x7fffffff; - - if (compressedSize) { - if (compressedSize > bufLen) { - delete [] buf; - return false; - } - unsigned int readLen = 0; - while ((readLen < compressedSize)&&(inp != end)) { - buf[readLen++] = (char)*inp; - ++inp; - } - if (readLen != compressedSize) { - delete [] buf; - return false; - } - - if (LZ4_uncompress_unknownOutputSize(buf,buf2,compressedSize,bufLen) != (int)originalSize) { - delete [] buf; - return false; - } else out((const void *)buf2,(unsigned int)originalSize); - } else { // stored - if (originalSize > bufLen) { - delete [] buf; - return false; - } - unsigned int readLen = 0; - while ((readLen < originalSize)&&(inp != end)) { - buf[readLen++] = (char)*inp; - ++inp; - } - if (readLen != originalSize) { - delete [] buf; - return false; - } - - out((const void *)buf,(unsigned int)originalSize); - } - } - - delete [] buf; - return true; - } catch ( ... ) { - delete [] buf; - throw; - } - } + static int64_t getFileSize(const char *path); /** * @return Current time in milliseconds since epoch @@ -678,6 +513,7 @@ public: return ((*aptr & mask) == (*aptr & mask)); } + // Byte swappers for big/little endian conversion static inline uint8_t hton(uint8_t n) throw() { return n; } static inline int8_t hton(int8_t n) throw() { return n; } static inline uint16_t hton(uint16_t n) throw() { return htons(n); } |