فهرست منبع

More software update work, and settings in local.conf.

Adam Ierymenko 8 سال پیش
والد
کامیت
c6b0b07605
6فایلهای تغییر یافته به همراه142 افزوده شده و 129 حذف شده
  1. 26 14
      make-freebsd.mk
  2. 18 46
      make-linux.mk
  3. 16 25
      make-mac.mk
  4. 16 3
      service/OneService.cpp
  5. 39 35
      service/SoftwareUpdater.cpp
  6. 27 6
      service/SoftwareUpdater.hpp

+ 26 - 14
make-freebsd.mk

@@ -1,6 +1,5 @@
 CC=cc
 CXX=c++
-
 INCLUDES=
 DEFS=
 LIBS=
@@ -8,11 +7,6 @@ LIBS=
 include objects.mk
 OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o
 
-# "make official" is a shortcut for this
-ifeq ($(ZT_OFFICIAL_RELEASE),1)
-	DEFS+=-DZT_OFFICIAL_RELEASE
-endif
-
 # Build with ZT_ENABLE_CLUSTER=1 to build with cluster support
 ifeq ($(ZT_ENABLE_CLUSTER),1)
 	DEFS+=-DZT_ENABLE_CLUSTER
@@ -34,6 +28,32 @@ else
 	STRIP=strip --strip-all
 endif
 
+# Determine system build architecture from compiler target
+CC_MACH=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
+ZT_ARCHITECTURE=0
+ifeq ($(CC_MACH),x86_64)
+        ZT_ARCHITECTURE=2
+endif
+ifeq ($(CC_MACH),amd64)
+        ZT_ARCHITECTURE=2
+endif
+ifeq ($(CC_MACH),i386)
+        ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),i686)
+        ZT_ARCHITECTURE=1
+endif
+ifeq ($(CC_MACH),arm)
+        ZT_ARCHITECTURE=3
+endif
+ifeq ($(CC_MACH),arm64)
+        ZT_ARCHITECTURE=4
+endif
+ifeq ($(CC_MACH),aarch64)
+        ZT_ARCHITECTURE=4
+endif
+DEFS+=-DZT_BUILD_PLATFORM=7 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="disable"
+
 CXXFLAGS+=$(CFLAGS) -fno-rtti
 
 all:	one
@@ -48,18 +68,10 @@ selftest:	$(OBJS) selftest.o
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
 	$(STRIP) zerotier-selftest
 
-# No installer on FreeBSD yet
-#installer: one FORCE
-#	./buildinstaller.sh
-
 clean:
 	rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-*
 
 debug:	FORCE
 	make -j 4 ZT_DEBUG=1
 
-#official: FORCE
-#	make -j 4 ZT_OFFICIAL_RELEASE=1
-#	./buildinstaller.sh
-
 FORCE:

+ 18 - 46
make-linux.mk

@@ -1,24 +1,3 @@
-#
-# Makefile for ZeroTier One on Linux
-#
-# This is confirmed to work on distributions newer than CentOS 6 (the
-# one used for reference builds) and on 32 and 64 bit x86 and ARM
-# machines. It should also work on other 'normal' machines and recent
-# distributions. Editing might be required for tiny devices or weird
-# distros.
-#
-# Targets
-#   one: zerotier-one and symlinks (cli and idtool)
-#   manpages: builds manpages, requires 'ronn' or nodeJS (will use either)
-#   all: builds 'one' and 'manpages'
-#   selftest: zerotier-selftest
-#   debug: builds 'one' and 'selftest' with tracing and debug flags
-#   clean: removes all built files, objects, other trash
-#   distclean: removes a few other things that might be present
-#   debian: build DEB packages; deb dev tools must be present
-#   redhat: build RPM packages; rpm dev tools must be present
-#
-
 # Automagically pick clang or gcc, with preference for clang
 # This is only done if we have not overridden these with an environment or CLI variable
 ifeq ($(origin CC),default)
@@ -28,8 +7,6 @@ ifeq ($(origin CXX),default)
 	CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
 endif
 
-#UNAME_M=$(shell $(CC) -dumpmachine | cut -d '-' -f 1)
-
 INCLUDES?=
 DEFS?=-D_FORTIFY_SOURCE=2
 LDLIBS?=
@@ -52,28 +29,23 @@ else
 	DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER
 endif
 
-ifeq ($(ZT_USE_MINIUPNPC),1)
-	OBJS+=osdep/PortMapper.o
-
-	DEFS+=-DZT_USE_MINIUPNPC
-
-	# Auto-detect libminiupnpc at least v2.0
-	MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
-	ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
-		DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
-		LDLIBS+=-lminiupnpc
-	else
-		DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
-		OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
-	endif
-
-	# Auto-detect libnatpmp
-	ifeq ($(wildcard /usr/include/natpmp.h),)
-		OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
-	else
-		LDLIBS+=-lnatpmp
-		DEFS+=-DZT_USE_SYSTEM_NATPMP
-	endif
+# Auto-detect miniupnpc and nat-pmp as well and use system libs if present,
+# otherwise build into binary as done on Mac and Windows.
+OBJS+=osdep/PortMapper.o
+DEFS+=-DZT_USE_MINIUPNPC
+MINIUPNPC_IS_NEW_ENOUGH=$(shell grep -sqr '.*define.*MINIUPNPC_VERSION.*"2.."' /usr/include/miniupnpc/miniupnpc.h && echo 1)
+ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
+	DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
+	LDLIBS+=-lminiupnpc
+else
+	DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING=\"Linux\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+	OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
+endif
+ifeq ($(wildcard /usr/include/natpmp.h),)
+	OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o
+else
+	LDLIBS+=-lnatpmp
+	DEFS+=-DZT_USE_SYSTEM_NATPMP
 endif
 
 ifeq ($(ZT_ENABLE_CLUSTER),1)
@@ -137,7 +109,7 @@ endif
 ifeq ($(CC_MACH),aarch64)
         ZT_ARCHITECTURE=4
 endif
-DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE)
+DEFS+=-DZT_BUILD_PLATFORM=1 -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
 
 all:	one
 

+ 16 - 25
make-mac.mk

@@ -1,45 +1,39 @@
-ifeq ($(origin CC),default)
-	CC=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
-endif
-ifeq ($(origin CXX),default)
-	CXX=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
-endif
-
+CC=clang
+CXX=clang++
 INCLUDES=
 DEFS=
 LIBS=
-ARCH_FLAGS=-arch x86_64
-
-include objects.mk
-OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o
-
-# Disable codesign since open source users will not have ZeroTier's certs
+ARCH_FLAGS=
 CODESIGN=echo
 PRODUCTSIGN=echo
 CODESIGN_APP_CERT=
 CODESIGN_INSTALLER_CERT=
 
-# Build with libminiupnpc by default for Mac -- desktops/laptops almost always want this
-ZT_USE_MINIUPNPC?=1
+# 3 == MacOS, 2 == X64 (the only arch for MacOS right now)
+DEFS+=-DZT_BUILD_PLATFORM=3 -DZT_BUILD_ARCHITECTURE=2
 
-# For internal use only -- signs everything with ZeroTier's developer cert
+include objects.mk
+OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o
+
+# Official releases are signed with our Apple cert and apply software updates by default
 ifeq ($(ZT_OFFICIAL_RELEASE),1)
-	DEFS+=-DZT_OFFICIAL_RELEASE
+	DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="apply"
 	ZT_USE_MINIUPNPC=1
 	CODESIGN=codesign
 	PRODUCTSIGN=productsign
 	CODESIGN_APP_CERT="Developer ID Application: ZeroTier, Inc (8ZD9JUCZ4V)"
 	CODESIGN_INSTALLER_CERT="Developer ID Installer: ZeroTier, Inc (8ZD9JUCZ4V)"
+else
+	DEFS+=-DZT_SOFTWARE_UPDATE_DEFAULT="download"
 endif
 
 ifeq ($(ZT_ENABLE_CLUSTER),1)
 	DEFS+=-DZT_ENABLE_CLUSTER
 endif
 
-ifeq ($(ZT_USE_MINIUPNPC),1)
-	DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
-	OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
-endif
+# Build miniupnpc and nat-pmp as included libraries -- extra defs are required for these sources
+DEFS+=-DMACOSX -DZT_USE_MINIUPNPC -DMINIUPNP_STATICLIB -D_DARWIN_C_SOURCE -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -DOS_STRING=\"Darwin/15.0.0\" -DMINIUPNPC_VERSION_STRING=\"2.0\" -DUPNP_VERSION_STRING=\"UPnP/1.1\" -DENABLE_STRNATPMPERR
+OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o osdep/PortMapper.o
 
 # Debug mode -- dump trace output, build binary with -g
 ifeq ($(ZT_DEBUG),1)
@@ -55,10 +49,7 @@ else
 	STRIP=strip
 endif
 
-CXXFLAGS=$(CFLAGS) -mmacosx-version-min=10.7 -std=c++11 -stdlib=libc++ 
-
-# 3 == MacOS, 2 == X64
-DEFS+=-DZT_BUILD_PLATFORM=3 -DZT_BUILD_ARCHITECTURE=2
+CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++ 
 
 all: one macui
 

+ 16 - 3
service/OneService.cpp

@@ -264,6 +264,7 @@ public:
 	Phy<OneServiceImpl *> _phy;
 	Node *_node;
 	SoftwareUpdater *_updater;
+	bool _updateAutoApply;
 	unsigned int _primaryPort;
 
 	// Local configuration and memo-ized static path definitions
@@ -367,6 +368,7 @@ public:
 		,_phy(this,false,true)
 		,_node((Node *)0)
 		,_updater((SoftwareUpdater *)0)
+		,_updateAutoApply(false)
 		,_primaryPort(port)
 		,_controlPlane((ControlPlane *)0)
 		,_lastDirectReceiveFromGlobal(0)
@@ -454,9 +456,6 @@ public:
 				_node = new Node(this,&cb,OSUtils::now());
 			}
 
-			_updater = new SoftwareUpdater(*_node,_homePath);
-			_updater->loadUpdatesToDistribute();
-
 			// Read local configuration
 			{
 				uint64_t trustedPathIds[ZT_MAX_TRUSTED_PATHS];
@@ -982,6 +981,20 @@ public:
 				_node->setRelayPolicy(ZT_RELAY_POLICY_NEVER);
 			else _node->setRelayPolicy(ZT_RELAY_POLICY_TRUSTED);
 
+			const std::string up(OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT));
+			const bool udist = OSUtils::jsonBool(settings["softwareUpdateDist"],false);
+			if (((up == "apply")||(up == "download"))||(udist)) {
+				if (!_updater)
+					_updater = new SoftwareUpdater(*_node,_homePath);
+				_updateAutoApply = (up == "apply");
+				_updater->setUpdateDistribution(udist);
+				_updater->setChannel(OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL));
+			} else {
+				delete _updater;
+				_updater = (SoftwareUpdater *)0;
+				_updateAutoApply = false;
+			}
+
 			json &ignoreIfs = settings["interfacePrefixBlacklist"];
 			if (ignoreIfs.is_array()) {
 				for(unsigned long i=0;i<ignoreIfs.size();++i) {

+ 39 - 35
service/SoftwareUpdater.cpp

@@ -257,6 +257,7 @@ SoftwareUpdater::SoftwareUpdater(Node &node,const std::string &homePath) :
 	_node(node),
 	_lastCheckTime(0),
 	_homePath(homePath),
+	_channel(ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL),
 	_latestBinLength(0),
 	_latestBinValid(false)
 {
@@ -266,28 +267,31 @@ SoftwareUpdater::~SoftwareUpdater()
 {
 }
 
-void SoftwareUpdater::loadUpdatesToDistribute()
+void SoftwareUpdater::setUpdateDistribution(bool distribute)
 {
-	std::string udd(_homePath + ZT_PATH_SEPARATOR_S + "update-dist.d");
-	std::vector<std::string> ud(OSUtils::listDirectory(udd.c_str()));
-	for(std::vector<std::string>::iterator u(ud.begin());u!=ud.end();++u) {
-		// Each update has a companion .json file describing it. Other files are ignored.
-		if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) {
-			std::string buf;
-			if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) {
-				try {
-					_D d;
-					d.meta = OSUtils::jsonParse(buf);
-					std::string metaHash = OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]);
-					if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5)).c_str(),d.bin))) {
-						uint8_t sha512[ZT_SHA512_DIGEST_LEN];
-						SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length());
-						if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
-							_dist[Array<uint8_t,16>(sha512)] = d;
-							printf("update-dist.d: %s\n",u->c_str());
+	_dist.clear();
+	if (distribute) {
+		std::string udd(_homePath + ZT_PATH_SEPARATOR_S + "update-dist.d");
+		std::vector<std::string> ud(OSUtils::listDirectory(udd.c_str()));
+		for(std::vector<std::string>::iterator u(ud.begin());u!=ud.end();++u) {
+			// Each update has a companion .json file describing it. Other files are ignored.
+			if ((u->length() > 5)&&(u->substr(u->length() - 5,5) == ".json")) {
+				std::string buf;
+				if (OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + *u).c_str(),buf)) {
+					try {
+						_D d;
+						d.meta = OSUtils::jsonParse(buf);
+						std::string metaHash = OSUtils::jsonBinFromHex(d.meta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH]);
+						if ((metaHash.length() == ZT_SHA512_DIGEST_LEN)&&(OSUtils::readFile((udd + ZT_PATH_SEPARATOR_S + u->substr(0,u->length() - 5)).c_str(),d.bin))) {
+							uint8_t sha512[ZT_SHA512_DIGEST_LEN];
+							SHA512::hash(sha512,d.bin.data(),(unsigned int)d.bin.length());
+							if (!memcmp(sha512,metaHash.data(),ZT_SHA512_DIGEST_LEN)) { // double check that hash in JSON is correct
+								_dist[Array<uint8_t,16>(sha512)] = d;
+								printf("update-dist.d: %s\n",u->c_str());
+							}
 						}
-					}
-				} catch ( ... ) {} // ignore bad meta JSON, etc.
+					} catch ( ... ) {} // ignore bad meta JSON, etc.
+				}
 			}
 		}
 	}
@@ -393,18 +397,17 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 					idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 18) << 16;
 					idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 19) << 8;
 					idx |= (unsigned long)*(reinterpret_cast<const uint8_t *>(data) + 20);
+					printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_latestBin.length());
 					if (idx == _latestBin.length()) {
 						_latestBin.append(reinterpret_cast<const char *>(data) + 21,len - 21);
-					}
-					printf("<< DATA @%u / %u bytes (we now have %u bytes)\n",(unsigned int)idx,(unsigned int)(len - 21),(unsigned int)_latestBin.length());
-
-					if (_latestBin.length() < _latestBinLength) {
-						Buffer<128> gd;
-						gd.append((uint8_t)VERB_GET_DATA);
-						gd.append(_latestBinHashPrefix.data,16);
-						gd.append((uint32_t)_latestBin.length());
-						_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
-						printf(">> GET_DATA @%u\n",(unsigned int)_latestBin.length());
+						if (_latestBin.length() < _latestBinLength) {
+							Buffer<128> gd;
+							gd.append((uint8_t)VERB_GET_DATA);
+							gd.append(_latestBinHashPrefix.data,16);
+							gd.append((uint32_t)_latestBin.length());
+							_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+							printf(">> GET_DATA @%u\n",(unsigned int)_latestBin.length());
+						}
 					}
 				}
 				break;
@@ -417,7 +420,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 	}
 }
 
-nlohmann::json SoftwareUpdater::check(const uint64_t now)
+bool SoftwareUpdater::check(const uint64_t now)
 {
 	if ((now - _lastCheckTime) >= ZT_SOFTWARE_UPDATE_CHECK_PERIOD) {
 		_lastCheckTime = now;
@@ -439,7 +442,7 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now)
 			ZT_BUILD_PLATFORM,
 			ZT_BUILD_ARCHITECTURE,
 			(int)ZT_VENDOR_ZEROTIER,
-			"release");
+			_channel.c_str());
 		_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len);
 		printf(">> GET_LATEST\n");
 	}
@@ -449,13 +452,14 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now)
 			if (_latestBinValid) {
 				return _latestMeta;
 			} else {
-				// This is the important security verification part!
+				// This is the very important security validation part that makes sure
+				// this software update doesn't have cooties.
 
 				try {
 					// (1) Check the hash itself to make sure the image is basically okay
 					uint8_t sha512[ZT_SHA512_DIGEST_LEN];
 					SHA512::hash(sha512,_latestBin.data(),(unsigned int)_latestBin.length());
-					if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"")) {
+					if (Utils::hex(sha512,ZT_SHA512_DIGEST_LEN) == OSUtils::jsonString(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH],"~")) {
 						// (2) Check signature by signing authority
 						std::string sig(OSUtils::jsonBinFromHex(_latestMeta[ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE]));
 						if (Identity(ZT_SOFTWARE_UPDATE_SIGNING_AUTHORITY).verify(_latestBin.data(),(unsigned int)_latestBin.length(),sig.data(),(unsigned int)sig.length())) {
@@ -489,7 +493,7 @@ nlohmann::json SoftwareUpdater::check(const uint64_t now)
 
 void SoftwareUpdater::apply()
 {
-	if ((_latestBin.length() == _latestBinLength)&&(_latestBinLength > 0)&&(_latestBinValid)) {
+	if ((_latestBin.length() > 0)&&(_latestBinValid)) {
 	}
 }
 

+ 27 - 6
service/SoftwareUpdater.hpp

@@ -64,19 +64,25 @@
 //#define ZT_SOFTWARE_UPDATE_CHECK_PERIOD (60 * 60 * 1000)
 #define ZT_SOFTWARE_UPDATE_CHECK_PERIOD 5000
 
+/**
+ * Default update channel
+ */
+#define ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL "release"
+
 #define ZT_SOFTWARE_UPDATE_JSON_VERSION_MAJOR "versionMajor"
 #define ZT_SOFTWARE_UPDATE_JSON_VERSION_MINOR "versionMinor"
 #define ZT_SOFTWARE_UPDATE_JSON_VERSION_REVISION "versionRev"
-#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner"
 #define ZT_SOFTWARE_UPDATE_JSON_PLATFORM "platform"
 #define ZT_SOFTWARE_UPDATE_JSON_ARCHITECTURE "arch"
 #define ZT_SOFTWARE_UPDATE_JSON_VENDOR "vendor"
 #define ZT_SOFTWARE_UPDATE_JSON_CHANNEL "channel"
+#define ZT_SOFTWARE_UPDATE_JSON_EXPECT_SIGNED_BY "expectedSigner"
 #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNED_BY "updateSigner"
 #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIGNATURE "updateSig"
 #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_HASH "updateHash"
 #define ZT_SOFTWARE_UPDATE_JSON_UPDATE_SIZE "updateSize"
-#define ZT_SOFTWARE_UPDATE_JSON_EXEC_ARGS "updateExecArgs"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_EXEC_ARGS "updateExecArgs"
+#define ZT_SOFTWARE_UPDATE_JSON_UPDATE_URL "updateUrl"
 
 namespace ZeroTier {
 
@@ -123,9 +129,11 @@ public:
 	~SoftwareUpdater();
 
 	/**
-	 * Load update-dist.d if it exists
+	 * Set whether or not we will distribute updates
+	 *
+	 * @param distribute If true, scan update-dist.d now and distribute updates found there -- if false, clear and stop distributing
 	 */
-	void loadUpdatesToDistribute();
+	void setUpdateDistribution(bool distribute);
 
 	/**
 	 * Handle a software update user message
@@ -141,9 +149,14 @@ public:
 	 *
 	 * It should be called about every 10 seconds.
 	 *
-	 * @return Null JSON object or update information if there is an update downloaded and ready
+	 * @return True if we've downloaded and verified an update
 	 */
-	nlohmann::json check(const uint64_t now);
+	bool check(const uint64_t now);
+
+	/**
+	 * @return Meta-data for downloaded update or NULL if none
+	 */
+	inline const nlohmann::json &pending() const { return _latestMeta; }
 
 	/**
 	 * Apply any ready update now
@@ -153,10 +166,18 @@ public:
 	 */
 	void apply();
 
+	/**
+	 * Set software update channel
+	 *
+	 * @param channel 'release', 'beta', etc.
+	 */
+	inline void setChannel(const std::string &channel) { _channel = channel; }
+
 private:
 	Node &_node;
 	uint64_t _lastCheckTime;
 	std::string _homePath;
+	std::string _channel;
 
 	// Offered software updates if we are an update host (we have update-dist.d and update hosting is enabled)
 	struct _D