Browse Source

Certificates, etc... work in progress.

Adam Ierymenko 5 years ago
parent
commit
f447608d6b

+ 2 - 2
Makefile

@@ -25,7 +25,7 @@ central-controller-docker:
 	docker build -t registry.zerotier.com/zerotier-central/ztcentral-controller:${TIMESTAMP} -f controller/central-docker/Dockerfile .
 
 clean:
-	rm -rf ${BUILDDIR} cmake-build-*
+	rm -rf ${BUILDDIR}
 
 distclean:
-	rm -rf ${BUILDDIR} cmake-build-*
+	rm -rf ${BUILDDIR}

+ 4 - 5
cmd/zerotier/cli/help.go

@@ -34,7 +34,8 @@ Global Options:
 Commands:
   help                                   Show this help
   version                                Print version
-  service                                Start node (see below)
+  service [-options]                     Start node (see below)
+    -d                                   Fork into background (Unix only)
   status                                 Show node status and configuration
   join [-options] <network>              Join a virtual network
     -a <token>                           Token to submit to controller
@@ -56,10 +57,8 @@ Commands:
     locator <locator>                    Explicitly update peer locator
   roots                                  List root peers
   root [command]                       - Root management commands
-    add <identity> [endpoint]            Designate a peer as a root
-    remove <address>                     Un-designate a peer as a root
-    subscribe <url> [<key hash>]         Subscribe to a set of roots
-    unsubscribe <url | key hash>         Unsubscribe from a set of roots
+    add <identity | url> [endpoint]      Add a root or a root set
+    remove <address | url | serial>      Remove a root or root set
   set [option] [value]                 - Get or set a core config option
     port <port>                          Primary P2P port
     secondaryport <port/0>               Secondary P2P port (0 to disable)

+ 14 - 7
cmd/zerotier/cli/service.go

@@ -15,8 +15,11 @@ package cli
 
 import (
 	"fmt"
+	"io/ioutil"
 	"os"
 	"os/signal"
+	"path"
+	"strconv"
 	"syscall"
 
 	"zerotier/pkg/zerotier"
@@ -28,16 +31,20 @@ func Service(basePath, authToken string, args []string) {
 		os.Exit(1)
 	}
 
+	pidPath := path.Join(basePath, "zerotier.pid")
+	_ = ioutil.WriteFile(pidPath, []byte(strconv.FormatInt(int64(os.Getpid()), 10)), 0644)
+
 	node, err := zerotier.NewNode(basePath)
 	if err != nil {
 		fmt.Println("FATAL: error initializing node: " + err.Error())
-		os.Exit(1)
+	} else {
+		osSignalChannel := make(chan os.Signal, 2)
+		signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGSTOP)
+		signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2, syscall.SIGPIPE, syscall.SIGHUP)
+		<-osSignalChannel
+		node.Close()
 	}
 
-	osSignalChannel := make(chan os.Signal, 2)
-	signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS)
-	signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
-	<-osSignalChannel
-	node.Close()
-	os.Exit(0)
+	_ = os.Remove(pidPath)
+	os.Exit(1)
 }

+ 44 - 0
core/Blob.hpp

@@ -0,0 +1,44 @@
+/*
+ * Copyright (c)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#ifndef ZT_BLOB_HPP
+#define ZT_BLOB_HPP
+
+#include "Constants.hpp"
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Container for arbitrary bytes for use in collections
+ *
+ * @tparam S Size of container in bytes
+ */
+template<unsigned int S>
+struct Blob
+{
+	uint8_t data[S];
+
+	ZT_INLINE Blob() { Utils::zero<S>(data); }
+
+	ZT_INLINE bool operator==(const Blob &b) const noexcept { return (memcmp(data,b.data,S) == 0); }
+	ZT_INLINE bool operator!=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) != 0); }
+	ZT_INLINE bool operator<(const Blob &b) const noexcept { return (memcmp(data,b.data,S) < 0); }
+	ZT_INLINE bool operator>(const Blob &b) const noexcept { return (memcmp(data,b.data,S) > 0); }
+	ZT_INLINE bool operator<=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) <= 0); }
+	ZT_INLINE bool operator>=(const Blob &b) const noexcept { return (memcmp(data,b.data,S) >= 0); }
+};
+
+} // namespace ZeroTier
+
+#endif

+ 5 - 2
core/CMakeLists.txt

@@ -2,13 +2,14 @@ cmake_minimum_required (VERSION 2.8)
 project(zt_core)
 
 configure_file(
-        version.h.in
-        version.h
+	version.h.in
+	version.h
 )
 
 set(core_headers
 	zerotier.h
 	Address.hpp
+	Blob.hpp
 	Buf.hpp
 	C25519.hpp
 	Capability.hpp
@@ -24,6 +25,7 @@ set(core_headers
 	Expect.hpp
 	FCV.hpp
 	Fingerprint.hpp
+	IdentificationCertificate.hpp
 	Identity.hpp
 	InetAddress.hpp
 	Locator.hpp
@@ -67,6 +69,7 @@ set(core_src
 	Dictionary.cpp
 	ECC384.cpp
 	Endpoint.cpp
+	IdentificationCertificate.cpp
 	Identity.cpp
 	InetAddress.cpp
 	Locator.cpp

+ 42 - 91
core/Dictionary.cpp

@@ -12,56 +12,64 @@
 /****/
 
 #include "Dictionary.hpp"
-#include "SHA512.hpp"
 
 namespace ZeroTier {
 
 Dictionary::Dictionary()
-{
-}
+{}
+
+Dictionary::~Dictionary()
+{}
 
-Vector<uint8_t> &Dictionary::operator[](const char *k)
+Vector< uint8_t > &Dictionary::operator[](const char *k)
 {
-	return m_entries[s_key(k)];
+	if (k)
+		return m_entries[s_key(k)];
+	else return m_entries[""];
 }
 
-const Vector<uint8_t> &Dictionary::operator[](const char *k) const
+const Vector< uint8_t > &Dictionary::operator[](const char *k) const
 {
-	static const Vector<uint8_t> s_emptyEntry;
-	SortedMap< String, Vector<uint8_t> >::const_iterator e(m_entries.find(s_key(k)));
-	return (e == m_entries.end()) ? s_emptyEntry : e->second;
+	static const Vector< uint8_t > s_emptyEntry;
+	if (k) {
+		SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(s_key(k)));
+		return (e == m_entries.end()) ? s_emptyEntry : e->second;
+	} else {
+		SortedMap< String, Vector< uint8_t > >::const_iterator e(m_entries.find(""));
+		return (e == m_entries.end()) ? s_emptyEntry : e->second;
+	}
 }
 
 void Dictionary::add(const char *k, bool v)
 {
-	Vector<uint8_t> &e = (*this)[k];
+	Vector< uint8_t > &e = (*this)[k];
 	e.resize(2);
-	e[0] = (uint8_t) (v ? '1' : '0');
+	e[0] = (uint8_t)(v ? '1' : '0');
 	e[1] = 0;
 }
 
 void Dictionary::add(const char *k, const Address &v)
 {
-	Vector<uint8_t> &e = (*this)[k];
+	Vector< uint8_t > &e = (*this)[k];
 	e.resize(ZT_ADDRESS_STRING_SIZE_MAX);
-	v.toString((char *) e.data());
+	v.toString((char *)e.data());
 }
 
 void Dictionary::add(const char *k, const char *v)
 {
 	if ((v) && (*v)) {
-		Vector<uint8_t> &e = (*this)[k];
+		Vector< uint8_t > &e = (*this)[k];
 		e.clear();
 		while (*v)
-			e.push_back((uint8_t) *(v++));
+			e.push_back((uint8_t)*(v++));
 	}
 }
 
 void Dictionary::add(const char *k, const void *data, unsigned int len)
 {
-	Vector<uint8_t> &e = (*this)[k];
+	Vector< uint8_t > &e = (*this)[k];
 	if (len != 0) {
-		e.assign((const uint8_t *) data, (const uint8_t *) data + len);
+		e.assign((const uint8_t *)data, (const uint8_t *)data + len);
 	} else {
 		e.clear();
 	}
@@ -69,9 +77,9 @@ void Dictionary::add(const char *k, const void *data, unsigned int len)
 
 bool Dictionary::getB(const char *k, bool dfl) const
 {
-	const Vector<uint8_t> &e = (*this)[k];
+	const Vector< uint8_t > &e = (*this)[k];
 	if (!e.empty()) {
-		switch ((char) e[0]) {
+		switch ((char)e[0]) {
 			case '1':
 			case 't':
 			case 'T':
@@ -89,15 +97,15 @@ uint64_t Dictionary::getUI(const char *k, uint64_t dfl) const
 {
 	uint8_t tmp[18];
 	uint64_t v = dfl;
-	const Vector<uint8_t> &e = (*this)[k];
+	const Vector< uint8_t > &e = (*this)[k];
 	if (!e.empty()) {
 		if (e.back() != 0) {
 			const unsigned long sl = e.size();
 			Utils::copy(tmp, e.data(), (sl > 17) ? 17 : sl);
 			tmp[17] = 0;
-			return Utils::unhex((const char *) tmp);
+			return Utils::unhex((const char *)tmp);
 		}
-		return Utils::unhex((const char *) e.data());
+		return Utils::unhex((const char *)e.data());
 	}
 	return v;
 }
@@ -106,13 +114,13 @@ char *Dictionary::getS(const char *k, char *v, const unsigned int cap) const
 {
 	if (cap == 0) // sanity check
 		return v;
-	const Vector<uint8_t> &e = (*this)[k];
+	const Vector< uint8_t > &e = (*this)[k];
 	unsigned int i = 0;
 	const unsigned int last = cap - 1;
 	for (;;) {
 		if ((i == last) || (i >= (unsigned int)e.size()))
 			break;
-		v[i] = (char) e[i];
+		v[i] = (char)e[i];
 		++i;
 	}
 	v[i] = 0;
@@ -124,16 +132,14 @@ void Dictionary::clear()
 	m_entries.clear();
 }
 
-void Dictionary::encode(Vector<uint8_t> &out, const bool omitSignatureFields) const
+void Dictionary::encode(Vector< uint8_t > &out) const
 {
 	out.clear();
-	for (SortedMap< String, Vector<uint8_t> >::const_iterator ti(m_entries.begin());ti != m_entries.end();++ti) {
-		if ((!omitSignatureFields) || ((ti->first != ZT_DICTIONARY_SIGNATURE_KEY))) {
-			s_appendKey(out, ti->first.data());
-			for (Vector<uint8_t>::const_iterator i(ti->second.begin());i != ti->second.end();++i)
-				s_appendValueByte(out, *i);
-			out.push_back((uint8_t) '\n');
-		}
+	for (SortedMap< String, Vector< uint8_t > >::const_iterator ti(m_entries.begin()); ti != m_entries.end(); ++ti) {
+		s_appendKey(out, ti->first.data());
+		for (Vector< uint8_t >::const_iterator i(ti->second.begin()); i != ti->second.end(); ++i)
+			s_appendValueByte(out, *i);
+		out.push_back((uint8_t)'\n');
 	}
 	out.push_back(0);
 }
@@ -142,9 +148,9 @@ bool Dictionary::decode(const void *data, unsigned int len)
 {
 	clear();
 	String k;
-	Vector<uint8_t> *v = nullptr;
+	Vector< uint8_t > *v = nullptr;
 	bool escape = false;
-	for (unsigned int di = 0;di < len;++di) {
+	for (unsigned int di = 0; di < len; ++di) {
 		uint8_t c = reinterpret_cast<const uint8_t *>(data)[di];
 		if (!c) break;
 		if (v) {
@@ -168,7 +174,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
 						break;
 				}
 			} else {
-				if (c == (uint8_t) '\n') {
+				if (c == (uint8_t)'\n') {
 					k.clear();
 					v = nullptr;
 				} else if (c == 92) { // backslash
@@ -180,7 +186,7 @@ bool Dictionary::decode(const void *data, unsigned int len)
 		} else {
 			if ((c < 33) || (c > 126) || (c == 92)) {
 				return false;
-			} else if (c == (uint8_t) '=') {
+			} else if (c == (uint8_t)'=') {
 				k.push_back(0);
 				v = &m_entries[k];
 			} else if (k.size() < 7) {
@@ -193,59 +199,4 @@ bool Dictionary::decode(const void *data, unsigned int len)
 	return true;
 }
 
-void Dictionary::sign(
-	const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE],
-	const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
-	const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE],
-	const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE])
-{
-	Vector<uint8_t> buf;
-	encode(buf, true);
-
-	uint8_t c25519Signature[ZT_C25519_SIGNATURE_LEN];
-	C25519::sign(c25519PrivateKey, c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature);
-
-	uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
-	static_assert(ZT_ECC384_SIGNATURE_HASH_SIZE == ZT_SHA384_DIGEST_SIZE,"size mismatch");
-	SHA384(hbuf, buf.data(), (unsigned int)buf.size());
-	uint8_t p384Signature[ZT_ECC384_SIGNATURE_SIZE];
-	ECC384ECDSASign(p384PrivateKey, hbuf, p384Signature);
-
-	SHA384(hbuf, c25519PublicKey, ZT_C25519_COMBINED_PUBLIC_KEY_SIZE, p384PublicKey, ZT_ECC384_PUBLIC_KEY_SIZE);
-
-	Dictionary signature;
-	signature["kh"].assign(hbuf, hbuf + ZT_SHA384_DIGEST_SIZE);
-	signature["ed25519"].assign(c25519Signature, c25519Signature + ZT_C25519_SIGNATURE_LEN);
-	signature["p384"].assign(p384Signature, p384Signature + ZT_ECC384_SIGNATURE_SIZE);
-	signature.encode((*this)[ZT_DICTIONARY_SIGNATURE_KEY], true);
-}
-
-bool Dictionary::verify(
-	const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
-	const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const
-{
-	try {
-		const Vector< uint8_t > &data = (*this)[ZT_DICTIONARY_SIGNATURE_KEY];
-		if (data.empty())
-			return false;
-		Dictionary signature;
-		if (!signature.decode(data.data(), (unsigned int)data.size()))
-			return false;
-		const Vector< uint8_t > &p384Signature = signature["p384"];
-		const Vector< uint8_t > &c25519Signature = signature["ed25519"];
-		if ((p384Signature.size() != ZT_ECC384_SIGNATURE_SIZE) || (c25519Signature.size() != ZT_C25519_SIGNATURE_LEN))
-			return false;
-
-		Vector< uint8_t > buf;
-		encode(buf, true);
-
-		if (C25519::verify(c25519PublicKey, buf.data(), (unsigned int)buf.size(), c25519Signature.data(), (unsigned int)c25519Signature.size())) {
-			uint8_t hbuf[ZT_ECC384_SIGNATURE_HASH_SIZE];
-			SHA384(hbuf, buf.data(), (unsigned int)buf.size());
-			return ECC384ECDSAVerify(p384PublicKey, hbuf, p384Signature.data());
-		}
-	} catch ( ... ) {}
-	return false;
-}
-
 } // namespace ZeroTier

+ 49 - 42
core/Dictionary.hpp

@@ -19,10 +19,6 @@
 #include "Address.hpp"
 #include "Buf.hpp"
 #include "Containers.hpp"
-#include "C25519.hpp"
-#include "ECC384.hpp"
-
-#define ZT_DICTIONARY_SIGNATURE_KEY "@S"
 
 namespace ZeroTier {
 
@@ -46,10 +42,10 @@ class Identity;
 class Dictionary
 {
 public:
-	typedef SortedMap< String, Vector < uint8_t > >
-	::const_iterator const_iterator;
+	typedef SortedMap< String, Vector < uint8_t > >::const_iterator const_iterator;
 
 	Dictionary();
+	~Dictionary();
 
 	/**
 	 * Get a reference to a value
@@ -90,12 +86,17 @@ public:
 	 * @param k Key to set
 	 * @param v Integer to set, will be cast to uint64_t and stored as hex
 	 */
-	template< typename I >
-	ZT_INLINE void add(const char *const k, I v)
-	{
-		char buf[17];
-		add(k, Utils::hex((uint64_t)(v), buf));
-	}
+	ZT_INLINE void add(const char *const k, const uint64_t v)
+	{ char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); }
+
+	/**
+	 * Add an integer as a hexadecimal string value
+	 *
+	 * @param k Key to set
+	 * @param v Integer to set, will be cast to uint64_t and stored as hex
+	 */
+	ZT_INLINE void add(const char *const k, const int64_t v)
+	{ char buf[17]; add(k, Utils::hex((uint64_t)(v), buf)); }
 
 	/**
 	 * Add an address in 10-digit hex string format
@@ -145,6 +146,7 @@ public:
 	/**
 	 * Get an object supporting the marshal/unmarshal interface pattern
 	 * 
+	 * @tparam T Object type (inferred)
 	 * @param k Key to look up
 	 * @param obj Object to unmarshal() into
 	 * @return True if unmarshal was successful
@@ -158,6 +160,27 @@ public:
 		return (obj.unmarshal(d.data(), (unsigned int)d.size()) > 0);
 	}
 
+	/**
+	 * Add an object supporting the marshal/unmarshal interface pattern
+	 *
+	 * @tparam T Object type (inferred)
+	 * @param k Key to add
+	 * @param obj Object to marshal() into vector
+	 * @return True if successful
+	 */
+	template< typename T >
+	ZT_INLINE bool addO(const char *k, T &obj)
+	{
+		uint8_t tmp[4096];
+		static_assert(sizeof(tmp) >= T::marshalSizeMax(),"buffer too small");
+		int l = obj.marshal(tmp);
+		if (l > 0) {
+			(*this)[k].assign(tmp, tmp + l);
+			return true;
+		}
+		return false;
+	}
+
 	/**
 	 * Erase all entries in dictionary
 	 */
@@ -179,9 +202,8 @@ public:
 	 * Encode to a string in the supplied vector
 	 *
 	 * @param out String encoded dictionary
-	 * @param omitSignatureFields If true omit the signature fields from encoding (default: false)
 	 */
-	void encode(Vector <uint8_t> &out, bool omitSignatureFields = false) const;
+	void encode(Vector <uint8_t> &out) const;
 
 	/**
 	 * Decode a string encoded dictionary
@@ -195,34 +217,6 @@ public:
 	 */
 	bool decode(const void *data, unsigned int len);
 
-	/**
-	 * Sign this dictionary with both an Ed25519 key and a NIST P-384 key.
-	 *
-	 * This is currently used just for signing root sets for the root subscribe
-	 * feature. It uses both key types for more crypto cowbell.
-	 *
-	 * @param c25519PrivateKey Curve25519 combined key (C25519 and ed25519), though only ed25519 part is used
-	 * @param c25519PublicKey Public part of Curve25519 combined key
-	 * @param p384PrivateKey NIST P-384 private key
-	 * @param p384PublicKey NIST P-384 public key
-	 */
-	void sign(
-		const uint8_t c25519PrivateKey[ZT_C25519_COMBINED_PRIVATE_KEY_SIZE],
-		const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
-		const uint8_t p384PrivateKey[ZT_ECC384_PRIVATE_KEY_SIZE],
-		const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]);
-
-	/**
-	 * Verify this dictionary's signature
-	 *
-	 * @param c25519PublicKey Curve25519 public key
-	 * @param p384PublicKey P-384 public key
-	 * @return True if signatures are valid
-	 */
-	bool verify(
-		const uint8_t c25519PublicKey[ZT_C25519_COMBINED_PUBLIC_KEY_SIZE],
-		const uint8_t p384PublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]) const;
-
 	/**
 	 * Append a key=value pair to a buffer (vector or FCV)
 	 * 
@@ -376,6 +370,19 @@ public:
 		return mlen;
 	}
 
+	static ZT_INLINE char *arraySubscript(char buf[256],const char *name,const unsigned long sub) noexcept
+	{
+		for(unsigned int i=0;i<(256 - 17);++i) {
+			if ((buf[i] = name[i]) == 0) {
+				buf[i++] = '#';
+				Utils::hex(sub, buf + i);
+				return buf;
+			}
+		}
+		buf[0] = 0;
+		return buf;
+	}
+
 private:
 	template< typename V >
 	ZT_INLINE static void s_appendValueByte(V &out, const uint8_t c)

+ 260 - 0
core/IdentificationCertificate.cpp

@@ -0,0 +1,260 @@
+/*
+ * Copyright (c)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#include "IdentificationCertificate.hpp"
+#include "SHA512.hpp"
+
+namespace ZeroTier {
+
+IdentificationCertificate &IdentificationCertificate::operator=(const ZT_IdentificationCertificate &apiCert)
+{
+	Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert);
+	m_identities.clear();
+	m_locators.clear();
+	m_nodes.clear();
+	m_networks.clear();
+	return *this;
+}
+
+IdentificationCertificate &IdentificationCertificate::operator=(const IdentificationCertificate &cert)
+{
+	Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, (const ZT_IdentificationCertificate *)(&cert));
+
+	m_identities.clear();
+	m_locators.clear();
+	m_nodes.clear();
+	m_networks.clear();
+
+	this->subject.nodeCount = 0;
+	this->subject.networkCount = 0;
+
+	if (cert.issuer) {
+		m_identities.push_back(*reinterpret_cast<const Identity *>(cert.issuer));
+		this->issuer = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
+	}
+
+	for (unsigned int i = 0; i < cert.subject.nodeCount; ++i) {
+		if (cert.subject.nodes[i].locator)
+			addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity), *reinterpret_cast<const Locator *>(cert.subject.nodes[i].locator));
+		else if (cert.subject.nodes[i].identity)
+			addSubjectNode(*reinterpret_cast<const Identity *>(cert.subject.nodes[i].identity));
+	}
+
+	for (unsigned int i = 0; i < cert.subject.networkCount; ++i)
+		addSubjectNetwork(cert.subject.networks[i].id, cert.subject.networks[i].controller);
+
+	return *this;
+}
+
+ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id)
+{
+	m_nodes.resize(++this->subject.nodeCount);
+	this->subject.nodes = m_nodes.data();
+	m_identities.push_back(id);
+	m_nodes.back().identity = reinterpret_cast<ZT_Identity *>(&(m_identities.back()));
+	m_nodes.back().locator = nullptr;
+	return &(m_nodes.back());
+}
+
+ZT_IdentificationCertificate_Node *IdentificationCertificate::addSubjectNode(const Identity &id, const Locator &loc)
+{
+	ZT_IdentificationCertificate_Node *n = addSubjectNode(id);
+	m_locators.push_back(loc);
+	n->locator = reinterpret_cast<ZT_Locator *>(&(m_locators.back()));
+	return n;
+}
+
+ZT_IdentificationCertificate_Network *IdentificationCertificate::addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller)
+{
+	m_networks.resize(++this->subject.networkCount);
+	this->subject.networks = m_networks.data();
+	m_networks.back().id = id;
+	Utils::copy< sizeof(ZT_Fingerprint) >(&(m_networks.back().controller), &controller);
+	return &(m_networks.back());
+}
+
+Vector< uint8_t > IdentificationCertificate::encode(const bool omitSignature) const
+{
+	char tmp[256];
+	Vector< uint8_t > enc;
+	Dictionary d;
+
+	d.add("v", (uint64_t)this->version);
+	d.add("mP", (uint64_t)this->maxPathLength);
+	d.add("f", this->flags);
+	d.add("v0", this->validity[0]);
+	d.add("v1", this->validity[1]);
+
+	d.add("s.n[]", (uint64_t)this->subject.nodeCount);
+	for (unsigned int i = 0; i < this->subject.nodeCount; ++i) {
+		d.addO(Dictionary::arraySubscript(tmp, "s.n[].i", i), *reinterpret_cast<const Identity *>(this->subject.nodes[i].identity));
+		if (this->subject.nodes[i].locator)
+			d.addO(Dictionary::arraySubscript(tmp, "s.n[].l", i), *reinterpret_cast<const Locator *>(this->subject.nodes[i].locator));
+	}
+
+	d.add("s.nw[]", (uint64_t)this->subject.networkCount);
+	for (unsigned int i = 0; i < this->subject.networkCount; ++i) {
+		d.add(Dictionary::arraySubscript(tmp, "s.nw[].i", i), this->subject.networks[i].id);
+		Fingerprint fp(this->subject.networks[i].controller);
+		d.addO(Dictionary::arraySubscript(tmp, "s.nw[].c", i), fp);
+	}
+
+	d.add("s.n.c", this->subject.name.country);
+	d.add("s.n.o", this->subject.name.organization);
+	d.add("s.n.u", this->subject.name.unit);
+	d.add("s.n.l", this->subject.name.locality);
+	d.add("s.n.p", this->subject.name.province);
+	d.add("s.n.sA", this->subject.name.streetAddress);
+	d.add("s.n.pC", this->subject.name.postalCode);
+	d.add("s.n.cN", this->subject.name.commonName);
+	d.add("s.n.sN", this->subject.name.serialNo);
+	d.add("s.n.e", this->subject.name.email);
+	d.add("s.n.ur", this->subject.name.url);
+
+	if (this->issuer)
+		d.addO("i", *reinterpret_cast<const Identity *>(this->issuer));
+
+	d.add("iN.c", this->issuerName.country);
+	d.add("iN.o", this->issuerName.organization);
+	d.add("iN.u", this->issuerName.unit);
+	d.add("iN.l", this->issuerName.locality);
+	d.add("iN.p", this->issuerName.province);
+	d.add("iN.sA", this->issuerName.streetAddress);
+	d.add("iN.pC", this->issuerName.postalCode);
+	d.add("iN.cN", this->issuerName.commonName);
+	d.add("iN.sN", this->issuerName.serialNo);
+	d.add("iN.e", this->issuerName.email);
+	d.add("iN.ur", this->issuerName.url);
+
+	if ((!omitSignature) && (this->signatureSize > 0) && (this->signatureSize <= sizeof(this->signature)))
+		d["si"].assign(this->signature, this->signature + this->signatureSize);
+
+	d.encode(enc);
+	return enc;
+}
+
+bool IdentificationCertificate::decode(const Vector< uint8_t > &data)
+{
+	char tmp[256];
+
+	Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this);
+	m_identities.clear();
+	m_locators.clear();
+	m_nodes.clear();
+	m_networks.clear();
+
+	Dictionary d;
+	if (!d.decode(data.data(), (unsigned int)data.size()))
+		return false;
+
+	this->version = (unsigned int)d.getUI("v");
+	this->maxPathLength = (unsigned int)d.getUI("mP");
+	this->flags = d.getUI("f");
+	this->validity[0] = (int64_t)d.getUI("v0");
+	this->validity[1] = (int64_t)d.getUI("v1");
+
+	unsigned int cnt = (unsigned int)d.getUI("s.n[]");
+	for (unsigned int i = 0; i < cnt; ++i) {
+		const Vector< uint8_t > &identityData = d[Dictionary::arraySubscript(tmp, "s.n[].i", i)];
+		if (identityData.empty())
+			return false;
+		Identity id;
+		if (id.unmarshal(identityData.data(), (unsigned int)identityData.size()) <= 0)
+			return false;
+		const Vector< uint8_t > &locatorData = d[Dictionary::arraySubscript(tmp, "s.n[].l", i)];
+		if (!locatorData.empty()) {
+			Locator loc;
+			if (loc.unmarshal(locatorData.data(), (unsigned int)locatorData.size()) <= 0)
+				return false;
+			this->addSubjectNode(id, loc);
+		} else {
+			this->addSubjectNode(id);
+		}
+	}
+
+	cnt = (unsigned int)d.getUI("s.nw[]");
+	for (unsigned int i = 0; i < cnt; ++i) {
+		const uint64_t nwid = d.getUI(Dictionary::arraySubscript(tmp, "s.nw[].i", i));
+		if (nwid == 0)
+			return false;
+		const Vector< uint8_t > &fingerprintData = d[Dictionary::arraySubscript(tmp, "s.nw[].c", i)];
+		if (fingerprintData.empty())
+			return false;
+		Fingerprint fp;
+		if (fp.unmarshal(fingerprintData.data(), (unsigned int)fingerprintData.size()) <= 0)
+			return false;
+		this->addSubjectNetwork(nwid, fp);
+	}
+
+	d.getS("s.n.c", this->subject.name.country, sizeof(this->subject.name.country));
+	d.getS("s.n.o", this->subject.name.organization, sizeof(this->subject.name.organization));
+	d.getS("s.n.u", this->subject.name.unit, sizeof(this->subject.name.unit));
+	d.getS("s.n.l", this->subject.name.locality, sizeof(this->subject.name.locality));
+	d.getS("s.n.p", this->subject.name.province, sizeof(this->subject.name.province));
+	d.getS("s.n.sA", this->subject.name.streetAddress, sizeof(this->subject.name.streetAddress));
+	d.getS("s.n.pC", this->subject.name.postalCode, sizeof(this->subject.name.postalCode));
+	d.getS("s.n.cN", this->subject.name.commonName, sizeof(this->subject.name.commonName));
+	d.getS("s.n.sN", this->subject.name.serialNo, sizeof(this->subject.name.serialNo));
+	d.getS("s.n.e", this->subject.name.email, sizeof(this->subject.name.email));
+	d.getS("s.n.ur", this->subject.name.url, sizeof(this->subject.name.url));
+
+	const Vector< uint8_t > &issuerData = d["i"];
+	if (!issuerData.empty()) {
+		Identity id;
+		if (id.unmarshal(issuerData.data(), (int)issuerData.size()) > 0) {
+			m_identities.push_back(id);
+			this->issuer = reinterpret_cast<const Identity *>(&(m_identities.back()));
+		}
+	}
+
+	d.getS("iN.c", this->issuerName.country, sizeof(this->issuerName.country));
+	d.getS("iN.o", this->issuerName.organization, sizeof(this->issuerName.organization));
+	d.getS("iN.u", this->issuerName.unit, sizeof(this->issuerName.unit));
+	d.getS("iN.l", this->issuerName.locality, sizeof(this->issuerName.locality));
+	d.getS("iN.p", this->issuerName.province, sizeof(this->issuerName.province));
+	d.getS("iN.sA", this->issuerName.streetAddress, sizeof(this->issuerName.streetAddress));
+	d.getS("iN.pC", this->issuerName.postalCode, sizeof(this->issuerName.postalCode));
+	d.getS("iN.cN", this->issuerName.commonName, sizeof(this->issuerName.commonName));
+	d.getS("iN.sN", this->issuerName.serialNo, sizeof(this->issuerName.serialNo));
+	d.getS("iN.e", this->issuerName.email, sizeof(this->issuerName.email));
+	d.getS("iN.ur", this->issuerName.url, sizeof(this->issuerName.url));
+
+	const Vector< uint8_t > &sig = d["si"];
+	if (sig.size() > sizeof(this->signature))
+		return false;
+	Utils::copy(this->signature, sig.data(), (unsigned int)sig.size());
+	this->signatureSize = (unsigned int)sig.size();
+
+	Vector< uint8_t > enc(encode(true));
+	SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
+
+	return true;
+}
+
+bool IdentificationCertificate::sign(const Identity &issuer)
+{
+	Vector< uint8_t > enc(encode(true));
+	SHA384(this->serialNo, enc.data(), (unsigned int)enc.size());
+	return (this->signatureSize = issuer.sign(enc.data(), (unsigned int)enc.size(), this->signature, sizeof(this->signature))) > 0;
+}
+
+bool IdentificationCertificate::verify() const
+{
+	if (this->issuer) {
+		Vector< uint8_t > enc(encode(true));
+		return reinterpret_cast<const Identity *>(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize);
+	}
+	return false;
+}
+
+} // namespace ZeroTier

+ 135 - 0
core/IdentificationCertificate.hpp

@@ -0,0 +1,135 @@
+/*
+ * Copyright (c)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#ifndef ZT_IDENTIFICATIONCERTIFICATE_HPP
+#define ZT_IDENTIFICATIONCERTIFICATE_HPP
+
+#include "Constants.hpp"
+#include "SHA512.hpp"
+#include "C25519.hpp"
+#include "ECC384.hpp"
+#include "SharedPtr.hpp"
+#include "Identity.hpp"
+#include "Locator.hpp"
+#include "Dictionary.hpp"
+#include "Utils.hpp"
+#include "Containers.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Certificate identifying the real world owner of an identity or network.
+ *
+ * This is a wrapper around the straight C ZT_IdentificationCertificate and
+ * handles allocating memory for objects and disposing of it on GC. If filling
+ * out a ZT_IdentificationCertificate structure, identities and other objects
+ * should be attached via the addXXX() methods rather than by directly setting
+ * the pointers in the C structure.
+ *
+ * If identities and similar objects are NOT added via the addXXX() methods,
+ * this will not take care of de-allocating them when destroyed.
+ *
+ * The serialNo field is filled in automatically by sign() and decode(), so
+ * it can be left undefined when building certificates.
+ */
+class IdentificationCertificate : public ZT_IdentificationCertificate
+{
+public:
+	ZT_INLINE IdentificationCertificate() noexcept
+	{ Utils::zero< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this); }
+
+	ZT_INLINE IdentificationCertificate(const ZT_IdentificationCertificate &apiCert)
+	{ Utils::copy< sizeof(ZT_IdentificationCertificate) >((ZT_IdentificationCertificate *)this, &apiCert); }
+
+	ZT_INLINE IdentificationCertificate(const IdentificationCertificate &cert)
+	{ *this = cert; }
+
+	IdentificationCertificate &operator=(const ZT_IdentificationCertificate &apiCert);
+
+	IdentificationCertificate &operator=(const IdentificationCertificate &cert);
+
+	/**
+	 * Add a subject node/identity without a locator
+	 *
+	 * @param id Identity
+	 * @return Pointer to C struct
+	 */
+	ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id);
+
+	/**
+	 * Add a subject node/identity with a locator
+	 *
+	 * @param id Identity
+	 * @param loc Locator signed by identity (signature is NOT checked here)
+	 * @return Pointer to C struct
+	 */
+	ZT_IdentificationCertificate_Node *addSubjectNode(const Identity &id, const Locator &loc);
+
+	/**
+	 * Add a subject network
+	 *
+	 * @param id Network ID
+	 * @param controller Network controller's full fingerprint
+	 * @return Pointer to C struct
+	 */
+	ZT_IdentificationCertificate_Network *addSubjectNetwork(const uint64_t id, const ZT_Fingerprint &controller);
+
+	/**
+	 * Marshal this certificate in binary form
+	 *
+	 * The internal encoding used here is Dictionary to permit easy
+	 * extensibility.
+	 *
+	 * @param omitSignature If true omit the signature field (for signing and verification, default is false)
+	 * @return Marshaled certificate
+	 */
+	Vector< uint8_t > encode(bool omitSignature = false) const;
+
+	/**
+	 * Decode this certificate from marshaled bytes.
+	 *
+	 * @param data Marshalled certificate
+	 * @return True if input is valid and was unmarshalled (signature is NOT checked)
+	 */
+	bool decode(const Vector< uint8_t > &data);
+
+	/**
+	 * Sign this certificate (and also fill in serialNo).
+	 *
+	 * @param issuer Issuer identity (must have secret key)
+	 * @return True on success
+	 */
+	bool sign(const Identity &issuer);
+
+	/**
+	 * Verify certificate signature against the issuer contained therein
+	 *
+	 * @return True if certificate is signed and signature is valid
+	 */
+	bool verify() const;
+
+private:
+	// These hold any identity or locator objects that are owned by and should
+	// be deleted with this certificate. Lists are used so the pointers never
+	// change.
+	List< Identity > m_identities;
+	List< Locator > m_locators;
+
+	// These are stored in a vector because the memory needs to be contiguous.
+	Vector< ZT_IdentificationCertificate_Node > m_nodes;
+	Vector< ZT_IdentificationCertificate_Network > m_networks;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 51 - 24
core/Identity.hpp

@@ -64,7 +64,7 @@ public:
 
 	ZT_INLINE Identity() noexcept
 	{
-		Utils::memoryLock(this,sizeof(Identity));
+		Utils::memoryLock(this, sizeof(Identity));
 		memoryZero(this);
 	}
 
@@ -78,25 +78,27 @@ public:
 	 */
 	explicit ZT_INLINE Identity(const char *str)
 	{
-		Utils::memoryLock(this,sizeof(Identity));
+		Utils::memoryLock(this, sizeof(Identity));
 		fromString(str);
 	}
 
 	ZT_INLINE ~Identity()
 	{
-		Utils::memoryUnlock(this,sizeof(Identity));
+		Utils::memoryUnlock(this, sizeof(Identity));
 		Utils::burn(reinterpret_cast<void *>(&this->m_priv), sizeof(this->m_priv));
 	}
 
 	/**
 	 * Set identity to NIL value (all zero)
 	 */
-	ZT_INLINE void zero() noexcept { memoryZero(this); }
+	ZT_INLINE void zero() noexcept
+	{ memoryZero(this); }
 
 	/**
 	 * @return Identity type (undefined if identity is null or invalid)
 	 */
-	ZT_INLINE Type type() const noexcept { return m_type; }
+	ZT_INLINE Type type() const noexcept
+	{ return m_type; }
 
 	/**
 	 * Generate a new identity (address, key pair)
@@ -122,17 +124,20 @@ public:
 	/**
 	 * @return True if this identity contains a private key
 	 */
-	ZT_INLINE bool hasPrivate() const noexcept { return m_hasPrivate; }
+	ZT_INLINE bool hasPrivate() const noexcept
+	{ return m_hasPrivate; }
 
 	/**
 	 * @return This identity's address
 	 */
-	ZT_INLINE Address address() const noexcept { return Address(m_fp.address); }
+	ZT_INLINE Address address() const noexcept
+	{ return Address(m_fp.address); }
 
 	/**
 	 * @return Full fingerprint of this identity (address plus SHA384 of keys)
 	 */
-	ZT_INLINE const Fingerprint &fingerprint() const noexcept { return m_fp; }
+	ZT_INLINE const Fingerprint &fingerprint() const noexcept
+	{ return m_fp; }
 
 	/**
 	 * Compute a hash of this identity's public and private keys.
@@ -155,7 +160,7 @@ public:
 	 * @param siglen Length of buffer
 	 * @return Number of bytes actually written to sig or 0 on error
 	 */
-	unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const;
+	unsigned int sign(const void *data, unsigned int len, void *sig, unsigned int siglen) const;
 
 	/**
 	 * Verify a message signature against this identity
@@ -166,7 +171,7 @@ public:
 	 * @param siglen Length of signature in bytes
 	 * @return True if signature validates and data integrity checks
 	 */
-	bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const;
+	bool verify(const void *data, unsigned int len, const void *sig, unsigned int siglen) const;
 
 	/**
 	 * Shortcut method to perform key agreement with another identity
@@ -177,7 +182,7 @@ public:
 	 * @param key Result parameter to fill with key bytes
 	 * @return Was agreement successful?
 	 */
-	bool agree(const Identity &id,uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const;
+	bool agree(const Identity &id, uint8_t key[ZT_SYMMETRIC_KEY_SIZE]) const;
 
 	/**
 	 * Serialize to a more human-friendly string
@@ -186,8 +191,14 @@ public:
 	 * @param buf Buffer to store string
 	 * @return ASCII string representation of identity (pointer to buf)
 	 */
-	char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
-	ZT_INLINE String toString(const bool includePrivate = false) const { char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]; toString(includePrivate,buf); return String(buf); }
+	char *toString(bool includePrivate, char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
+
+	ZT_INLINE String toString(const bool includePrivate = false) const
+	{
+		char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH];
+		toString(includePrivate, buf);
+		return String(buf);
+	}
 
 	/**
 	 * Deserialize a human-friendly string
@@ -203,20 +214,36 @@ public:
 	/**
 	 * @return True if this identity contains something
 	 */
-	explicit ZT_INLINE operator bool() const noexcept { return (m_fp); }
+	explicit ZT_INLINE operator bool() const noexcept
+	{ return (m_fp); }
+
+	ZT_INLINE unsigned long hashCode() const noexcept
+	{ return m_fp.hashCode(); }
+
+	ZT_INLINE bool operator==(const Identity &id) const noexcept
+	{ return (m_fp == id.m_fp); }
+
+	ZT_INLINE bool operator!=(const Identity &id) const noexcept
+	{ return !(*this == id); }
+
+	ZT_INLINE bool operator<(const Identity &id) const noexcept
+	{ return (m_fp < id.m_fp); }
+
+	ZT_INLINE bool operator>(const Identity &id) const noexcept
+	{ return (id < *this); }
+
+	ZT_INLINE bool operator<=(const Identity &id) const noexcept
+	{ return !(id < *this); }
+
+	ZT_INLINE bool operator>=(const Identity &id) const noexcept
+	{ return !(*this < id); }
 
-	ZT_INLINE unsigned long hashCode() const noexcept { return m_fp.hashCode(); }
+	static constexpr int marshalSizeMax() noexcept
+	{ return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
 
-	ZT_INLINE bool operator==(const Identity &id) const noexcept { return (m_fp == id.m_fp); }
-	ZT_INLINE bool operator!=(const Identity &id) const noexcept { return !(*this == id); }
-	ZT_INLINE bool operator<(const Identity &id) const noexcept { return (m_fp < id.m_fp); }
-	ZT_INLINE bool operator>(const Identity &id) const noexcept { return (id < *this); }
-	ZT_INLINE bool operator<=(const Identity &id) const noexcept { return !(id < *this); }
-	ZT_INLINE bool operator>=(const Identity &id) const noexcept { return !(*this < id); }
+	int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX], bool includePrivate = false) const noexcept;
 
-	static constexpr int marshalSizeMax() noexcept { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
-	int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],bool includePrivate = false) const noexcept;
-	int unmarshal(const uint8_t *data,int len) noexcept;
+	int unmarshal(const uint8_t *data, int len) noexcept;
 
 private:
 	void m_computeHash();

+ 154 - 154
core/Mutex.hpp

@@ -1,154 +1,154 @@
-/*
- * Copyright (c)2013-2020 ZeroTier, Inc.
- *
- * Use of this software is governed by the Business Source License included
- * in the LICENSE.TXT file in the project's root directory.
- *
- * Change Date: 2024-01-01
- *
- * On the date above, in accordance with the Business Source License, use
- * of this software will be governed by version 2.0 of the Apache License.
- */
-/****/
-
-#ifndef ZT_MUTEX_HPP
-#define ZT_MUTEX_HPP
-
-#include "Constants.hpp"
-
-#include <cstdint>
-#include <cstdlib>
-
-#ifndef __WINDOWS__
-#include <pthread.h>
-#endif
-
-namespace ZeroTier {
-
-class Mutex
-{
-public:
-	ZT_INLINE Mutex() noexcept { pthread_mutex_init(&_mh,nullptr); }
-	ZT_INLINE ~Mutex() noexcept { pthread_mutex_destroy(&_mh); }
-
-	ZT_INLINE void lock() const noexcept { pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh)); }
-	ZT_INLINE void unlock() const noexcept { pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh)); }
-
-	class Lock
-	{
-	public:
-		explicit ZT_INLINE Lock(Mutex &m) noexcept : _m(&m) { m.lock(); }
-		explicit ZT_INLINE Lock(const Mutex &m) noexcept : _m(const_cast<Mutex *>(&m)) { _m->lock(); }
-		ZT_INLINE ~Lock() { _m->unlock(); }
-	private:
-		Mutex *const _m;
-	};
-
-private:
-	ZT_INLINE Mutex(const Mutex &) noexcept {}
-	ZT_INLINE const Mutex &operator=(const Mutex &) noexcept { return *this; }
-
-	pthread_mutex_t _mh;
-};
-
-class RWMutex
-{
-public:
-	ZT_INLINE RWMutex() noexcept { pthread_rwlock_init(&_mh,nullptr); }
-	ZT_INLINE ~RWMutex() noexcept { pthread_rwlock_destroy(&_mh); }
-
-	ZT_INLINE void lock() const noexcept { pthread_rwlock_wrlock(&((const_cast <RWMutex *> (this))->_mh)); }
-	ZT_INLINE void rlock() const noexcept { pthread_rwlock_rdlock(&((const_cast <RWMutex *> (this))->_mh)); }
-	ZT_INLINE void unlock() const noexcept { pthread_rwlock_unlock(&((const_cast <RWMutex *> (this))->_mh)); }
-	ZT_INLINE void runlock() const noexcept { pthread_rwlock_unlock(&((const_cast <RWMutex *> (this))->_mh)); }
-
-	/**
-	 * RAAI locker that acquires only the read lock (shared read)
-	 */
-	class RLock
-	{
-	public:
-		explicit ZT_INLINE RLock(RWMutex &m) noexcept : _m(&m) { m.rlock(); }
-		explicit ZT_INLINE RLock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)) { _m->rlock(); }
-		ZT_INLINE ~RLock() { _m->runlock(); }
-	private:
-		RWMutex *const _m;
-	};
-
-	/**
-	 * RAAI locker that acquires the write lock (exclusive write, no readers)
-	 */
-	class Lock
-	{
-	public:
-		explicit ZT_INLINE Lock(RWMutex &m) noexcept : _m(&m) { m.lock(); }
-		explicit ZT_INLINE Lock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)) { _m->lock(); }
-		ZT_INLINE ~Lock() { _m->unlock(); }
-	private:
-		RWMutex *const _m;
-	};
-
-	/**
-	 * RAAI locker that acquires the read lock first and can switch modes
-	 *
-	 * Use writing() to acquire the write lock if not already acquired. Use reading() to
-	 * let go of the write lock and go back to only holding the read lock.
-	 */
-	class RMaybeWLock
-	{
-	public:
-		explicit ZT_INLINE RMaybeWLock(RWMutex &m) noexcept : _m(&m),_w(false) { m.rlock(); }
-		explicit ZT_INLINE RMaybeWLock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)),_w(false) { _m->rlock(); }
-		ZT_INLINE void writing() noexcept { if (!_w) { _w = true; _m->runlock(); _m->lock(); } }
-		ZT_INLINE void reading() noexcept { if (_w) { _w = false; _m->unlock(); _m->rlock(); } }
-		ZT_INLINE ~RMaybeWLock() { if (_w) _m->unlock(); else _m->runlock(); }
-	private:
-		RWMutex *const _m;
-		bool _w;
-	};
-
-private:
-	ZT_INLINE RWMutex(const RWMutex &) noexcept {}
-	ZT_INLINE const RWMutex &operator=(const RWMutex &) noexcept { return *this; }
-
-	pthread_rwlock_t _mh;
-};
-
-} // namespace ZeroTier
-
-#if 0
-#include <Windows.h>
-
-namespace ZeroTier {
-
-class Mutex
-{
-public:
-	ZT_INLINE Mutex() { InitializeCriticalSection(&_cs); }
-	ZT_INLINE ~Mutex() { DeleteCriticalSection(&_cs); }
-	ZT_INLINE void lock() { EnterCriticalSection(&_cs); }
-	ZT_INLINE void unlock() { LeaveCriticalSection(&_cs); }
-	ZT_INLINE void lock() const { (const_cast <Mutex *> (this))->lock(); }
-	ZT_INLINE void unlock() const { (const_cast <Mutex *> (this))->unlock(); }
-
-	class Lock
-	{
-	public:
-		ZT_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); }
-		ZT_INLINE Lock(const Mutex &m) : _m(const_cast<Mutex *>(&m)) { _m->lock(); }
-		ZT_INLINE ~Lock() { _m->unlock(); }
-	private:
-		Mutex *const _m;
-	};
-
-private:
-	ZT_INLINE Mutex(const Mutex &) {}
-	ZT_INLINE const Mutex &operator=(const Mutex &) { return *this; }
-
-	CRITICAL_SECTION _cs;
-};
-
-} // namespace ZeroTier
-#endif
-
-#endif
+/*
+ * Copyright (c)2013-2020 ZeroTier, Inc.
+ *
+ * Use of this software is governed by the Business Source License included
+ * in the LICENSE.TXT file in the project's root directory.
+ *
+ * Change Date: 2024-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by version 2.0 of the Apache License.
+ */
+/****/
+
+#ifndef ZT_MUTEX_HPP
+#define ZT_MUTEX_HPP
+
+#include "Constants.hpp"
+
+#include <cstdint>
+#include <cstdlib>
+
+#ifndef __WINDOWS__
+#include <pthread.h>
+#endif
+
+namespace ZeroTier {
+
+class Mutex
+{
+public:
+	ZT_INLINE Mutex() noexcept { pthread_mutex_init(&_mh,nullptr); }
+	ZT_INLINE ~Mutex() noexcept { pthread_mutex_destroy(&_mh); }
+
+	ZT_INLINE void lock() const noexcept { pthread_mutex_lock(&((const_cast <Mutex *> (this))->_mh)); }
+	ZT_INLINE void unlock() const noexcept { pthread_mutex_unlock(&((const_cast <Mutex *> (this))->_mh)); }
+
+	class Lock
+	{
+	public:
+		explicit ZT_INLINE Lock(Mutex &m) noexcept : _m(&m) { m.lock(); }
+		explicit ZT_INLINE Lock(const Mutex &m) noexcept : _m(const_cast<Mutex *>(&m)) { _m->lock(); }
+		ZT_INLINE ~Lock() { _m->unlock(); }
+	private:
+		Mutex *const _m;
+	};
+
+private:
+	ZT_INLINE Mutex(const Mutex &) noexcept {}
+	ZT_INLINE const Mutex &operator=(const Mutex &) noexcept { return *this; }
+
+	pthread_mutex_t _mh;
+};
+
+class RWMutex
+{
+public:
+	ZT_INLINE RWMutex() noexcept { pthread_rwlock_init(&_mh,nullptr); }
+	ZT_INLINE ~RWMutex() noexcept { pthread_rwlock_destroy(&_mh); }
+
+	ZT_INLINE void lock() const noexcept { pthread_rwlock_wrlock(&((const_cast <RWMutex *> (this))->_mh)); }
+	ZT_INLINE void rlock() const noexcept { pthread_rwlock_rdlock(&((const_cast <RWMutex *> (this))->_mh)); }
+	ZT_INLINE void unlock() const noexcept { pthread_rwlock_unlock(&((const_cast <RWMutex *> (this))->_mh)); }
+	ZT_INLINE void runlock() const noexcept { pthread_rwlock_unlock(&((const_cast <RWMutex *> (this))->_mh)); }
+
+	/**
+	 * RAAI locker that acquires only the read lock (shared read)
+	 */
+	class RLock
+	{
+	public:
+		explicit ZT_INLINE RLock(RWMutex &m) noexcept : _m(&m) { m.rlock(); }
+		explicit ZT_INLINE RLock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)) { _m->rlock(); }
+		ZT_INLINE ~RLock() { _m->runlock(); }
+	private:
+		RWMutex *const _m;
+	};
+
+	/**
+	 * RAAI locker that acquires the write lock (exclusive write, no readers)
+	 */
+	class Lock
+	{
+	public:
+		explicit ZT_INLINE Lock(RWMutex &m) noexcept : _m(&m) { m.lock(); }
+		explicit ZT_INLINE Lock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)) { _m->lock(); }
+		ZT_INLINE ~Lock() { _m->unlock(); }
+	private:
+		RWMutex *const _m;
+	};
+
+	/**
+	 * RAAI locker that acquires the read lock first and can switch modes
+	 *
+	 * Use writing() to acquire the write lock if not already acquired. Use reading() to
+	 * let go of the write lock and go back to only holding the read lock.
+	 */
+	class RMaybeWLock
+	{
+	public:
+		explicit ZT_INLINE RMaybeWLock(RWMutex &m) noexcept : _m(&m),_w(false) { m.rlock(); }
+		explicit ZT_INLINE RMaybeWLock(const RWMutex &m) noexcept : _m(const_cast<RWMutex *>(&m)),_w(false) { _m->rlock(); }
+		ZT_INLINE void writing() noexcept { if (!_w) { _w = true; _m->runlock(); _m->lock(); } }
+		ZT_INLINE void reading() noexcept { if (_w) { _w = false; _m->unlock(); _m->rlock(); } }
+		ZT_INLINE ~RMaybeWLock() { if (_w) _m->unlock(); else _m->runlock(); }
+	private:
+		RWMutex *const _m;
+		bool _w;
+	};
+
+private:
+	ZT_INLINE RWMutex(const RWMutex &) noexcept {}
+	ZT_INLINE const RWMutex &operator=(const RWMutex &) noexcept { return *this; }
+
+	pthread_rwlock_t _mh;
+};
+
+} // namespace ZeroTier
+
+#if 0
+#include <Windows.h>
+
+namespace ZeroTier {
+
+class Mutex
+{
+public:
+	ZT_INLINE Mutex() { InitializeCriticalSection(&_cs); }
+	ZT_INLINE ~Mutex() { DeleteCriticalSection(&_cs); }
+	ZT_INLINE void lock() { EnterCriticalSection(&_cs); }
+	ZT_INLINE void unlock() { LeaveCriticalSection(&_cs); }
+	ZT_INLINE void lock() const { (const_cast <Mutex *> (this))->lock(); }
+	ZT_INLINE void unlock() const { (const_cast <Mutex *> (this))->unlock(); }
+
+	class Lock
+	{
+	public:
+		ZT_INLINE Lock(Mutex &m) : _m(&m) { m.lock(); }
+		ZT_INLINE Lock(const Mutex &m) : _m(const_cast<Mutex *>(&m)) { _m->lock(); }
+		ZT_INLINE ~Lock() { _m->unlock(); }
+	private:
+		Mutex *const _m;
+	};
+
+private:
+	ZT_INLINE Mutex(const Mutex &) {}
+	ZT_INLINE const Mutex &operator=(const Mutex &) { return *this; }
+
+	CRITICAL_SECTION _cs;
+};
+
+} // namespace ZeroTier
+#endif
+
+#endif

+ 1 - 3
core/Node.cpp

@@ -119,9 +119,7 @@ Node::Node(
 			stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int)strlen(RR->publicIdentityStr));
 	}
 
-	// 2X hash our identity private key(s) to obtain a symmetric key for encrypting
-	// locally cached data at rest (as a defense in depth measure). This is not used
-	// for any network level encryption or authentication.
+	// Create a secret key for encrypting local data at rest.
 	uint8_t tmph[ZT_SHA384_DIGEST_SIZE];
 	RR->identity.hashWithPrivate(tmph);
 	SHA384(tmph, tmph, ZT_SHA384_DIGEST_SIZE);

+ 1 - 1
core/Protocol.hpp

@@ -194,7 +194,7 @@
 #define ZT_PROTO_PACKET_FRAGMENT_PAYLOAD_START_AT ZT_PROTO_MIN_FRAGMENT_LENGTH
 
 /**
- * Header flag indicating that a packet is fragmented and more fragments should be expected
+ * Outer flag indicating that a packet is fragmented and this is just the head.
  */
 #define ZT_PROTO_FLAG_FRAGMENTED 0x40U
 

+ 2 - 0
core/Salsa20.cpp

@@ -145,7 +145,9 @@ static ZT_INLINE void p_salsaCrypt(p_SalsaState *const state, const uint8_t *m,
 
 	for (;;) {
 		if (likely(bytes >= 64)) {
+#ifdef ZT_SALSA20_SSE
 			_mm_prefetch(m + 128, _MM_HINT_T0);
+#endif
 		} else {
 			for (unsigned int i = 0;i < bytes;++i)
 				tmp[i] = m[i];

+ 7 - 8
core/Topology.cpp

@@ -29,7 +29,6 @@ Topology::Topology(const RuntimeEnvironment *renv, void *tPtr) :
 			Identity id;
 			int l = id.unmarshal(dptr, drem);
 			if ((l > 0) && (id)) {
-				m_roots.insert(id);
 				ZT_SPEW("restored root %s", id.address().toString().c_str());
 				if ((drem -= l) <= 0)
 					break;
@@ -189,25 +188,25 @@ void Topology::m_updateRootPeers(void *tPtr)
 {
 	// assumes m_peers_l is locked for write
 	Vector< SharedPtr< Peer > > rp;
-	for (Set< Identity >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) {
-		Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->address()));
+	for (Map< Identity, Set< SubscriptionKeyHash > >::iterator r(m_roots.begin()); r != m_roots.end(); ++r) {
+		Map< Address, SharedPtr< Peer > >::iterator pp(m_peers.find(r->first.address()));
 		SharedPtr< Peer > p;
 		if (pp != m_peers.end())
 			p = pp->second;
 
 		if (!p)
-			m_loadCached(tPtr, r->address(), p);
+			m_loadCached(tPtr, r->first.address(), p);
 
-		if ((!p) || (p->identity() != *r)) {
+		if ((!p) || (p->identity() != r->first)) {
 			p.set(new Peer(RR));
-			p->init(*r);
-			m_peers[r->address()] = p;
+			p->init(r->first);
+			m_peers[r->first.address()] = p;
 		}
 
 		rp.push_back(p);
 	}
+	std::sort(rp.begin(), rp.end(), p_RootSortComparisonOperator());
 	m_rootPeers.swap(rp);
-	std::sort(m_rootPeers.begin(), m_rootPeers.end(), p_RootSortComparisonOperator());
 }
 
 } // namespace ZeroTier

+ 7 - 1
core/Topology.hpp

@@ -25,6 +25,7 @@
 #include "ScopedPtr.hpp"
 #include "Fingerprint.hpp"
 #include "Containers.hpp"
+#include "Blob.hpp"
 
 namespace ZeroTier {
 
@@ -36,6 +37,11 @@ class RuntimeEnvironment;
 class Topology
 {
 public:
+	/**
+	 * Hash of public keys for signing a root set definition
+	 */
+	typedef Blob<ZT_SHA384_DIGEST_SIZE> RootSetId;
+
 	Topology(const RuntimeEnvironment *renv, void *tPtr);
 
 	/**
@@ -237,7 +243,7 @@ private:
 	RWMutex m_peers_l; // locks m_peers, m_roots, and m_rootPeers
 	Map< uint64_t, SharedPtr< Path > > m_paths;
 	Map< Address, SharedPtr< Peer > > m_peers;
-	Set< Identity > m_roots;
+	Map< Identity, Set< SubscriptionKeyHash > > m_roots;
 	Vector< SharedPtr< Peer > > m_rootPeers;
 };
 

+ 22 - 16
core/Utils.cpp

@@ -132,14 +132,19 @@ char *decimal(unsigned long n, char s[24]) noexcept
 
 char *hex(uint64_t i, char buf[17]) noexcept
 {
-	if (i) {
-		char *p = buf + 16;
-		*p = 0;
-		while (i) {
-			*(--p) = HEXCHARS[i & 0xfU];
-			i >>= 4;
+	if (i != 0) {
+		char *p = nullptr;
+		for (int b = 60; b >= 0; b -= 4) {
+			const unsigned int nyb = (unsigned int)(i >> (unsigned int)b) & 0xfU;
+			if (p) {
+				*(p++) = HEXCHARS[nyb];
+			} else if (nyb != 0) {
+				p = buf;
+				*(p++) = HEXCHARS[nyb];
+			}
 		}
-		return p;
+		*p = 0;
+		return buf;
 	} else {
 		buf[0] = '0';
 		buf[1] = 0;
@@ -267,20 +272,18 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept
 			close(devURandomFd);
 #endif
 
-			// Mix in additional entropy from time, the address of 'buf', CPU RDRAND if present, etc.
-			randomState[0] += (uint64_t)time(nullptr);
-			randomState[1] += (uint64_t)((uintptr_t)buf);
 #ifdef __UNIX_LIKE__
-			randomState[2] += (uint64_t)getpid();
-			randomState[3] += (uint64_t)getppid();
+			randomState[0] += (uint64_t)getpid();
+			randomState[1] += (uint64_t)getppid();
 #endif
 #ifdef ZT_ARCH_X64
 			if (CPUID.rdrand) {
+				// RDRAND is very slow on some chips, so only sample it a little bit for extra entropy.
 				uint64_t tmp = 0;
-				for (int k = 0; k < ZT_GETSECURERANDOM_STATE_SIZE; ++k) {
-					_rdrand64_step((unsigned long long *)&tmp);
-					randomState[k] ^= tmp;
-				}
+				_rdrand64_step((unsigned long long *)&tmp);
+				randomState[2] ^= tmp;
+				_rdrand64_step((unsigned long long *)&tmp);
+				randomState[3] ^= tmp;
 			}
 #endif
 		}
@@ -289,6 +292,9 @@ void getSecureRandom(void *const buf, unsigned int bytes) noexcept
 		// replacing the first 64 bytes with this hash, and then re-initializing
 		// AES with the first 32 bytes.
 		randomByteCounter = 0;
+		randomState[4] += (uint64_t)((uintptr_t)buf);
+		randomState[5] += (uint64_t)bytes;
+		randomState[6] += (uint64_t)time(nullptr);
 		SHA512(randomState, randomState, sizeof(randomState));
 		randomGen.init(randomState);
 	}

+ 0 - 3
core/Utils.hpp

@@ -144,9 +144,6 @@ char *decimal(unsigned long n, char s[24]) noexcept;
 /**
  * Convert an unsigned integer into hex
  *
- * The returned pointer won't point to the start of 'buf', since
- * hex writing is done in reverse order.
- * 
  * @param i Any unsigned integer
  * @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur.
  * @return Pointer to s containing hex string with trailing zero byte

+ 175 - 16
core/zerotier.h

@@ -276,6 +276,181 @@ typedef void ZT_Identity;
  */
 typedef void ZT_Locator;
 
+/**
+ * Full identity fingerprint with address and 384-bit hash of public key(s)
+ */
+typedef struct
+{
+	/**
+	 * Short address (only least significant 40 bits are used)
+	 */
+	uint64_t address;
+
+	/**
+	 * 384-bit hash of identity public key(s)
+	 */
+	uint8_t hash[48];
+} ZT_Fingerprint;
+
+/**
+ * Maximum length of string fields in identification certificates
+ */
+#define ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH 127
+
+/**
+ * Maximum length of a signature
+ */
+#define ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE 256
+
+/**
+ * Information about a real world entity.
+ */
+typedef struct
+{
+	char country[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char organization[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char unit[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char locality[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char province[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char streetAddress[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char postalCode[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char commonName[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char serialNo[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char email[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+	char url[ZT_IDENTIFICATION_CERTIFICATE_MAX_STRING_LENGTH + 1];
+} ZT_IdentificationCertificate_Name;
+
+/**
+ * Identity and optional locator for a node
+ */
+typedef struct
+{
+	/**
+	 * Identity (never NULL)
+	 */
+	const ZT_Identity *identity;
+
+	/**
+	 * Locator or NULL if not specified
+	 */
+	const ZT_Locator *locator;
+} ZT_IdentificationCertificate_Node;
+
+/**
+ * ID and primary controller for a network
+ */
+typedef struct
+{
+	/**
+	 * Network ID
+	 */
+	uint64_t id;
+
+	/**
+	 * Full fingerprint of primary controller
+	 */
+	ZT_Fingerprint controller;
+} ZT_IdentificationCertificate_Network;
+
+/**
+ * Identification certificate subject
+ */
+typedef struct
+{
+	/**
+	 * Identities and optional locators of nodes
+	 */
+	ZT_IdentificationCertificate_Node *nodes;
+
+	/**
+	 * Number of nodes
+	 */
+	unsigned int nodeCount;
+
+	/**
+	 * Networks owned by this entity
+	 */
+	ZT_IdentificationCertificate_Network *networks;
+
+	/**
+	 * Number of networks
+	 */
+	unsigned int networkCount;
+
+	/**
+	 * Information about owner of items.
+	 */
+	ZT_IdentificationCertificate_Name name;
+} ZT_IdentificationCertificate_Subject;
+
+/**
+ * Identification certificate
+ *
+ * This is designed so it could be converted to/from an X509 format
+ * for interoperability with X509 systems. OCSP could be implemented
+ * too, though it would probably require the development of an OCSP
+ * proxy server that queried the issuer via the ZeroTier protocol.
+ */
+typedef struct
+{
+	/**
+	 * Serial number, a SHA384 hash of this certificate.
+	 */
+	uint8_t serialNo[48];
+
+	/**
+	 * Certificate version
+	 */
+	unsigned int version;
+
+	/**
+	 * Maximum path length from this certificate toward further certificates.
+	 *
+	 * Subjects may sign other certificates whose path lengths are less than
+	 * this value. A value of zero indicates that no identification certificates
+	 * may be signed (not a CA).
+	 */
+	unsigned int maxPathLength;
+
+	/**
+	 * Flags (for future use, currently zero).
+	 *
+	 * This could be used to implement key usage flags similar to X509 if
+	 * these are needed.
+	 */
+	uint64_t flags;
+
+	/**
+	 * Valid time range: not before, not after.
+	 */
+	int64_t validity[2];
+
+	/**
+	 * Subject of certificate
+	 */
+	ZT_IdentificationCertificate_Subject subject;
+
+	/**
+	 * Issuer node identity and public key(s).
+	 */
+	const ZT_Identity *issuer;
+
+	/**
+	 * Issuer information
+	 */
+	ZT_IdentificationCertificate_Name issuerName;
+
+	/**
+	 * Signature by issuer (algorithm determined by identity type).
+	 */
+	uint8_t signature[ZT_IDENTIFICATION_CERTIFICATE_MAX_SIGNATURE_SIZE];
+
+	/**
+	 * Size of signature in bytes.
+	 */
+	unsigned int signatureSize;
+} ZT_IdentificationCertificate;
+
 /**
  * Credential type IDs
  */
@@ -308,22 +483,6 @@ enum ZT_EndpointType
 	ZT_ENDPOINT_TYPE_IP_HTTP =       8   // IP/HTTP encapsulation
 };
 
-/**
- * Full identity fingerprint with address and 384-bit hash of public key(s)
- */
-typedef struct
-{
-	/**
-	 * Short address (only least significant 40 bits are used)
-	 */
-	uint64_t address;
-
-	/**
-	 * 384-bit hash of identity public key(s)
-	 */
-	uint8_t hash[48];
-} ZT_Fingerprint;
-
 /**
  * Flag indicating that VL1 tracing should be generated
  */

+ 484 - 484
installsupport/windows/ZeroTier One.aip

@@ -1,484 +1,484 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
-  <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
-    <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;AppXProductDetailsComponent;AppXDependenciesComponent;AppXAppDetailsComponent;AppXVisualAssetsComponent;AppXCapabilitiesComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
-    <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
-    <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
-    <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
-    <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
-    <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
-    <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
-    <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
-    <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
-    <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
-    <ROW Property="ALLUSERS" Value="1"/>
-    <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
-    <ROW Property="ARPCONTACT" Value="[email protected]"/>
-    <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
-    <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
-    <ROW Property="ARPNOREPAIR" Value="1" MultiBuildValue="ExeBuild:1"/>
-    <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
-    <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
-    <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
-    <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
-    <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
-    <ROW Property="CTRLS" Value="2"/>
-    <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
-    <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
-    <ROW Property="ProductCode" Value="1033:{80CEE5C9-4DF0-43F5-B232-484D6455978E} " Type="16"/>
-    <ROW Property="ProductLanguage" Value="1033"/>
-    <ROW Property="ProductName" Value="ZeroTier One"/>
-    <ROW Property="ProductVersion" Value="1.4.6" Type="32"/>
-    <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
-    <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
-    <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
-    <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
-    <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
-    <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
-    <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
-    <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
-    <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
-    <ROW Directory="FontsFolder" Directory_Parent="TARGETDIR" DefaultDir="FONTSF~1|FontsFolder" IsPseudoRoot="1"/>
-    <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
-    <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
-    <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
-    <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
-    <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
-    <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
-    <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
-    <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
-    <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
-    <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
-    <ROW Component="AI_CustomARPName" ComponentId="{717E3B00-472C-4E07-BC45-AF83F5442898}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
-    <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
-    <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
-    <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/>
-    <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
-    <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
-    <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
-    <ROW Component="copyutil.exe" ComponentId="{9B9E89FB-81CB-4500-978B-11E2FA81B5B4}" Directory_="APPDIR" Attributes="0" KeyPath="copyutil.exe"/>
-    <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
-    <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
-    <ROW Component="segoeui.ttf" ComponentId="{9F415308-A118-419F-AD8A-678DEA856B78}" Directory_="FontsFolder" Attributes="144" Type="0"/>
-    <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
-    <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
-    <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
-    <ROW Component="zttap300.inf" ComponentId="{D4839F5E-FB94-41CB-9B1B-177A97ADC904}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
-    <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier segoeui.ttf zerotierone_x64.exe zerotierone_x86.exe zttap300.cat_1"/>
-    <ROW Feature="zttap300" Feature_Parent="ZeroTierOne" Title="zttap300" Description="ZeroTier Virtual Network Port Driver" Display="1" Level="1" Directory_="APPDIR" Attributes="16" Components="zttap300.inf"/>
-    <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
-    <ROW File="Hardcodet.Wpf.TaskbarNotification.dll" Component_="Hardcodet.Wpf.TaskbarNotification.dll" FileName="HARDCO~1.DLL|Hardcodet.Wpf.TaskbarNotification.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Hardcodet.Wpf.TaskbarNotification.dll" SelfReg="false" NextFile="segoeui.ttf" DigSign="true"/>
-    <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" NextFile="copyutil.exe" DigSign="true"/>
-    <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
-    <ROW File="copyutil.exe" Component_="copyutil.exe" FileName="copyutil.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\copyutil\bin\Release\copyutil.exe" SelfReg="false" NextFile="Hardcodet.Wpf.TaskbarNotification.dll" DigSign="true"/>
-    <ROW File="segoeui.ttf" Component_="segoeui.ttf" FileName="segoeui.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeui.ttf" SelfReg="false" NextFile="segoeuib.ttf"/>
-    <ROW File="segoeuib.ttf" Component_="segoeui.ttf" FileName="segoeuib.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuib.ttf" SelfReg="false" NextFile="segoeuii.ttf"/>
-    <ROW File="segoeuii.ttf" Component_="segoeui.ttf" FileName="segoeuii.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuii.ttf" SelfReg="false" NextFile="segoeuiz.ttf"/>
-    <ROW File="segoeuiz.ttf" Component_="segoeui.ttf" FileName="segoeuiz.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuiz.ttf" SelfReg="false"/>
-    <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
-    <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
-    <ROW File="zttap300.cat_2" Component_="zttap300.inf" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
-    <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
-    <ROW File="zttap300.inf" Component_="zttap300.inf" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
-    <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
-    <ROW File="zttap300.sys_2" Component_="zttap300.inf" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
-    <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.AiPersistentDataComponent">
-    <ROW PersistentRow="segoeui.ttf" Type="0" Condition="1"/>
-    <ROW PersistentRow="segoeuib.ttf" Type="0" Condition="1"/>
-    <ROW PersistentRow="segoeuii.ttf" Type="0" Condition="1"/>
-    <ROW PersistentRow="segoeuiz.ttf" Type="0" Condition="1"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
-    <ROW BootstrOptKey="GlobalOptions" GeneralOptions="qh" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.BootstrapperUISequenceComponent">
-    <ROW Action="AI_DetectSoftware" Sequence="101"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
-    <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
-    <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" ExtUI="true" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]_0"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
-    <ATTRIBUTE name="Enable" value="false"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
-    <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="110" InstallCondition="(NOT Installed) AND VersionNT64" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND VersionNT64"/>
-    <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="110" InstallCondition="(NOT Installed) AND (NOT VersionNT64)" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND (NOT VersionNT64)"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
-    <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
-    <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
-    <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
-    <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
-    <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
-    <ROW FirewallException="ZeroTierOneControl" DisplayName="ZeroTier One TCP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
-    <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
-    <ROW FirewallException="ZeroTierOnex64Binary" DisplayName="ZeroTier One x64 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="0" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
-    <ROW FirewallException="ZeroTierOnex86Binary" DisplayName="ZeroTier One x86 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="0" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
-    <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
-    <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
-    <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
-    <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
-    <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
-    <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
-    <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
-    <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
-    <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
-    <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
-    <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
-    <ROW Action="AI_AiBackupImmediate" Description="Preparing backup operation" DescriptionLocId="ActionText.Description.AI_AiBackupImmediate" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupImmediate"/>
-    <ROW Action="AI_AiBackupRollback" Description="Rollback backup" DescriptionLocId="ActionText.Description.AI_AiBackupRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupRollback"/>
-    <ROW Action="AI_AiRestoreDeferred" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
-    <ROW Action="AI_AiRestoreDeferredImpersonate" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
-    <ROW Action="AI_AiRestoreRollback" Description="Rollback restore" DescriptionLocId="ActionText.Description.AI_AiRestoreRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreRollback"/>
-    <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
-    <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
-    <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
-    <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
-    <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwConfig" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwConfig"/>
-    <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
-    <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwRemove" Template="Configuring Windows Firewall rule:  &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwRemove"/>
-    <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" Template="Rolling back Windows Firewall configurations." TemplateLocId="ActionText.Template.AI_FwRollback"/>
-    <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
-    <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" Template="Commit text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
-    <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" Template="Updating text file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
-    <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
-    <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" Template="Rolling back text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
-    <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" Template="Committing XML file configurations." TemplateLocId="ActionText.Template.AI_XmlCommit"/>
-    <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlConfig" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
-    <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
-    <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlRemove" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
-    <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" Template="Rolling back XML file configurations." TemplateLocId="ActionText.Template.AI_XmlRollback"/>
-    <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
-    <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/>
-    <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
-    <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
-    <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
-    <ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
-    <ROW Name="ResourceCleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ResourceCleaner.dll"/>
-    <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
-    <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
-    <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
-    <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
-    <ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
-    <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
-    <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiConditionComponent">
-    <ROW Feature_="zttap300" Level="0" Condition="((VersionNT &lt; 500) OR (NOT VersionNT))"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
-    <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
-    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
-    <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
-    <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
-    <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
-    <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
-    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
-    <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
-    <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
-    <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
-    <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
-    <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
-    <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
-    <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
-    <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
-    <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
-    <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
-    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
-    <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
-    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
-    <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
-    <ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="102"/>
-    <ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="101"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
-    <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
-    <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
-    <ROW Action="AI_AiBackupCleanup" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupCleanup" WithoutSeq="true"/>
-    <ROW Action="AI_AiBackupImmediate" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupImmediate"/>
-    <ROW Action="AI_AiBackupRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiBackupRollback"/>
-    <ROW Action="AI_AiRestoreDeferred" Type="11265" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferred"/>
-    <ROW Action="AI_AiRestoreDeferredImpersonate" Type="9217" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferredImpersonate"/>
-    <ROW Action="AI_AiRestoreRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiRestoreRollback" WithoutSeq="true"/>
-    <ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
-    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
-    <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
-    <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
-    <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
-    <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
-    <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
-    <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/>
-    <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/>
-    <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
-    <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
-    <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
-    <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
-    <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
-    <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
-    <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
-    <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
-    <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
-    <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/>
-    <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
-    <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/>
-    <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
-    <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
-    <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
-    <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
-    <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
-    <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
-    <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
-    <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
-    <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
-    <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
-    <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
-    <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
-    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
-    <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
-    <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
-    <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
-    <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
-    <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
-    <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
-    <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
-    <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
-    <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
-    <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
-    <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
-    <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
-    <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
-    <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
-    <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
-    <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
-    <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
-    <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
-    <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
-    <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
-    <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
-    <ROW Action="TerminateUI" Type="65" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
-    <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
-    <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiFontsComponent">
-    <ROW File_="segoeui.ttf"/>
-    <ROW File_="segoeuib.ttf"/>
-    <ROW File_="segoeuii.ttf"/>
-    <ROW File_="segoeuiz.ttf"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
-    <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
-    <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
-    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
-    <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1503"/>
-    <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
-    <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
-    <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
-    <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
-    <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
-    <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
-    <ROW Action="InstallFinalize" Sequence="6600" SeqType="0" MsiKey="InstallFinalize"/>
-    <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1502"/>
-    <ROW Action="AI_GetArpIconPath" Sequence="1402"/>
-    <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/>
-    <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/>
-    <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
-    <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
-    <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
-    <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
-    <ROW Action="AI_DetectSoftware" Sequence="103"/>
-    <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
-    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/>
-    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102" Builds="ExeBuild"/>
-    <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/>
-    <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/>
-    <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/>
-    <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/>
-    <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/>
-    <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/>
-    <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6594" Builds="ExeBuild"/>
-    <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/>
-    <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/>
-    <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/>
-    <ROW Action="TerminateUI" Sequence="1602"/>
-    <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
-    <ROW Action="AI_AiBackupImmediate" Sequence="1401"/>
-    <ROW Action="AI_AiBackupRollback" Sequence="1501"/>
-    <ROW Action="AI_AiRestoreDeferred" Sequence="6595"/>
-    <ROW Action="AI_EnableDebugLog" Sequence="51"/>
-    <ROW Action="AI_AiRestoreDeferredImpersonate" Sequence="6596"/>
-    <ROW Action="AI_AppSearchEx" Sequence="101"/>
-    <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
-    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
-    <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
-    <ROW Action="AI_DpiContentScale" Sequence="52"/>
-    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
-    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="103"/>
-    <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
-    <ROW Action="AI_DetectSoftware" Sequence="102"/>
-    <ROW Action="AI_EnableDebugLog" Sequence="51"/>
-    <ROW Action="AI_AppSearchEx" Sequence="101"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
-    <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
-    <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
-    <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
-    <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
-    <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
-    <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
-    <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
-    <ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
-    <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
-    <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
-    <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
-    <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
-    <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
-    <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
-    <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
-    <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
-    <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
-    <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
-    <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
-    <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
-    <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
-    <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
-    <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
-    <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
-    <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
-    <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
-    <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
-    <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
-    <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
-    <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
-    <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
-    <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
-    <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
-    <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
-    <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
-    <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
-    <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
-    <ATTRIBUTE name="UsedTheme" value="classic"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
-    <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
-    <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
-    <ROW SearchKey="UpgradeCode" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="2" Property="ZTTAP300_X86_INSTALLED"/>
-    <ROW SearchKey="_" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="1" Property="ZTTAP300_X64_INSTALLED"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
-    <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
-    <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
-    <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
-    <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
-    <ROW Name="Append/Create" TxtUpdateSet="zerotiercli1.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -i %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -i %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
-    <ROW Name="Replace" TxtUpdateSet="zerotiercli1.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
-    <ROW Key="zerotiercli.bat" Component="regid.201001.com.zerotier" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
-    <ROW Key="zerotiercli1.bat" Component="regid.201001.com.zerotier" FileName="zerotier-idtool.bat" Directory="APPDIR" Options="17"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
-    <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
-    <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
-    <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
-    <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
-    <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6"/>
-    <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
-    <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
-    <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="4"/>
-    <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
-    <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
-    <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
-    <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
-    <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
-    <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
-    <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
-    <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
-    <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
-    <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
-    <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
-    <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
-    <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
-    <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
-    <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
-    <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
-    <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
-    <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
-  </COMPONENT>
-  <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
-    <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
-    <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
-  </COMPONENT>
-</DOCUMENT>
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<DOCUMENT Type="Advanced Installer" CreateVersion="10.9" version="14.5.2" Modules="enterprise" RootPath="." Language="en" Id="{DC564647-6BF0-4550-87F4-89C938D0159C}">
+  <COMPONENT cid="caphyon.advinst.msicomp.ProjectOptionsComponent">
+    <ROW Name="HiddenItems" Value="UpdaterComponent;SerValComponent;AutorunComponent;MultipleInstancesComponent;AppXProductDetailsComponent;AppXDependenciesComponent;AppXAppDetailsComponent;AppXVisualAssetsComponent;AppXCapabilitiesComponent;AppXAppDeclarationsComponent;AppXUriRulesComponent;MsiJavaComponent;MsiRegsComponent;MsiExtComponent;MsiAssemblyComponent;AnalyticsComponent;ActSyncAppComponent;MsiMergeModsComponent;MsiThemeComponent;BackgroundImagesComponent;DictionaryComponent;ScheduledTasksComponent;CPLAppletComponent;GameUxComponent;UserAccountsComponent;MsiClassComponent;WebApplicationsComponent;MsiOdbcDataSrcComponent;SqlConnectionComponent;SharePointSlnComponent;SilverlightSlnComponent"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
+    <ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
+    <ROW Property="AI_EMBD_MSI_EXTR_PATH" Value="[TempFolder]" ValueLocId="-"/>
+    <ROW Property="AI_EXTERNALUIUNINSTALLERNAME" MultiBuildValue="DefaultBuild:aiui"/>
+    <ROW Property="AI_FINDEXE_TITLE" Value="Select the installation package for [|ProductName]" ValueLocId="AI.Property.FindExeTitle"/>
+    <ROW Property="AI_PREDEF_LCONDS_PROPS" Value="AI_DETECTED_DOTNET_VERSION"/>
+    <ROW Property="AI_PRODUCTNAME_ARP" Value="ZeroTier One"/>
+    <ROW Property="AI_REQUIRED_DOTNET_DISPLAY" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+    <ROW Property="AI_REQUIRED_DOTNET_VERSION" MultiBuildValue="DefaultBuild:4.5" ValueLocId="-"/>
+    <ROW Property="AI_UNINSTALLER" Value="msiexec.exe"/>
+    <ROW Property="ALLUSERS" Value="1"/>
+    <ROW Property="ARPCOMMENTS" Value="This installer database contains the logic and data required to install [|ProductName]."/>
+    <ROW Property="ARPCONTACT" Value="[email protected]"/>
+    <ROW Property="ARPHELPLINK" Value="https://www.zerotier.com/"/>
+    <ROW Property="ARPNOMODIFY" MultiBuildValue="DefaultBuild:1"/>
+    <ROW Property="ARPNOREPAIR" Value="1" MultiBuildValue="ExeBuild:1"/>
+    <ROW Property="ARPPRODUCTICON" Value="ZeroTierIcon.exe" Type="8"/>
+    <ROW Property="ARPSYSTEMCOMPONENT" Value="1"/>
+    <ROW Property="ARPURLINFOABOUT" Value="https://www.zerotier.com/"/>
+    <ROW Property="ARPURLUPDATEINFO" Value="https://www.zerotier.com/"/>
+    <ROW Property="AiFeatIcoZeroTierOne" Value="ZeroTierIcon.exe" Type="8"/>
+    <ROW Property="CTRLS" Value="2"/>
+    <ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:2"/>
+    <ROW Property="Manufacturer" Value="ZeroTier, Inc."/>
+    <ROW Property="ProductCode" Value="1033:{80CEE5C9-4DF0-43F5-B232-484D6455978E} " Type="16"/>
+    <ROW Property="ProductLanguage" Value="1033"/>
+    <ROW Property="ProductName" Value="ZeroTier One"/>
+    <ROW Property="ProductVersion" Value="1.4.6" Type="32"/>
+    <ROW Property="REBOOT" MultiBuildValue="DefaultBuild:ReallySuppress"/>
+    <ROW Property="RUNAPPLICATION" Value="1" Type="4"/>
+    <ROW Property="SecureCustomProperties" Value="OLDPRODUCTS;AI_NEWERPRODUCTFOUND;AI_SETUPEXEPATH;SETUPEXEDIR"/>
+    <ROW Property="UpgradeCode" Value="{B0E2A5F3-88B6-4E77-B922-CB4739B4C4C8}"/>
+    <ROW Property="WindowsType9X" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+    <ROW Property="WindowsType9XDisplay" MultiBuildValue="DefaultBuild:Windows 9x/ME#ExeBuild:Windows 9x/ME" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT40" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT40Display" MultiBuildValue="DefaultBuild:Windows NT 4.0#ExeBuild:Windows NT 4.0" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT50" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT50Display" MultiBuildValue="DefaultBuild:Windows 2000#ExeBuild:Windows 2000" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT5X" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT5XDisplay" MultiBuildValue="DefaultBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86#ExeBuild:Windows XP/2003 RTM, Windows XP/2003 SP1, Windows XP SP2 x86" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT64" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNT64Display" MultiBuildValue="DefaultBuild:Windows XP SP2 x64, Windows Server 2003 SP2 x64" ValueLocId="-"/>
+    <ROW Property="WindowsTypeNTDisplay" MultiBuildValue="DefaultBuild:Windows XP SP3 x86, Windows Server 2003 SP2 x86" ValueLocId="-"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiDirsComponent">
+    <ROW Directory="APPDIR" Directory_Parent="TARGETDIR" DefaultDir="APPDIR:." IsPseudoRoot="1" DirectoryOptions="2"/>
+    <ROW Directory="CommonAppDataFolder" Directory_Parent="TARGETDIR" DefaultDir="COMMON~1|CommonAppDataFolder" IsPseudoRoot="1"/>
+    <ROW Directory="FontsFolder" Directory_Parent="TARGETDIR" DefaultDir="FONTSF~1|FontsFolder" IsPseudoRoot="1"/>
+    <ROW Directory="One_Dir" Directory_Parent="ZeroTier_Dir" DefaultDir="One"/>
+    <ROW Directory="ProgramFilesFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~1|ProgramFilesFolder" IsPseudoRoot="1"/>
+    <ROW Directory="ProgramMenuFolder" Directory_Parent="TARGETDIR" DefaultDir="PROGRA~2|ProgramMenuFolder" IsPseudoRoot="1"/>
+    <ROW Directory="TARGETDIR" DefaultDir="SourceDir"/>
+    <ROW Directory="ZeroTier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="ZeroTier"/>
+    <ROW Directory="networks.d_Dir" Directory_Parent="One_Dir" DefaultDir="networks.d"/>
+    <ROW Directory="regid.201001.com.zerotier_Dir" Directory_Parent="CommonAppDataFolder" DefaultDir="REGID2~1.ZER|regid.2010-01.com.zerotier"/>
+    <ROW Directory="tapwindows_Dir" Directory_Parent="One_Dir" DefaultDir="TAP-WI~1|tap-windows"/>
+    <ROW Directory="x64_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x64"/>
+    <ROW Directory="x86_Dir" Directory_Parent="tapwindows_Dir" DefaultDir="x86"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiCompsComponent">
+    <ROW Component="AI_CustomARPName" ComponentId="{717E3B00-472C-4E07-BC45-AF83F5442898}" Directory_="APPDIR" Attributes="4" KeyPath="DisplayName" Options="1"/>
+    <ROW Component="AI_DisableModify" ComponentId="{020DCABD-5D56-49B9-AF48-F07F0B55E590}" Directory_="APPDIR" Attributes="4" KeyPath="NoModify" Options="1"/>
+    <ROW Component="AI_ExePath" ComponentId="{8E02B36C-7A19-429B-A93E-77A9261AC918}" Directory_="APPDIR" Attributes="4" KeyPath="AI_ExePath"/>
+    <ROW Component="Hardcodet.Wpf.TaskbarNotification.dll" ComponentId="{BEA825AF-2555-44AF-BE40-47FFC16DCBA6}" Directory_="APPDIR" Attributes="0" KeyPath="Hardcodet.Wpf.TaskbarNotification.dll"/>
+    <ROW Component="Newtonsoft.Json.dll" ComponentId="{0B2F229D-5425-42FB-9E28-F6D25AB2B4B5}" Directory_="APPDIR" Attributes="0" KeyPath="Newtonsoft.Json.dll"/>
+    <ROW Component="ProductInformation" ComponentId="{DB078D04-EA8E-4A7C-9001-89BAD932F9D9}" Directory_="APPDIR" Attributes="4" KeyPath="Version"/>
+    <ROW Component="ZeroTierOne.exe" ComponentId="{18B51525-77BF-4FD9-9C18-A10D4CFC25BA}" Directory_="APPDIR" Attributes="0" KeyPath="ZeroTierOne.exe"/>
+    <ROW Component="copyutil.exe" ComponentId="{9B9E89FB-81CB-4500-978B-11E2FA81B5B4}" Directory_="APPDIR" Attributes="0" KeyPath="copyutil.exe"/>
+    <ROW Component="networks.d" ComponentId="{EF54D0DF-889F-41DC-AF5C-4E7F96AB1C8B}" Directory_="networks.d_Dir" Attributes="0"/>
+    <ROW Component="regid.201001.com.zerotier" ComponentId="{A39C80FC-6A8F-454F-9052-10DAC3C3B139}" Directory_="regid.201001.com.zerotier_Dir" Attributes="0"/>
+    <ROW Component="segoeui.ttf" ComponentId="{9F415308-A118-419F-AD8A-678DEA856B78}" Directory_="FontsFolder" Attributes="144" Type="0"/>
+    <ROW Component="zerotierone_x64.exe" ComponentId="{DFCFB72D-B055-4E60-B6D8-81FF585C2183}" Directory_="One_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zerotierone_x64.exe"/>
+    <ROW Component="zerotierone_x86.exe" ComponentId="{5D2F3366-4FE1-40A4-A81A-66C49FA11F1C}" Directory_="One_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zerotierone_x86.exe"/>
+    <ROW Component="zttap300.cat_1" ComponentId="{9F913E48-095B-4EA3-98DA-EDAB1593F3E3}" Directory_="x86_Dir" Attributes="0" Condition="NOT VersionNT64" KeyPath="zttap300.cat_3" Type="0"/>
+    <ROW Component="zttap300.inf" ComponentId="{D4839F5E-FB94-41CB-9B1B-177A97ADC904}" Directory_="x64_Dir" Attributes="256" Condition="VersionNT64" KeyPath="zttap300.inf"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiFeatsComponent">
+    <ROW Feature="ZeroTierOne" Title="MainFeature" Description="ZeroTier One" Display="1" Level="1" Directory_="APPDIR" Attributes="0" Components="AI_CustomARPName AI_DisableModify AI_ExePath Hardcodet.Wpf.TaskbarNotification.dll Newtonsoft.Json.dll ProductInformation ZeroTierOne.exe copyutil.exe networks.d regid.201001.com.zerotier segoeui.ttf zerotierone_x64.exe zerotierone_x86.exe zttap300.cat_1"/>
+    <ROW Feature="zttap300" Feature_Parent="ZeroTierOne" Title="zttap300" Description="ZeroTier Virtual Network Port Driver" Display="1" Level="1" Directory_="APPDIR" Attributes="16" Components="zttap300.inf"/>
+    <ATTRIBUTE name="CurrentFeature" value="ZeroTierOne"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiFilesComponent">
+    <ROW File="Hardcodet.Wpf.TaskbarNotification.dll" Component_="Hardcodet.Wpf.TaskbarNotification.dll" FileName="HARDCO~1.DLL|Hardcodet.Wpf.TaskbarNotification.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Hardcodet.Wpf.TaskbarNotification.dll" SelfReg="false" NextFile="segoeui.ttf" DigSign="true"/>
+    <ROW File="Newtonsoft.Json.dll" Component_="Newtonsoft.Json.dll" FileName="NEWTON~1.DLL|Newtonsoft.Json.dll" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\Newtonsoft.Json.dll" SelfReg="false" NextFile="copyutil.exe" DigSign="true"/>
+    <ROW File="ZeroTierOne.exe" Component_="ZeroTierOne.exe" FileName="ZEROTI~1.EXE|ZeroTier One.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\WinUI\bin\Release\ZeroTier One.exe" SelfReg="false" NextFile="zttap300.cat_2" DigSign="true"/>
+    <ROW File="copyutil.exe" Component_="copyutil.exe" FileName="copyutil.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\copyutil\bin\Release\copyutil.exe" SelfReg="false" NextFile="Hardcodet.Wpf.TaskbarNotification.dll" DigSign="true"/>
+    <ROW File="segoeui.ttf" Component_="segoeui.ttf" FileName="segoeui.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeui.ttf" SelfReg="false" NextFile="segoeuib.ttf"/>
+    <ROW File="segoeuib.ttf" Component_="segoeui.ttf" FileName="segoeuib.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuib.ttf" SelfReg="false" NextFile="segoeuii.ttf"/>
+    <ROW File="segoeuii.ttf" Component_="segoeui.ttf" FileName="segoeuii.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuii.ttf" SelfReg="false" NextFile="segoeuiz.ttf"/>
+    <ROW File="segoeuiz.ttf" Component_="segoeui.ttf" FileName="segoeuiz.ttf" Attributes="0" SourcePath="..\..\..\windows\WinUI\Fonts\segoeuiz.ttf" SelfReg="false"/>
+    <ROW File="zerotierone_x64.exe" Component_="zerotierone_x64.exe" FileName="ZEROTI~2.EXE|zerotier-one_x64.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\x64\Release\zerotier-one_x64.exe" SelfReg="false" NextFile="ZeroTierOne.exe" DigSign="true"/>
+    <ROW File="zerotierone_x86.exe" Component_="zerotierone_x86.exe" FileName="ZEROTI~1.EXE|zerotier-one_x86.exe" Version="65535.65535.65535.65535" Attributes="0" SourcePath="..\..\..\windows\Build\Win32\Release\zerotier-one_x86.exe" SelfReg="false" NextFile="zerotierone_x64.exe" DigSign="true"/>
+    <ROW File="zttap300.cat_2" Component_="zttap300.inf" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_2"/>
+    <ROW File="zttap300.cat_3" Component_="zttap300.cat_1" FileName="zttap300.cat" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.cat" SelfReg="false" NextFile="zttap300.sys_3"/>
+    <ROW File="zttap300.inf" Component_="zttap300.inf" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.inf" SelfReg="false" NextFile="zttap300.cat_3"/>
+    <ROW File="zttap300.inf_1" Component_="zttap300.cat_1" FileName="zttap300.inf" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.inf" SelfReg="false" NextFile="Newtonsoft.Json.dll"/>
+    <ROW File="zttap300.sys_2" Component_="zttap300.inf" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x64\zttap300.sys" SelfReg="false" NextFile="zttap300.inf"/>
+    <ROW File="zttap300.sys_3" Component_="zttap300.cat_1" FileName="zttap300.sys" Attributes="0" SourcePath="..\..\bin\tap-windows-ndis6\x86\zttap300.sys" SelfReg="false" NextFile="zttap300.inf_1"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.AiPersistentDataComponent">
+    <ROW PersistentRow="segoeui.ttf" Type="0" Condition="1"/>
+    <ROW PersistentRow="segoeuib.ttf" Type="0" Condition="1"/>
+    <ROW PersistentRow="segoeuii.ttf" Type="0" Condition="1"/>
+    <ROW PersistentRow="segoeuiz.ttf" Type="0" Condition="1"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.BootstrOptComponent">
+    <ROW BootstrOptKey="GlobalOptions" GeneralOptions="qh" DownloadFolder="[AppDataFolder][|Manufacturer]\[|ProductName]\prerequisites"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.BootstrapperUISequenceComponent">
+    <ROW Action="AI_DetectSoftware" Sequence="101"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.BuildComponent">
+    <ROW BuildKey="DefaultBuild" BuildName="MSI" BuildOrder="1" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="4" ExtUI="true" UseLargeSchema="true"/>
+    <ROW BuildKey="ExeBuild" BuildName="update" BuildOrder="2" BuildType="1" PackageFolder="..\..\.." PackageFileName="ZeroTier One" Languages="en" InstallationType="2" CabsLocation="1" CompressCabs="false" UseLzma="true" LzmaMethod="2" LzmaCompressionLevel="4" PackageType="1" FilesInsideExe="true" ExeIconPath="..\..\..\artwork\ZeroTierIcon.ico" ExtractionFolder="[AppDataFolder][|Manufacturer]\[|ProductName] [|ProductVersion]\install" MsiCmdLine="/qn" ExtUI="true" UseLargeSchema="true" ExeName="zt1_update_2_1,2_[|ProductVersion]_0"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.CacheComponent">
+    <ATTRIBUTE name="Enable" value="false"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageComponent">
+    <ROW ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Order="1" Options="110" InstallCondition="(NOT Installed) AND VersionNT64" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND VersionNT64"/>
+    <ROW ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Order="2" Options="110" InstallCondition="(NOT Installed) AND (NOT VersionNT64)" MaintenanceCondition="FALSE" RemoveCondition="REMOVE=&quot;ALL&quot; AND (NOT VersionNT64)"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.ChainedPackageFileComponent">
+    <ROW FileId="ZeroTierOne_NDIS6_x64.msi" ChainedPackage="ZeroTierOne_NDIS6_x64.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x64.msi" Content="..\..\bin\tap-windows-ndis6\x64\ZeroTierOne_NDIS6_x64.msi"/>
+    <ROW FileId="ZeroTierOne_NDIS6_x86.msi" ChainedPackage="ZeroTierOne_NDIS6_x86.msi" Options="1" TargetPath="ZeroTierOne_NDIS6_x86.msi" Content="..\..\bin\tap-windows-ndis6\x86\ZeroTierOne_NDIS6_x86.msi"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.DictionaryComponent">
+    <ROW Path="&lt;AI_DICTS&gt;ui.ail"/>
+    <ROW Path="&lt;AI_DICTS&gt;ui_en.ail"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.DigCertStoreComponent">
+    <ROW TimeStampUrl="http://timestamp.verisign.com/scripts/timstamp.dll" SignerDescription="ZeroTier One" DescriptionUrl="https://www.zerotier.com/" SignOptions="7" SignTool="0" UseSha256="1" Thumbprint="7f01c3746df9e6c8235ea2ae38d3cdfeb185728b Subject: ZeroTier, Inc.&#10;Issuer: DigiCert EV Code Signing CA (SHA2)&#10;Valid from 11/30/2016 to 12/05/2019"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.FirewallExceptionComponent">
+    <ROW FirewallException="ZeroTierOneControl" DisplayName="ZeroTier One TCP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="TCP"/>
+    <ROW FirewallException="ZeroTierOneUDP9993" DisplayName="ZeroTier One UDP/9993" GroupName="ZeroTierOne" Enabled="1" Scope="*" Condition="1" Profiles="7" Port="9993" Protocol="UDP"/>
+    <ROW FirewallException="ZeroTierOnex64Binary" DisplayName="ZeroTier One x64 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x64.exe=2) AND ($zerotierone_x64.exe=3))" Profiles="0" AppPath="[#zerotierone_x64.exe]" Port="*" Protocol="ANY"/>
+    <ROW FirewallException="ZeroTierOnex86Binary" DisplayName="ZeroTier One x86 Binary" Enabled="1" Scope="*" Condition="((?zerotierone_x86.exe=2) AND ($zerotierone_x86.exe=3))" Profiles="0" AppPath="[#zerotierone_x86.exe]" Port="*" Protocol="ANY"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.FragmentComponent">
+    <ROW Fragment="CommonUI.aip" Path="&lt;AI_FRAGS&gt;CommonUI.aip"/>
+    <ROW Fragment="MaintenanceTypeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceTypeDlg.aip"/>
+    <ROW Fragment="MaintenanceWelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\MaintenanceWelcomeDlg.aip"/>
+    <ROW Fragment="SequenceDialogs.aip" Path="&lt;AI_THEMES&gt;classic\fragments\SequenceDialogs.aip"/>
+    <ROW Fragment="Sequences.aip" Path="&lt;AI_FRAGS&gt;Sequences.aip"/>
+    <ROW Fragment="StaticUIStrings.aip" Path="&lt;AI_FRAGS&gt;StaticUIStrings.aip"/>
+    <ROW Fragment="UI.aip" Path="&lt;AI_THEMES&gt;classic\fragments\UI.aip"/>
+    <ROW Fragment="Validation.aip" Path="&lt;AI_FRAGS&gt;Validation.aip"/>
+    <ROW Fragment="VerifyRemoveDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRemoveDlg.aip"/>
+    <ROW Fragment="VerifyRepairDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\VerifyRepairDlg.aip"/>
+    <ROW Fragment="WelcomeDlg.aip" Path="&lt;AI_THEMES&gt;classic\fragments\WelcomeDlg.aip"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiActionTextComponent">
+    <ROW Action="AI_AiBackupImmediate" Description="Preparing backup operation" DescriptionLocId="ActionText.Description.AI_AiBackupImmediate" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupImmediate"/>
+    <ROW Action="AI_AiBackupRollback" Description="Rollback backup" DescriptionLocId="ActionText.Description.AI_AiBackupRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiBackupRollback"/>
+    <ROW Action="AI_AiRestoreDeferred" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+    <ROW Action="AI_AiRestoreDeferredImpersonate" Description="Executing restore operation" DescriptionLocId="ActionText.Description.AI_AiRestoreDeferred" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreDeferred"/>
+    <ROW Action="AI_AiRestoreRollback" Description="Rollback restore" DescriptionLocId="ActionText.Description.AI_AiRestoreRollback" Template="Path: [1]" TemplateLocId="ActionText.Template.AI_AiRestoreRollback"/>
+    <ROW Action="AI_DeleteLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+    <ROW Action="AI_DeleteRLzma" Description="Deleting files extracted from archive" DescriptionLocId="ActionText.Description.AI_DeleteLzma" TemplateLocId="-"/>
+    <ROW Action="AI_ExtractFiles" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+    <ROW Action="AI_ExtractLzma" Description="Extracting files from archive" DescriptionLocId="ActionText.Description.AI_ExtractLzma" TemplateLocId="-"/>
+    <ROW Action="AI_FwConfig" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwConfig" Template="Configuring Windows Firewall rule: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwConfig"/>
+    <ROW Action="AI_FwInstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwInstall"/>
+    <ROW Action="AI_FwRemove" Description="Executing Windows Firewall configurations" DescriptionLocId="ActionText.Description.AI_FwRemove" Template="Configuring Windows Firewall rule:  &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_FwRemove"/>
+    <ROW Action="AI_FwRollback" Description="Rolling back Windows Firewall configurations." DescriptionLocId="ActionText.Description.AI_FwRollback" Template="Rolling back Windows Firewall configurations." TemplateLocId="ActionText.Template.AI_FwRollback"/>
+    <ROW Action="AI_FwUninstall" Description="Generating actions to configure Windows Firewall" DescriptionLocId="ActionText.Description.AI_FwUninstall"/>
+    <ROW Action="AI_TxtUpdaterCommit" Description="Commit text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterCommit" Template="Commit text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterCommit"/>
+    <ROW Action="AI_TxtUpdaterConfig" Description="Executing text file updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterConfig" Template="Updating text file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_TxtUpdaterConfig"/>
+    <ROW Action="AI_TxtUpdaterInstall" Description="Generating actions to configure text files updates" DescriptionLocId="ActionText.Description.AI_TxtUpdaterInstall"/>
+    <ROW Action="AI_TxtUpdaterRollback" Description="Rolling back text file changes. " DescriptionLocId="ActionText.Description.AI_TxtUpdaterRollback" Template="Rolling back text file changes." TemplateLocId="ActionText.Template.AI_TxtUpdaterRollback"/>
+    <ROW Action="AI_XmlCommit" Description="Committing XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlCommit" Template="Committing XML file configurations." TemplateLocId="ActionText.Template.AI_XmlCommit"/>
+    <ROW Action="AI_XmlConfig" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlConfig" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlConfig"/>
+    <ROW Action="AI_XmlInstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlInstall"/>
+    <ROW Action="AI_XmlRemove" Description="Executing XML file configurations" DescriptionLocId="ActionText.Description.AI_XmlRemove" Template="Configuring XML file: &quot;[1]&quot;" TemplateLocId="ActionText.Template.AI_XmlRemove"/>
+    <ROW Action="AI_XmlRollback" Description="Rolling back XML file configurations." DescriptionLocId="ActionText.Description.AI_XmlRollback" Template="Rolling back XML file configurations." TemplateLocId="ActionText.Template.AI_XmlRollback"/>
+    <ROW Action="AI_XmlUninstall" Description="Generating actions to configure XML files" DescriptionLocId="ActionText.Description.AI_XmlUninstall"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiAppSearchComponent">
+    <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="ExeBuild"/>
+    <ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_LM" Builds="ExeBuild"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
+    <ROW Name="ExternalUICleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ExternalUICleaner.dll"/>
+    <ROW Name="NetFirewall.dll" SourcePath="&lt;AI_CUSTACTS&gt;NetFirewall.dll"/>
+    <ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
+    <ROW Name="ResourceCleaner.dll" SourcePath="&lt;AI_CUSTACTS&gt;ResourceCleaner.dll"/>
+    <ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
+    <ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
+    <ROW Name="aicustact.dll" SourcePath="&lt;AI_CUSTACTS&gt;aicustact.dll"/>
+    <ROW Name="chainersupport.dll" SourcePath="&lt;AI_CUSTACTS&gt;chainersupport.dll"/>
+    <ROW Name="lzmaextractor.dll" SourcePath="&lt;AI_CUSTACTS&gt;lzmaextractor.dll"/>
+    <ROW Name="msichainer.exe" SourcePath="&lt;AI_CUSTACTS&gt;msichainer.exe"/>
+    <ROW Name="xmlCfg.dll" SourcePath="&lt;AI_CUSTACTS&gt;xmlCfg.dll"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiConditionComponent">
+    <ROW Feature_="zttap300" Level="0" Condition="((VersionNT &lt; 500) OR (NOT VersionNT))"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlComponent">
+    <ROW Dialog_="WelcomeDlg" Control="WelcomeDlgDialogInitializer" Type="DialogInitializer" X="0" Y="0" Width="0" Height="0" Attributes="0" Order="-1" TextLocId="-" HelpLocId="-" ExtDataLocId="-"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
+    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="1"/>
+    <ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
+    <ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
+    <ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
+    <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_MAINT" Ordering="198"/>
+    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="202"/>
+    <ROW Dialog_="MaintenanceTypeDlg" Control_="ChangeButton" Event="NewDialog" Argument="CustomizeDlg" Condition="AI_MAINT" Ordering="501"/>
+    <ROW Dialog_="MaintenanceTypeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceWelcomeDlg" Condition="AI_MAINT" Ordering="1"/>
+    <ROW Dialog_="MaintenanceTypeDlg" Control_="RemoveButton" Event="NewDialog" Argument="VerifyRemoveDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="601"/>
+    <ROW Dialog_="VerifyRemoveDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="1"/>
+    <ROW Dialog_="MaintenanceTypeDlg" Control_="RepairButton" Event="NewDialog" Argument="VerifyRepairDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="601"/>
+    <ROW Dialog_="VerifyRepairDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="1"/>
+    <ROW Dialog_="VerifyRepairDlg" Control_="Repair" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Repair&quot;" Ordering="399" Options="1"/>
+    <ROW Dialog_="VerifyRemoveDlg" Control_="Remove" Event="EndDialog" Argument="Return" Condition="AI_MAINT AND InstallMode=&quot;Remove&quot;" Ordering="299" Options="1"/>
+    <ROW Dialog_="PatchWelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_PATCH" Ordering="201"/>
+    <ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
+    <ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="203"/>
+    <ROW Dialog_="ResumeDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_RESUME" Ordering="299"/>
+    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfRbDiskDlg" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND (PROMPTROLLBACKCOST=&quot;P&quot; OR NOT PROMPTROLLBACKCOST)" Ordering="2" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="EnableRollback" Argument="False" Condition="AI_INSTALL AND OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 0 AND PROMPTROLLBACKCOST=&quot;D&quot;" Ordering="3" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="Next" Event="SpawnDialog" Argument="OutOfDiskDlg" Condition="AI_INSTALL AND ( (OutOfDiskSpace = 1 AND OutOfNoRbDiskSpace = 1) OR (OutOfDiskSpace = 1 AND PROMPTROLLBACKCOST=&quot;F&quot;) )" Ordering="4" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL" Ordering="2" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="WelcomeDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL" Ordering="3" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[ButtonText_Next]" Argument="[AI_ButtonText_Next_Orig]" Condition="AI_INSTALL" Ordering="0" Options="2"/>
+    <ROW Dialog_="WelcomeDlg" Control_="Back" Event="[Text_Next]" Argument="[AI_Text_Next_Orig]" Condition="AI_INSTALL" Ordering="1" Options="2"/>
+    <ROW Dialog_="FatalError" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="102"/>
+    <ROW Dialog_="UserExit" Control_="Finish" Event="DoAction" Argument="AI_AiBackupCleanup" Condition="1" Ordering="101"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
+    <ROW Directory_="networks.d_Dir" Component_="networks.d" ManualDelete="false"/>
+    <ROW Directory_="regid.201001.com.zerotier_Dir" Component_="regid.201001.com.zerotier" ManualDelete="false"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiCustActComponent">
+    <ROW Action="AI_AiBackupCleanup" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupCleanup" WithoutSeq="true"/>
+    <ROW Action="AI_AiBackupImmediate" Type="1" Source="ResourceCleaner.dll" Target="OnAiBackupImmediate"/>
+    <ROW Action="AI_AiBackupRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiBackupRollback"/>
+    <ROW Action="AI_AiRestoreDeferred" Type="11265" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferred"/>
+    <ROW Action="AI_AiRestoreDeferredImpersonate" Type="9217" Source="ResourceCleaner.dll" Target="OnAiRestoreDeferredImpersonate"/>
+    <ROW Action="AI_AiRestoreRollback" Type="11521" Source="ResourceCleaner.dll" Target="OnAiRestoreRollback" WithoutSeq="true"/>
+    <ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
+    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
+    <ROW Action="AI_CommitChainers" Type="11841" Source="chainersupport.dll" Target="CommitChainedPackages" WithoutSeq="true"/>
+    <ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[~]"/>
+    <ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="[~]"/>
+    <ROW Action="AI_DATA_SETTER_2" Type="51" Source="CustomActionData" Target="[~]"/>
+    <ROW Action="AI_DATA_SETTER_3" Type="51" Source="CustomActionData" Target="[~]"/>
+    <ROW Action="AI_DATA_SETTER_4" Type="51" Source="AI_ExtractFiles" Target="[AI_SETUPEXEPATH]"/>
+    <ROW Action="AI_DATA_SETTER_6" Type="51" Source="CustomActionData" Target="ZeroTier One.exe"/>
+    <ROW Action="AI_DOWNGRADE" Type="19" Target="4010"/>
+    <ROW Action="AI_DeleteCadLzma" Type="51" Source="AI_DeleteLzma" Target="[AI_SETUPEXEPATH]"/>
+    <ROW Action="AI_DeleteLzma" Type="1025" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+    <ROW Action="AI_DeleteRCadLzma" Type="51" Source="AI_DeleteRLzma" Target="[AI_SETUPEXEPATH]"/>
+    <ROW Action="AI_DeleteRLzma" Type="1281" Source="lzmaextractor.dll" Target="DeleteLZMAFiles"/>
+    <ROW Action="AI_DetectSoftware" Type="257" Source="SoftwareDetector.dll" Target="OnDetectSoftware"/>
+    <ROW Action="AI_DoRemoveExternalUIStub" Type="3585" Source="ExternalUICleaner.dll" Target="DoRemoveExternalUIStub" WithoutSeq="true"/>
+    <ROW Action="AI_DpiContentScale" Type="1" Source="aicustact.dll" Target="DpiContentScale"/>
+    <ROW Action="AI_EnableDebugLog" Type="321" Source="aicustact.dll" Target="EnableDebugLog"/>
+    <ROW Action="AI_EstimateExtractFiles" Type="1" Source="Prereq.dll" Target="EstimateExtractFiles"/>
+    <ROW Action="AI_ExtractCadLzma" Type="51" Source="AI_ExtractLzma" Target="[AI_SETUPEXEPATH]"/>
+    <ROW Action="AI_ExtractFiles" Type="1025" Source="Prereq.dll" Target="ExtractSourceFiles" AdditionalSeq="AI_DATA_SETTER_4"/>
+    <ROW Action="AI_ExtractLzma" Type="1025" Source="lzmaextractor.dll" Target="ExtractLZMAFiles"/>
+    <ROW Action="AI_FindExeLzma" Type="1" Source="lzmaextractor.dll" Target="FindEXE"/>
+    <ROW Action="AI_FwConfig" Type="11265" Source="NetFirewall.dll" Target="OnFwConfig" WithoutSeq="true"/>
+    <ROW Action="AI_FwInstall" Type="1" Source="NetFirewall.dll" Target="OnFwInstall" AdditionalSeq="AI_DATA_SETTER_2"/>
+    <ROW Action="AI_FwRemove" Type="11265" Source="NetFirewall.dll" Target="OnFwRemove" WithoutSeq="true"/>
+    <ROW Action="AI_FwRollback" Type="11521" Source="NetFirewall.dll" Target="OnFwRollback" WithoutSeq="true"/>
+    <ROW Action="AI_FwUninstall" Type="1" Source="NetFirewall.dll" Target="OnFwUninstall" AdditionalSeq="AI_DATA_SETTER_3"/>
+    <ROW Action="AI_GetArpIconPath" Type="1" Source="aicustact.dll" Target="GetArpIconPath"/>
+    <ROW Action="AI_InstallModeCheck" Type="1" Source="aicustact.dll" Target="UpdateInstallMode" WithoutSeq="true"/>
+    <ROW Action="AI_LaunchApp" Type="1" Source="aicustact.dll" Target="[#ZeroTierOne.exe]"/>
+    <ROW Action="AI_PREPARE_UPGRADE" Type="65" Source="aicustact.dll" Target="PrepareUpgrade"/>
+    <ROW Action="AI_PrepareChainers" Type="1" Source="chainersupport.dll" Target="PrepareChainedPackages"/>
+    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH" Target="[AI_SETUPEXEPATH_ORIGINAL]"/>
+    <ROW Action="AI_RESTORE_LOCATION" Type="65" Source="aicustact.dll" Target="RestoreLocation"/>
+    <ROW Action="AI_RemoveExternalUIStub" Type="1" Source="ExternalUICleaner.dll" Target="RemoveExternalUIStub"/>
+    <ROW Action="AI_ResolveKnownFolders" Type="1" Source="aicustact.dll" Target="AI_ResolveKnownFolders"/>
+    <ROW Action="AI_RollbackChainers" Type="11585" Source="chainersupport.dll" Target="RollbackChainedPackages" WithoutSeq="true"/>
+    <ROW Action="AI_SHOW_LOG" Type="65" Source="aicustact.dll" Target="LaunchLogFile" WithoutSeq="true"/>
+    <ROW Action="AI_STORE_LOCATION" Type="51" Source="ARPINSTALLLOCATION" Target="[APPDIR]"/>
+    <ROW Action="AI_TxtUpdaterCommit" Type="11777" Source="TxtUpdater.dll" Target="OnTxtUpdaterCommit" WithoutSeq="true"/>
+    <ROW Action="AI_TxtUpdaterConfig" Type="11265" Source="TxtUpdater.dll" Target="OnTxtUpdaterConfig" WithoutSeq="true"/>
+    <ROW Action="AI_TxtUpdaterInstall" Type="1" Source="TxtUpdater.dll" Target="OnTxtUpdaterInstall"/>
+    <ROW Action="AI_TxtUpdaterRollback" Type="11521" Source="TxtUpdater.dll" Target="OnTxtUpdaterRollback" WithoutSeq="true"/>
+    <ROW Action="AI_XmlCommit" Type="11777" Source="xmlCfg.dll" Target="OnXmlCommit" WithoutSeq="true"/>
+    <ROW Action="AI_XmlConfig" Type="11265" Source="xmlCfg.dll" Target="OnXmlConfig" WithoutSeq="true"/>
+    <ROW Action="AI_XmlInstall" Type="1" Source="xmlCfg.dll" Target="OnXmlInstall" AdditionalSeq="AI_DATA_SETTER"/>
+    <ROW Action="AI_XmlRemove" Type="11265" Source="xmlCfg.dll" Target="OnXmlRemove" WithoutSeq="true"/>
+    <ROW Action="AI_XmlRollback" Type="11521" Source="xmlCfg.dll" Target="OnXmlRollback" WithoutSeq="true"/>
+    <ROW Action="AI_XmlUninstall" Type="1" Source="xmlCfg.dll" Target="OnXmlUninstall" AdditionalSeq="AI_DATA_SETTER_1"/>
+    <ROW Action="SET_APPDIR" Type="307" Source="APPDIR" Target="[ProgramFilesFolder][Manufacturer]\[ProductName]" MultiBuildTarget="DefaultBuild:[ProgramFilesFolder]ZeroTier\One"/>
+    <ROW Action="SET_SHORTCUTDIR" Type="307" Source="SHORTCUTDIR" Target="[ProgramMenuFolder][ProductName]" MultiBuildTarget="DefaultBuild:[ProgramMenuFolder]"/>
+    <ROW Action="SET_TARGETDIR_TO_APPDIR" Type="51" Source="TARGETDIR" Target="[APPDIR]"/>
+    <ROW Action="TapDeviceRemove32" Type="3154" Source="zerotierone_x86.exe" Target="-D"/>
+    <ROW Action="TapDeviceRemove64" Type="3154" Source="zerotierone_x64.exe" Target="-D"/>
+    <ROW Action="TerminateUI" Type="65" Source="aicustact.dll" Target="StopProcess" Options="1" AdditionalSeq="AI_DATA_SETTER_6"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiEmbeddedChainerComponent">
+    <ROW MsiEmbeddedChainer="msichainer.exe" Condition="VersionMsi &gt;= &quot;4.05&quot;" CommandLine="[AI_CHAINER_CMD_LINE]" Source="msichainer.exe" Type="2"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
+    <ROW Environment="Path" Name="=-*Path" Value="[~];[APPDIR]" Component_="ZeroTierOne.exe"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiFontsComponent">
+    <ROW File_="segoeui.ttf"/>
+    <ROW File_="segoeuib.ttf"/>
+    <ROW File_="segoeuii.ttf"/>
+    <ROW File_="segoeuiz.ttf"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiIconsComponent">
+    <ROW Name="ZeroTierIcon.exe" SourcePath="..\..\..\artwork\ZeroTierIcon.ico" Index="0"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstExSeqComponent">
+    <ROW Action="AI_DOWNGRADE" Condition="AI_NEWERPRODUCTFOUND AND (UILevel &lt;&gt; 5)" Sequence="210"/>
+    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+    <ROW Action="AI_STORE_LOCATION" Condition="(Not Installed) OR REINSTALL" Sequence="1503"/>
+    <ROW Action="AI_PREPARE_UPGRADE" Condition="AI_UPGRADE=&quot;No&quot; AND (Not Installed)" Sequence="1399"/>
+    <ROW Action="AI_ResolveKnownFolders" Sequence="52"/>
+    <ROW Action="AI_XmlInstall" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5103"/>
+    <ROW Action="AI_DATA_SETTER" Condition="(REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5102"/>
+    <ROW Action="AI_XmlUninstall" Condition="(REMOVE)" Sequence="3102"/>
+    <ROW Action="AI_DATA_SETTER_1" Condition="(REMOVE)" Sequence="3101"/>
+    <ROW Action="InstallFinalize" Sequence="6600" SeqType="0" MsiKey="InstallFinalize"/>
+    <ROW Action="AI_RemoveExternalUIStub" Condition="(REMOVE=&quot;ALL&quot;) AND ((VersionNT &gt; 500) OR((VersionNT = 500) AND (ServicePackLevel &gt;= 4)))" Sequence="1502"/>
+    <ROW Action="AI_GetArpIconPath" Sequence="1402"/>
+    <ROW Action="TapDeviceRemove32" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( NOT VersionNT64 )" Sequence="1603"/>
+    <ROW Action="TapDeviceRemove64" Condition="( Installed AND ( REMOVE = &quot;ALL&quot; OR AI_INSTALL_MODE = &quot;Remove&quot; ) AND NOT UPGRADINGPRODUCTCODE ) AND ( VersionNT64 )" Sequence="1604"/>
+    <ROW Action="AI_FwInstall" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5802"/>
+    <ROW Action="AI_DATA_SETTER_2" Condition="(VersionNT &gt;= 501) AND (REMOVE &lt;&gt; &quot;ALL&quot;)" Sequence="5801"/>
+    <ROW Action="AI_FwUninstall" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1702"/>
+    <ROW Action="AI_DATA_SETTER_3" Condition="(VersionNT &gt;= 501) AND (REMOVE=&quot;ALL&quot;)" Sequence="1701"/>
+    <ROW Action="AI_DetectSoftware" Sequence="103"/>
+    <ROW Action="AI_TxtUpdaterInstall" Sequence="5101"/>
+    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99" Builds="ExeBuild"/>
+    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="102" Builds="ExeBuild"/>
+    <ROW Action="AI_DeleteCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="199" Builds="ExeBuild"/>
+    <ROW Action="AI_DeleteRCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="198" Builds="ExeBuild"/>
+    <ROW Action="AI_ExtractCadLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="197" Builds="ExeBuild"/>
+    <ROW Action="AI_FindExeLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="196" Builds="ExeBuild"/>
+    <ROW Action="AI_ExtractLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1549" Builds="ExeBuild"/>
+    <ROW Action="AI_DeleteRLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="1548" Builds="ExeBuild"/>
+    <ROW Action="AI_DeleteLzma" Condition="SETUPEXEDIR=&quot;&quot; AND Installed AND (REMOVE&lt;&gt;&quot;ALL&quot;) AND (AI_INSTALL_MODE&lt;&gt;&quot;Remove&quot;) AND (NOT PATCH)" Sequence="6594" Builds="ExeBuild"/>
+    <ROW Action="AI_ExtractFiles" Sequence="3998" Builds="ExeBuild"/>
+    <ROW Action="AI_DATA_SETTER_4" Sequence="3997"/>
+    <ROW Action="AI_EstimateExtractFiles" Sequence="3999" Builds="ExeBuild"/>
+    <ROW Action="TerminateUI" Sequence="1602"/>
+    <ROW Action="AI_DATA_SETTER_6" Sequence="1601"/>
+    <ROW Action="AI_AiBackupImmediate" Sequence="1401"/>
+    <ROW Action="AI_AiBackupRollback" Sequence="1501"/>
+    <ROW Action="AI_AiRestoreDeferred" Sequence="6595"/>
+    <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+    <ROW Action="AI_AiRestoreDeferredImpersonate" Sequence="6596"/>
+    <ROW Action="AI_AppSearchEx" Sequence="101"/>
+    <ROW Action="AI_PrepareChainers" Condition="VersionMsi &gt;= &quot;4.05&quot;" Sequence="5851"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiInstallUISequenceComponent">
+    <ROW Action="AI_RESTORE_LOCATION" Condition="APPDIR=&quot;&quot;" Sequence="749"/>
+    <ROW Action="AI_ResolveKnownFolders" Sequence="53"/>
+    <ROW Action="AI_DpiContentScale" Sequence="52"/>
+    <ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Sequence="99"/>
+    <ROW Action="AI_RESTORE_AI_SETUPEXEPATH" Condition="AI_SETUPEXEPATH_ORIGINAL" Sequence="103"/>
+    <ROW Action="ExecuteAction" Sequence="1299" SeqType="0" MsiKey="ExecuteAction"/>
+    <ROW Action="AI_DetectSoftware" Sequence="102"/>
+    <ROW Action="AI_EnableDebugLog" Sequence="51"/>
+    <ROW Action="AI_AppSearchEx" Sequence="101"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiLaunchConditionsComponent">
+    <ROW Condition="( Version9X OR ( NOT VersionNT64 ) OR ( VersionNT64 AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType &lt;&gt; 1)) AND ((VersionNT64 &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2) OR (MsiNTProductType = 1)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNT64Display]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT64" IsPredefined="true" Builds="DefaultBuild"/>
+    <ROW Condition="( Version9X OR VersionNT64 OR ( VersionNT AND ((VersionNT &lt;&gt; 501) OR (ServicePackLevel &lt;&gt; 3)) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel &lt;&gt; 2)) ) )" Description="[ProductName] cannot be installed on the following Windows versions: [WindowsTypeNTDisplay]." DescriptionLocId="AI.LaunchCondition.NoSpecificNT" IsPredefined="true" Builds="DefaultBuild"/>
+    <ROW Condition="(VersionNT &lt;&gt; 400)" Description="[ProductName] cannot be installed on [WindowsTypeNT40Display]." DescriptionLocId="AI.LaunchCondition.NoNT40" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+    <ROW Condition="(VersionNT &lt;&gt; 500)" Description="[ProductName] cannot be installed on [WindowsTypeNT50Display]." DescriptionLocId="AI.LaunchCondition.NoNT50" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+    <ROW Condition="(VersionNT64 OR ((VersionNT &lt;&gt; 501) OR (ServicePackLevel = 3))) AND ((VersionNT &lt;&gt; 502) OR (ServicePackLevel = 2))" Description="[ProductName] cannot be installed on [WindowsTypeNT5XDisplay]." DescriptionLocId="AI.LaunchCondition.NoNT5X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+    <ROW Condition="AI_DETECTED_DOTNET_VERSION &gt;= AI_REQUIRED_DOTNET_VERSION" Description="[ProductName] cannot be installed on systems with .NET Framework version lower than [AI_REQUIRED_DOTNET_DISPLAY]." DescriptionLocId="AI.LaunchCondition.DotNET" IsPredefined="true" Builds="DefaultBuild"/>
+    <ROW Condition="Privileged" Description="[ProductName] requires administrative privileges to install." DescriptionLocId="AI.LaunchCondition.Privileged" IsPredefined="true" Builds="DefaultBuild"/>
+    <ROW Condition="SETUPEXEDIR OR (REMOVE=&quot;ALL&quot;)" Description="This package can only be run from a bootstrapper." DescriptionLocId="AI.LaunchCondition.RequireBootstrapper" IsPredefined="true" Builds="ExeBuild"/>
+    <ROW Condition="VersionNT" Description="[ProductName] cannot be installed on [WindowsType9XDisplay]." DescriptionLocId="AI.LaunchCondition.No9X" IsPredefined="true" Builds="DefaultBuild;ExeBuild"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiRegLocatorComponent">
+    <ROW Signature_="AI_EXE_PATH_CU" Root="1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+    <ROW Signature_="AI_EXE_PATH_LM" Root="2" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Type="2"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiRegsComponent">
+    <ROW Registry="AI_ExePath" Root="-1" Key="Software\Caphyon\Advanced Installer\LZMA\[ProductCode]\[ProductVersion]" Name="AI_ExePath" Value="[AI_SETUPEXEPATH]" Component_="AI_ExePath"/>
+    <ROW Registry="Comments" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Comments" Value="[ARPCOMMENTS]" Component_="AI_CustomARPName"/>
+    <ROW Registry="Contact" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Contact" Value="[ARPCONTACT]" Component_="AI_CustomARPName"/>
+    <ROW Registry="DisplayIcon" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayIcon" Value="[ARP_ICON_PATH]" Component_="AI_CustomARPName"/>
+    <ROW Registry="DisplayName" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayName" Value="[AI_PRODUCTNAME_ARP]" Component_="AI_CustomARPName"/>
+    <ROW Registry="DisplayVersion" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="DisplayVersion" Value="[ProductVersion]" Component_="AI_CustomARPName"/>
+    <ROW Registry="HelpLink" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpLink" Value="[ARPHELPLINK]" Component_="AI_CustomARPName"/>
+    <ROW Registry="HelpTelephone" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="HelpTelephone" Value="[ARPHELPTELEPHONE]" Component_="AI_CustomARPName"/>
+    <ROW Registry="InstallLocation" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="InstallLocation" Value="[APPDIR]" Component_="AI_CustomARPName"/>
+    <ROW Registry="ModifyPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="ModifyPath" Value="[AI_UNINSTALLER] /I [ProductCode]" Component_="AI_CustomARPName"/>
+    <ROW Registry="NoModify" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoModify" Value="#1" Component_="AI_DisableModify"/>
+    <ROW Registry="NoRepair" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="NoRepair" Value="#1" Component_="AI_CustomARPName"/>
+    <ROW Registry="Path" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Path" Value="[APPDIR]" Component_="ProductInformation"/>
+    <ROW Registry="Publisher" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="Publisher" Value="[Manufacturer]" Component_="AI_CustomARPName"/>
+    <ROW Registry="URLInfoAbout" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLInfoAbout" Value="[ARPURLINFOABOUT]" Component_="AI_CustomARPName"/>
+    <ROW Registry="URLUpdateInfo" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="URLUpdateInfo" Value="[ARPURLUPDATEINFO]" Component_="AI_CustomARPName"/>
+    <ROW Registry="UninstallPath" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallPath" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+    <ROW Registry="UninstallString" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="UninstallString" Value="[AI_UNINSTALLER] /x [ProductCode] AI_UNINSTALLER_CTP=1" Component_="AI_CustomARPName"/>
+    <ROW Registry="Version" Root="-1" Key="Software\[Manufacturer]\[ProductName]" Name="Version" Value="[ProductVersion]" Component_="ProductInformation"/>
+    <ROW Registry="VersionMajor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMajor" Value="#0" Component_="AI_CustomARPName"/>
+    <ROW Registry="VersionMinor" Root="-1" Key="Software\Microsoft\Windows\CurrentVersion\Uninstall\[ProductName] [ProductVersion]" Name="VersionMinor" Value="#7" Component_="AI_CustomARPName"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiServCtrlComponent">
+    <ROW ServiceControl="zerotierone_x64.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x64.exe"/>
+    <ROW ServiceControl="zerotierone_x86.exe" Name="ZeroTierOneService" Event="163" Wait="1" Component_="zerotierone_x86.exe"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiServInstComponent">
+    <ROW ServiceInstall="zerotierone_x64.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x64.exe" Description="Ethernet Virtualization Service"/>
+    <ROW ServiceInstall="zerotierone_x86.exe" Name="ZeroTierOneService" DisplayName="ZeroTier One" ServiceType="16" StartType="2" ErrorControl="32769" Component_="zerotierone_x86.exe" Description="Ethernet Virtualization Service"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiShortsComponent">
+    <ROW Shortcut="ZeroTierOne" Directory_="ProgramMenuFolder" Name="ZEROTI~1|ZeroTier One" Component_="ZeroTierOne.exe" Target="[#ZeroTierOne.exe]" Description="Ethernet Virtualization Control Panel" Hotkey="0" Icon_="ZeroTierIcon.exe" IconIndex="0" ShowCmd="1" WkDir="APPDIR"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiThemeComponent">
+    <ATTRIBUTE name="UsedTheme" value="classic"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.MsiUpgradeComponent">
+    <ROW UpgradeCode="[|UpgradeCode]" VersionMin="0.0.1" VersionMax="[|ProductVersion]" Attributes="257" ActionProperty="OLDPRODUCTS"/>
+    <ROW UpgradeCode="[|UpgradeCode]" VersionMin="[|ProductVersion]" Attributes="2" ActionProperty="AI_NEWERPRODUCTFOUND"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.PreReqSearchComponent">
+    <ROW SearchKey="UpgradeCode" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="2" Property="ZTTAP300_X86_INSTALLED"/>
+    <ROW SearchKey="_" SearchType="4" SearchString="{88AA80DE-14CA-4443-B024-6EC13F3EDDAD}" Order="1" Property="ZTTAP300_X64_INSTALLED"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.SoftwareIdentificationComponent">
+    <ATTRIBUTE name="LocalFile" value="regid.199509.com.example_ProductName.swidtag"/>
+    <ATTRIBUTE name="SystemFile" value="regid.199509.com.example_ProductName.swidtag_1"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateComponent">
+    <ROW Name="Append/Create" TxtUpdateSet="zerotiercli.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -q %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -q %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+    <ROW Name="Replace" TxtUpdateSet="zerotiercli.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+    <ROW Name="Append/Create" TxtUpdateSet="zerotiercli1.bat" FindPattern="@ECHO OFF&#13;&#10;if [\[][#zerotierone_x64.exe][\]] == [\[][\]] (&#13;&#10;&#9;[#zerotierone_x86.exe] -i %*&#13;&#10;) else (&#13;&#10;&#9;[#zerotierone_x64.exe] -i %*&#13;&#10;)&#13;&#10;" Options="160" Order="0" FileEncoding="0"/>
+    <ROW Name="Replace" TxtUpdateSet="zerotiercli1.bat" FindPattern="YourFindText" ReplacePattern="YourReplaceText" Options="2" Order="1" FileEncoding="-1"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.TxtUpdateSetComponent">
+    <ROW Key="zerotiercli.bat" Component="regid.201001.com.zerotier" FileName="zerotier-cli.bat" Directory="APPDIR" Options="17"/>
+    <ROW Key="zerotiercli1.bat" Component="regid.201001.com.zerotier" FileName="zerotier-idtool.bat" Directory="APPDIR" Options="17"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.XmlAttributeComponent">
+    <ROW XmlAttribute="xmlnsds" XmlElement="swidsoftware_identification_tag" Name="xmlns:ds" Flags="14" Order="0" Value="http://www.w3.org/2000/09/xmldsig#"/>
+    <ROW XmlAttribute="xmlnsswid" XmlElement="swidsoftware_identification_tag" Name="xmlns:swid" Flags="14" Order="1" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd"/>
+    <ROW XmlAttribute="xmlnsxsi" XmlElement="swidsoftware_identification_tag" Name="xmlns:xsi" Flags="14" Order="2" Value="http://www.w3.org/2001/XMLSchema-instance"/>
+    <ROW XmlAttribute="xsischemaLocation" XmlElement="swidsoftware_identification_tag" Name="xsi:schemaLocation" Flags="14" Order="3" Value="http://standards.iso.org/iso/19770/-2/2008/schema.xsd software_identification_tag.xsd"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.XmlElementComponent">
+    <ROW XmlElement="swidbuild" ParentElement="swidnumeric" Name="swid:build" Condition="1" Order="2" Flags="14" Text="6"/>
+    <ROW XmlElement="swidentitlement_required_indicator" ParentElement="swidsoftware_identification_tag" Name="swid:entitlement_required_indicator" Condition="1" Order="0" Flags="14" Text="false"/>
+    <ROW XmlElement="swidmajor" ParentElement="swidnumeric" Name="swid:major" Condition="1" Order="0" Flags="14" Text="1"/>
+    <ROW XmlElement="swidminor" ParentElement="swidnumeric" Name="swid:minor" Condition="1" Order="1" Flags="14" Text="4"/>
+    <ROW XmlElement="swidname" ParentElement="swidproduct_version" Name="swid:name" Condition="1" Order="0" Flags="14" Text="[ProductVersion]"/>
+    <ROW XmlElement="swidname_1" ParentElement="swidsoftware_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+    <ROW XmlElement="swidname_2" ParentElement="swidsoftware_licensor" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+    <ROW XmlElement="swidname_3" ParentElement="swidtag_creator" Name="swid:name" Condition="1" Order="0" Flags="14" Text="ZeroTier, Inc."/>
+    <ROW XmlElement="swidnumeric" ParentElement="swidproduct_version" Name="swid:numeric" Condition="1" Order="1" Flags="14"/>
+    <ROW XmlElement="swidproduct_title" ParentElement="swidsoftware_identification_tag" Name="swid:product_title" Condition="1" Order="1" Flags="14" Text="[ProductName]"/>
+    <ROW XmlElement="swidproduct_version" ParentElement="swidsoftware_identification_tag" Name="swid:product_version" Condition="1" Order="2" Flags="14"/>
+    <ROW XmlElement="swidregid" ParentElement="swidsoftware_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+    <ROW XmlElement="swidregid_1" ParentElement="swidsoftware_licensor" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+    <ROW XmlElement="swidregid_2" ParentElement="swidtag_creator" Name="swid:regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+    <ROW XmlElement="swidreview" ParentElement="swidnumeric" Name="swid:review" Condition="1" Order="3" Flags="14" Text="0"/>
+    <ROW XmlElement="swidsoftware_creator" ParentElement="swidsoftware_identification_tag" Name="swid:software_creator" Condition="1" Order="3" Flags="14"/>
+    <ROW XmlElement="swidsoftware_id" ParentElement="swidsoftware_identification_tag" Name="swid:software_id" Condition="1" Order="5" Flags="14"/>
+    <ROW XmlElement="swidsoftware_identification_tag" Name="swid:software_identification_tag" Condition="1" Order="0" Flags="14"/>
+    <ROW XmlElement="swidsoftware_licensor" ParentElement="swidsoftware_identification_tag" Name="swid:software_licensor" Condition="1" Order="4" Flags="14"/>
+    <ROW XmlElement="swidtag_creator" ParentElement="swidsoftware_identification_tag" Name="swid:tag_creator" Condition="1" Order="6" Flags="14"/>
+    <ROW XmlElement="swidtag_creator_regid" ParentElement="swidsoftware_id" Name="swid:tag_creator_regid" Condition="1" Order="1" Flags="14" Text="regid.2010-01.com.zerotier"/>
+    <ROW XmlElement="swidunique_id" ParentElement="swidsoftware_id" Name="swid:unique_id" Condition="1" Order="0" Flags="14" Text="ZeroTierOne"/>
+  </COMPONENT>
+  <COMPONENT cid="caphyon.advinst.msicomp.XmlFileComponent">
+    <ROW XmlFile="regid.199509.com.example_ProductName.swidtag" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="APPDIR" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+    <ROW XmlFile="regid.199509.com.example_ProductName.swidtag_1" FileName="REGID2~1.SWI|regid.2010-01.com.zerotier_ZeroTierOne.swidtag" DirProperty="regid.201001.com.zerotier_Dir" Component="ProductInformation" RootElement="swidsoftware_identification_tag" Flags="25" Version="1.0" Encoding="UTF-8" IndentUnits="2"/>
+  </COMPONENT>
+</DOCUMENT>

+ 35 - 3
main.cpp

@@ -11,9 +11,41 @@
  */
 /****/
 
+#if !defined(_WIN32) && !defined(_WIN64)
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#endif
+
 #include "zerotier_cgo.h"
 
-int main() {
-    ZeroTierMain();
-    return 0;
+int main(int argc,char **argv)
+{
+	// Fork into background if run with 'service -d'. This is best done prior
+	// to launching the Go code, since Go likes to start thread pools and stuff
+	// that don't play nice with fork. This is obviously unsupported on Windows.
+#if !defined(_WIN32) && !defined(_WIN64)
+	for(int i=1;i<argc;) {
+		if (strcmp(argv[i++], "service") == 0) {
+			for(;i<argc;) {
+				if (strcmp(argv[i++], "-d") == 0) {
+					long p = (long)fork();
+					if (p < 0) {
+						fprintf(stderr,"FATAL: fork() failed!\n");
+						return -1;
+					} else if (p > 0) {
+						return 0;
+					}
+					break;
+				}
+			}
+			break;
+		}
+	}
+#endif
+
+	ZeroTierMain();
+
+	return 0;
 }

+ 2 - 2
pkg/zerotier/node.go

@@ -47,7 +47,7 @@ var nullLogger = log.New(ioutil.Discard, "", 0)
 
 const (
 	NetworkIDStringLength = 16
-	NEtworkIDLength       = 8
+	NetworkIDLength       = 8
 	AddressStringLength   = 10
 	AddressLength         = 5
 
@@ -883,7 +883,7 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Poi
 	*((*uintptr)(dataP)) = 0
 	tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id)))
 	if found && len(tmp) > 0 {
-		cData := C.malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function
+		cData := C.ZT_malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function
 		if uintptr(cData) == 0 {
 			return -1
 		}

+ 3 - 0
serviceiocore/GoGlue.cpp

@@ -724,3 +724,6 @@ extern "C" int ZT_isTemporaryV6Address(const char *ifname, const struct sockaddr
 	return 0;
 #endif
 }
+
+extern "C" void *ZT_malloc(unsigned long s)
+{ return (void *)malloc((size_t)s); }

+ 2 - 0
serviceiocore/GoGlue.h

@@ -50,6 +50,8 @@ void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
 
 int ZT_isTemporaryV6Address(const char *ifname,const struct sockaddr_storage *a);
 
+void *ZT_malloc(unsigned long s);
+
 #ifdef __cplusplus
 }
 #endif