summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/Address.hpp4
-rw-r--r--node/Array.hpp4
-rw-r--r--node/AtomicCounter.hpp4
-rw-r--r--node/BandwidthAccount.hpp4
-rw-r--r--node/Buffer.hpp4
-rw-r--r--node/C25519.hpp4
-rw-r--r--node/CMWC4096.hpp4
-rw-r--r--node/CertificateOfMembership.hpp4
-rw-r--r--node/Condition.hpp4
-rw-r--r--node/Constants.hpp15
-rw-r--r--node/Defaults.cpp50
-rw-r--r--node/Defaults.hpp20
-rw-r--r--node/Demarc.hpp4
-rw-r--r--node/Dictionary.hpp4
-rw-r--r--node/EthernetTap.cpp2
-rw-r--r--node/EthernetTap.hpp4
-rw-r--r--node/HttpClient.cpp311
-rw-r--r--node/HttpClient.hpp90
-rw-r--r--node/Identity.hpp4
-rw-r--r--node/InetAddress.hpp4
-rw-r--r--node/Logger.hpp4
-rw-r--r--node/MAC.hpp4
-rw-r--r--node/MulticastGroup.hpp4
-rw-r--r--node/Multicaster.hpp4
-rw-r--r--node/Mutex.hpp4
-rw-r--r--node/Network.hpp4
-rw-r--r--node/NetworkConfig.hpp4
-rw-r--r--node/Node.cpp28
-rw-r--r--node/Node.hpp44
-rw-r--r--node/NodeConfig.cpp21
-rw-r--r--node/NodeConfig.hpp4
-rw-r--r--node/NonCopyable.hpp4
-rw-r--r--node/Packet.hpp51
-rw-r--r--node/PacketDecoder.hpp4
-rw-r--r--node/Peer.hpp4
-rw-r--r--node/Poly1305.hpp4
-rw-r--r--node/RuntimeEnvironment.hpp28
-rw-r--r--node/SHA512.hpp4
-rw-r--r--node/Salsa20.hpp4
-rw-r--r--node/Service.hpp4
-rw-r--r--node/SharedPtr.hpp4
-rw-r--r--node/SoftwareUpdater.cpp189
-rw-r--r--node/SoftwareUpdater.hpp124
-rw-r--r--node/Switch.hpp4
-rw-r--r--node/SysEnv.hpp4
-rw-r--r--node/Thread.hpp4
-rw-r--r--node/Topology.hpp4
-rw-r--r--node/UdpSocket.hpp4
-rw-r--r--node/Utils.cpp132
-rw-r--r--node/Utils.hpp238
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); }