Browse Source

Add awareness of online status, and put old OS-dep utils in OSUtils.

Adam Ierymenko 10 years ago
parent
commit
e34bc961db
5 changed files with 466 additions and 7 deletions
  1. 1 1
      node/Constants.hpp
  2. 13 5
      node/Node.cpp
  3. 19 1
      node/Node.hpp
  4. 210 0
      osdep/OSUtils.cpp
  5. 223 0
      osdep/OSUtils.hpp

+ 1 - 1
node/Constants.hpp

@@ -280,7 +280,7 @@
 /**
 /**
  * Timeout for overall peer activity (measured from last receive)
  * Timeout for overall peer activity (measured from last receive)
  */
  */
-#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 2) + ZT_PING_CHECK_INVERVAL)
+#define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_DIRECT_PING_DELAY + (ZT_PING_CHECK_INVERVAL * 3))
 
 
 /**
 /**
  * Stop relaying via peers that have not responded to direct sends
  * Stop relaying via peers that have not responded to direct sends

+ 13 - 5
node/Node.cpp

@@ -76,6 +76,7 @@ Node::Node(
 	_newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR;
 	_newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR;
 	_newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR;
 	_newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR;
 	_newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION;
 	_newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION;
+	_online = false;
 
 
 	std::string idtmp(dataStoreGet("identity.secret"));
 	std::string idtmp(dataStoreGet("identity.secret"));
 	if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
 	if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
@@ -187,19 +188,19 @@ class _PingPeersThatNeedPing
 {
 {
 public:
 public:
 	_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now) :
 	_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now) :
-		lastReceiveFromSupernode(0),
+		lastReceiveFromUpstream(0),
 		RR(renv),
 		RR(renv),
 		_now(now),
 		_now(now),
 		_supernodes(RR->topology->supernodeAddresses()) {}
 		_supernodes(RR->topology->supernodeAddresses()) {}
 
 
-	uint64_t lastReceiveFromSupernode;
+	uint64_t lastReceiveFromUpstream;
 
 
 	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 	{
 	{
 		if (std::find(_supernodes.begin(),_supernodes.end(),p->address()) != _supernodes.end()) {
 		if (std::find(_supernodes.begin(),_supernodes.end(),p->address()) != _supernodes.end()) {
 			p->doPingAndKeepalive(RR,_now);
 			p->doPingAndKeepalive(RR,_now);
-			if (p->lastReceive() > lastReceiveFromSupernode)
-				lastReceiveFromSupernode = p->lastReceive();
+			if (p->lastReceive() > lastReceiveFromUpstream)
+				lastReceiveFromUpstream = p->lastReceive();
 		} else if (p->alive(_now)) {
 		} else if (p->alive(_now)) {
 			p->doPingAndKeepalive(RR,_now);
 			p->doPingAndKeepalive(RR,_now);
 		}
 		}
@@ -218,6 +219,8 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,uint64_t *nextBackgroun
 	if ((now - _lastPingCheck) >= ZT_PING_CHECK_INVERVAL) {
 	if ((now - _lastPingCheck) >= ZT_PING_CHECK_INVERVAL) {
 		_lastPingCheck = now;
 		_lastPingCheck = now;
 
 
+		// This is used as a floor for the desperation and online status
+		// calculations if we just started up or have been asleep.
 		if ((now - _startTimeAfterInactivity) > (ZT_PING_CHECK_INVERVAL * 3))
 		if ((now - _startTimeAfterInactivity) > (ZT_PING_CHECK_INVERVAL * 3))
 			_startTimeAfterInactivity = now;
 			_startTimeAfterInactivity = now;
 
 
@@ -225,7 +228,12 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,uint64_t *nextBackgroun
 			_PingPeersThatNeedPing pfunc(RR,now);
 			_PingPeersThatNeedPing pfunc(RR,now);
 			RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
 			RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
 
 
-			_coreDesperation = (unsigned int)((now - std::max(_startTimeAfterInactivity,pfunc.lastReceiveFromSupernode)) / (ZT_PING_CHECK_INVERVAL * ZT_CORE_DESPERATION_INCREMENT));
+			const uint64_t lastActivityAgo = now - std::max(_startTimeAfterInactivity,pfunc.lastReceiveFromUpstream);
+			_coreDesperation = (unsigned int)(lastActivityAgo / (ZT_PING_CHECK_INVERVAL * ZT_CORE_DESPERATION_INCREMENT));
+			bool oldOnline = _online;
+			_online = (lastActivityAgo < ZT_PEER_ACTIVITY_TIMEOUT);
+			if (oldOnline != _online)
+				postEvent(_online ? ZT1_EVENT_ONLINE : ZT1_EVENT_OFFLINE);
 		} catch ( ... ) {
 		} catch ( ... ) {
 			return ZT1_RESULT_FATAL_ERROR_INTERNAL;
 			return ZT1_RESULT_FATAL_ERROR_INTERNAL;
 		}
 		}

+ 19 - 1
node/Node.hpp

@@ -172,7 +172,19 @@ public:
 	}
 	}
 
 
 	/**
 	/**
-	 * @return Overall system level of desperation based on how long it's been since an upstream node (supernode) has talked to us
+	 * Get an overall current level of desperation
+	 *
+	 * The current level of desperation is based on how recently an upstream
+	 * (a.k.a. supernode) peer has spoken to us. As such, it will change and
+	 * return to 0 once something like tunneling (higher desperation link) is
+	 * active. As a result, actual link desperation for outgoing messages
+	 * should be the max of either this or the most recent link desperation
+	 * for an incoming message from a given address. See Path.hpp and Peer.hpp.
+	 *
+	 * In other words think of this as 'the desperation we should try to
+	 * escalate to right now.'
+	 *
+	 * @return Overall system level of desperation
 	 */
 	 */
 	inline unsigned int coreDesperation() const throw() { return _coreDesperation; }
 	inline unsigned int coreDesperation() const throw() { return _coreDesperation; }
 
 
@@ -198,6 +210,11 @@ public:
 	 */
 	 */
 	inline int configureVirtualNetworkPort(uint64_t nwid,ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT1_Node *>(this),nwid,op,nc); }
 	inline int configureVirtualNetworkPort(uint64_t nwid,ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT1_Node *>(this),nwid,op,nc); }
 
 
+	/**
+	 * @return True if we appear to be online
+	 */
+	inline bool online() const throw() { return _online; }
+
 	/**
 	/**
 	 * If this version is newer than the newest we've seen, post a new version seen event
 	 * If this version is newer than the newest we've seen, post a new version seen event
 	 */
 	 */
@@ -231,6 +248,7 @@ private:
 	uint64_t _lastHousekeepingRun;
 	uint64_t _lastHousekeepingRun;
 	unsigned int _coreDesperation;
 	unsigned int _coreDesperation;
 	unsigned int _newestVersionSeen[3]; // major, minor, revision
 	unsigned int _newestVersionSeen[3]; // major, minor, revision
+	bool _online;
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 210 - 0
osdep/OSUtils.cpp

@@ -0,0 +1,210 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015  ZeroTier, Inc.
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+
+#include "../node/Constants.hpp"
+
+#ifdef __UNIX_LIKE__
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <dirent.h>
+#endif
+
+#ifdef __WINDOWS__
+#include <wincrypt.h>
+#endif
+
+#include "OSUtils.hpp"
+
+namespace ZeroTier {
+
+#ifdef __UNIX_LIKE__
+bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
+	throw()
+{
+	int fdout = ::open(stdoutPath,O_WRONLY|O_CREAT,0600);
+	if (fdout > 0) {
+		int fderr;
+		if (stderrPath) {
+			fderr = ::open(stderrPath,O_WRONLY|O_CREAT,0600);
+			if (fderr <= 0) {
+				::close(fdout);
+				return false;
+			}
+		} else fderr = fdout;
+		::close(STDOUT_FILENO);
+		::close(STDERR_FILENO);
+		::dup2(fdout,STDOUT_FILENO);
+		::dup2(fderr,STDERR_FILENO);
+		return true;
+	}
+	return false;
+}
+#endif // __UNIX_LIKE__
+
+std::map<std::string,bool> OSUtils::listDirectory(const char *path)
+{
+	std::map<std::string,bool> r;
+
+#ifdef __WINDOWS__
+	HANDLE hFind;
+	WIN32_FIND_DATAA ffd;
+	if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
+		do {
+			if ((strcmp(ffd.cFileName,"."))&&(strcmp(ffd.cFileName,"..")))
+				r[std::string(ffd.cFileName)] = ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+		} while (FindNextFileA(hFind,&ffd));
+		FindClose(hFind);
+	}
+#else
+	struct dirent de;
+	struct dirent *dptr;
+
+	DIR *d = opendir(path);
+	if (!d)
+		return r;
+
+	dptr = (struct dirent *)0;
+	for(;;) {
+		if (readdir_r(d,&de,&dptr))
+			break;
+		if (dptr) {
+			if ((strcmp(dptr->d_name,"."))&&(strcmp(dptr->d_name,"..")))
+				r[std::string(dptr->d_name)] = (dptr->d_type == DT_DIR);
+		} else break;
+	}
+#endif
+
+	return r;
+}
+
+void OSUtils::lockDownFile(const char *path,bool isDir)
+{
+#ifdef __UNIX_LIKE__
+	chmod(path,isDir ? 0700 : 0600);
+#else
+#ifdef __WINDOWS__
+	{
+		STARTUPINFOA startupInfo;
+		PROCESS_INFORMATION processInfo;
+
+		startupInfo.cb = sizeof(startupInfo);
+		memset(&startupInfo,0,sizeof(STARTUPINFOA));
+		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+			WaitForSingleObject(processInfo.hProcess,INFINITE);
+			CloseHandle(processInfo.hProcess);
+			CloseHandle(processInfo.hThread);
+		}
+
+		startupInfo.cb = sizeof(startupInfo);
+		memset(&startupInfo,0,sizeof(STARTUPINFOA));
+		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
+		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+			WaitForSingleObject(processInfo.hProcess,INFINITE);
+			CloseHandle(processInfo.hProcess);
+			CloseHandle(processInfo.hThread);
+		}
+	}
+#endif
+#endif
+}
+
+uint64_t OSUtils::getLastModified(const char *path)
+{
+	struct stat s;
+	if (stat(path,&s))
+		return 0;
+	return (((uint64_t)s.st_mtime) * 1000ULL);
+}
+
+bool OSUtils::fileExists(const char *path,bool followLinks)
+{
+	struct stat s;
+#ifdef __UNIX_LIKE__
+	if (!followLinks)
+		return (lstat(path,&s) == 0);
+#endif
+	return (stat(path,&s) == 0);
+}
+
+int64_t OSUtils::getFileSize(const char *path)
+{
+	struct stat s;
+	if (stat(path,&s))
+		return -1;
+#ifdef __WINDOWS__
+	return s.st_size;
+#else
+	if (S_ISREG(s.st_mode))
+		return s.st_size;
+#endif
+	return -1;
+}
+
+bool OSUtils::readFile(const char *path,std::string &buf)
+{
+	char tmp[4096];
+	FILE *f = fopen(path,"rb");
+	if (f) {
+		for(;;) {
+			long n = (long)fread(tmp,1,sizeof(tmp),f);
+			if (n > 0)
+				buf.append(tmp,n);
+			else break;
+		}
+		fclose(f);
+		return true;
+	}
+	return false;
+}
+
+bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
+{
+	FILE *f = fopen(path,"wb");
+	if (f) {
+		if ((long)fwrite(buf,1,len,f) != (long)len) {
+			fclose(f);
+			return false;
+		} else {
+			fclose(f);
+			return true;
+		}
+	}
+	return false;
+}
+
+} // namespace ZeroTier

+ 223 - 0
osdep/OSUtils.hpp

@@ -0,0 +1,223 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015  ZeroTier, Inc.
+ *
+ * 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_OSUTILS_HPP
+#define ZT_OSUTILS_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+
+#include <string>
+#include <stdexcept>
+#include <vector>
+#include <map>
+
+#include "../node/Constants.hpp"
+
+#ifdef __WINDOWS__
+#include <WinSock2.h>
+#include <Windows.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#endif
+
+namespace ZeroTier {
+
+/**
+ * Miscellaneous utility functions and global constants
+ */
+class OSUtils
+{
+public:
+#ifdef __UNIX_LIKE__
+	/**
+	 * Close STDOUT_FILENO and STDERR_FILENO and replace them with output to given path
+	 *
+	 * This can be called after fork() and prior to exec() to suppress output
+	 * from a subprocess, such as auto-update.
+	 *
+	 * @param stdoutPath Path to file to use for stdout
+	 * @param stderrPath Path to file to use for stderr, or NULL for same as stdout (default)
+	 * @return True on success
+	 */
+	static bool redirectUnixOutputs(const char *stdoutPath,const char *stderrPath = (const char *)0)
+		throw();
+#endif // __UNIX_LIKE__
+
+	/**
+	 * Delete a file
+	 *
+	 * @param path Path to delete
+	 * @return True if delete was successful
+	 */
+	static inline bool rm(const char *path)
+		throw()
+	{
+#ifdef __WINDOWS__
+		return (DeleteFileA(path) != FALSE);
+#else
+		return (unlink(path) == 0);
+#endif
+	}
+	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
+	 * @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);
+
+	/**
+	 * Set modes on a file to something secure
+	 * 
+	 * This locks a file so that only the owner can access it. What it actually
+	 * does varies by platform.
+	 * 
+	 * @param path Path to lock
+	 * @param isDir True if this is a directory
+	 */
+	static void lockDownFile(const char *path,bool isDir);
+
+	/**
+	 * Get file last modification time
+	 *
+	 * Resolution is often only second, not millisecond, but the return is
+	 * always in ms for comparison against now().
+	 *
+	 * @param path Path to file to get time
+	 * @return Last modification time in ms since epoch or 0 if not found
+	 */
+	static uint64_t getLastModified(const char *path);
+
+	/**
+	 * @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 bool fileExists(const char *path,bool followLinks = true);
+
+	/**
+	 * @param path Path to file
+	 * @return File size or -1 if nonexistent or other failure
+	 */
+	static int64_t getFileSize(const char *path);
+
+	/**
+	 * @return Current time in milliseconds since epoch
+	 */
+	static inline uint64_t now()
+		throw()
+	{
+#ifdef __WINDOWS__
+		FILETIME ft;
+		SYSTEMTIME st;
+		ULARGE_INTEGER tmp;
+		GetSystemTime(&st);
+		SystemTimeToFileTime(&st,&ft);
+		tmp.LowPart = ft.dwLowDateTime;
+		tmp.HighPart = ft.dwHighDateTime;
+		return ( ((tmp.QuadPart - 116444736000000000ULL) / 10000L) + st.wMilliseconds );
+#else
+		struct timeval tv;
+		gettimeofday(&tv,(struct timezone *)0);
+		return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) );
+#endif
+	};
+
+	/**
+	 * @return Current time in seconds since epoch, to the highest available resolution
+	 */
+	static inline double nowf()
+		throw()
+	{
+#ifdef __WINDOWS__
+		FILETIME ft;
+		SYSTEMTIME st;
+		ULARGE_INTEGER tmp;
+		GetSystemTime(&st);
+		SystemTimeToFileTime(&st,&ft);
+		tmp.LowPart = ft.dwLowDateTime;
+		tmp.HighPart = ft.dwHighDateTime;
+		return (((double)(tmp.QuadPart - 116444736000000000ULL)) / 10000000.0);
+#else
+		struct timeval tv;
+		gettimeofday(&tv,(struct timezone *)0);
+		return ( ((double)tv.tv_sec) + (((double)tv.tv_usec) / 1000000.0) );
+#endif
+	}
+
+	/**
+	 * Read the full contents of a file into a string buffer
+	 *
+	 * The buffer isn't cleared, so if it already contains data the file's data will
+	 * be appended.
+	 *
+	 * @param path Path of file to read
+	 * @param buf Buffer to fill
+	 * @return True if open and read successful
+	 */
+	static bool readFile(const char *path,std::string &buf);
+
+	/**
+	 * Write a block of data to disk, replacing any current file contents
+	 *
+	 * @param path Path to write
+	 * @param buf Buffer containing data
+	 * @param len Length of buffer
+	 * @return True if entire file was successfully written
+	 */
+	static bool writeFile(const char *path,const void *buf,unsigned int len);
+
+	/**
+	 * Write a block of data to disk, replacing any current file contents
+	 *
+	 * @param path Path to write
+	 * @param s Data to write
+	 * @return True if entire file was successfully written
+	 */
+	static inline bool writeFile(const char *path,const std::string &s)
+	{
+		return writeFile(path,s.data(),(unsigned int)s.length());
+	}
+};
+
+} // namespace ZeroTier
+
+#endif
+