Browse Source

Merge dev, clean up old files

Adam Ierymenko 5 years ago
parent
commit
14fab11081
12 changed files with 0 additions and 4330 deletions
  1. 0 461
      attic/Binder.hpp
  2. 0 287
      attic/Http.cpp
  3. 0 182
      attic/Http.hpp
  4. 0 1177
      attic/Phy.hpp
  5. 0 14
      attic/PortMapper-libnatpmp.c
  6. 0 41
      attic/PortMapper-miniupnpc.c
  7. 0 334
      attic/PortMapper.cpp
  8. 0 62
      attic/PortMapper.hpp
  9. 0 182
      attic/Root.hpp
  10. 0 18
      attic/linux-old-glibc-compat.c
  11. 0 30
      attic/listaddrinfo.go
  12. 0 1542
      attic/one.cpp

+ 0 - 461
attic/Binder.hpp

@@ -1,461 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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_BINDER_HPP
-#define ZT_BINDER_HPP
-
-#include "../node/Constants.hpp"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef __WINDOWS__
-#include <WinSock2.h>
-#include <Windows.h>
-#include <ShlObj.h>
-#include <netioapi.h>
-#include <iphlpapi.h>
-#else
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <ifaddrs.h>
-#ifdef __LINUX__
-#include <sys/ioctl.h>
-#include <net/if.h>
-#endif
-#endif
-
-#include <string>
-#include <vector>
-#include <algorithm>
-#include <utility>
-#include <map>
-#include <set>
-#include <atomic>
-
-#include "../node/InetAddress.hpp"
-#include "../node/Mutex.hpp"
-#include "../node/Utils.hpp"
-
-#include "Phy.hpp"
-#include "OSUtils.hpp"
-
-#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__))
-#define ZT_UDP_DESIRED_BUF_SIZE 1048576
-#else
-#define ZT_UDP_DESIRED_BUF_SIZE 131072
-#endif
-
-// Period between refreshes of bindings
-#define ZT_BINDER_REFRESH_PERIOD 30000
-
-// Max number of bindings
-#define ZT_BINDER_MAX_BINDINGS 256
-
-namespace ZeroTier {
-
-/**
- * Enumerates local devices and binds to all potential ZeroTier path endpoints
- *
- * This replaces binding to wildcard (0.0.0.0 and ::0) with explicit binding
- * as part of the path to default gateway support. Under the hood it uses
- * different queries on different OSes to enumerate devices, and also exposes
- * device enumeration and endpoint IP data for use elsewhere.
- *
- * On OSes that do not support local port enumeration or where this is not
- * meaningful, this degrades to binding to wildcard.
- */
-class Binder
-{
-private:
-	struct _Binding
-	{
-		_Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {}
-		PhySocket *udpSock;
-		PhySocket *tcpListenSock;
-		InetAddress address;
-	};
-
-public:
-	Binder() : _bindingCount(0) {}
-
-	/**
-	 * Close all bound ports, should be called on shutdown
-	 *
-	 * @param phy Physical interface
-	 */
-	template<typename PHY_HANDLER_TYPE>
-	void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
-	{
-		Mutex::Lock _l(_lock);
-		for(unsigned int b=0,c=_bindingCount;b<c;++b) {
-			phy.close(_bindings[b].udpSock,false);
-			phy.close(_bindings[b].tcpListenSock,false);
-		}
-		_bindingCount = 0;
-	}
-
-	/**
-	 * Scan local devices and addresses and rebind TCP and UDP
-	 *
-	 * This should be called after wake from sleep, on detected network device
-	 * changes, on startup, or periodically (e.g. every 30-60s).
-	 *
-	 * @param phy Physical interface
-	 * @param ports Ports to bind on all interfaces
-	 * @param portCount Number of ports
-	 * @param explicitBind If present, override interface IP detection and bind to these (if possible)
-	 * @param ifChecker Interface checker function to see if an interface should be used
-	 * @tparam PHY_HANDLER_TYPE Type for Phy<> template
-	 * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
-	 */
-	template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
-	void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,const std::vector<InetAddress> explicitBind,INTERFACE_CHECKER &ifChecker)
-	{
-		std::map<InetAddress,std::string> localIfAddrs;
-		PhySocket *udps,*tcps;
-		Mutex::Lock _l(_lock);
-		bool interfacesEnumerated = true;
-
-		if (explicitBind.empty()) {
-#ifdef __WINDOWS__
-
-			char aabuf[32768];
-			ULONG aalen = sizeof(aabuf);
-			if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
-				PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
-				while (a) {
-					PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
-					while (ua) {
-						InetAddress ip(ua->Address.lpSockaddr);
-						if (ifChecker.shouldBindInterface("",ip)) {
-							switch(ip.ipScope()) {
-								default: break;
-								case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-								case InetAddress::IP_SCOPE_GLOBAL:
-								case InetAddress::IP_SCOPE_SHARED:
-								case InetAddress::IP_SCOPE_PRIVATE:
-									for(int x=0;x<(int)portCount;++x) {
-										ip.setPort(ports[x]);
-										localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
-									}
-									break;
-							}
-						}
-						ua = ua->Next;
-					}
-					a = a->Next;
-				}
-			}
-			else {
-				interfacesEnumerated = false;
-			}
-
-#else // not __WINDOWS__
-
-			/* On Linux we use an alternative method if available since getifaddrs()
-			 * gets very slow when there are lots of network namespaces. This won't
-			 * work unless /proc/PID/net/if_inet6 exists and it may not on some
-			 * embedded systems, so revert to getifaddrs() there. */
-
-#ifdef __LINUX__
-			char fn[256],tmp[256];
-			std::set<std::string> ifnames;
-			const unsigned long pid = (unsigned long)getpid();
-
-			// Get all device names
-			OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid);
-			FILE *procf = fopen(fn,"r");
-			if (procf) {
-				while (fgets(tmp,sizeof(tmp),procf)) {
-					tmp[255] = 0;
-					char *saveptr = (char *)0;
-					for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) {
-						if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0))
-							ifnames.insert(f);
-						break; // we only want the first field
-					}
-				}
-				fclose(procf);
-			}
-			else {
-				interfacesEnumerated = false;
-			}
-
-			// Get IPv6 addresses (and any device names we don't already know)
-			OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid);
-			procf = fopen(fn,"r");
-			if (procf) {
-				while (fgets(tmp,sizeof(tmp),procf)) {
-					tmp[255] = 0;
-					char *saveptr = (char *)0;
-					unsigned char ipbits[16];
-					memset(ipbits,0,sizeof(ipbits));
-					char *devname = (char *)0;
-					int n = 0;
-					for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) {
-						switch(n++) {
-							case 0: // IP in hex
-								Utils::unhex(f,32,ipbits,16);
-								break;
-							case 5: // device name
-								devname = f;
-								break;
-						}
-					}
-					if (devname) {
-						ifnames.insert(devname);
-						InetAddress ip(ipbits,16,0);
-						if (ifChecker.shouldBindInterface(devname,ip)) {
-							switch(ip.ipScope()) {
-								default: break;
-								case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-								case InetAddress::IP_SCOPE_GLOBAL:
-								case InetAddress::IP_SCOPE_SHARED:
-								case InetAddress::IP_SCOPE_PRIVATE:
-									for(int x=0;x<(int)portCount;++x) {
-										ip.setPort(ports[x]);
-										localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
-									}
-									break;
-							}
-						}
-					}
-				}
-				fclose(procf);
-			}
-
-			// Get IPv4 addresses for each device
-			if (ifnames.size() > 0) {
-				const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0);
-				struct ifconf configuration;
-				configuration.ifc_len = 0;
-				configuration.ifc_buf = nullptr;
-
-				if (controlfd < 0) goto ip4_address_error;
-				if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
-				configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
-				if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
-
-				for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
-					struct ifreq& request = configuration.ifc_req[i];
-					struct sockaddr* addr = &request.ifr_ifru.ifru_addr;
-					if (addr->sa_family != AF_INET) continue;
-					std::string ifname = request.ifr_ifrn.ifrn_name;
-					// name can either be just interface name or interface name followed by ':' and arbitrary label
-					if (ifname.find(':') != std::string::npos)
-						ifname = ifname.substr(0, ifname.find(':'));
-
-					InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
-					if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
-						switch(ip.ipScope()) {
-						default: break;
-						case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-						case InetAddress::IP_SCOPE_GLOBAL:
-						case InetAddress::IP_SCOPE_SHARED:
-						case InetAddress::IP_SCOPE_PRIVATE:
-							for(int x=0;x<(int)portCount;++x) {
-								ip.setPort(ports[x]);
-								localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname));
-							}
-							break;
-						}
-					}
-				}
-
-			ip4_address_error:
-				free(configuration.ifc_buf);
-				if (controlfd > 0) close(controlfd);
-			}
-
-			const bool gotViaProc = (localIfAddrs.size() > 0);
-#else
-			const bool gotViaProc = false;
-#endif
-#if !defined(ZT_SDK) || !defined(__ANDROID__) // getifaddrs() freeifaddrs() not available on Android
-			if (!gotViaProc) {
-				struct ifaddrs *ifatbl = (struct ifaddrs *)0;
-				struct ifaddrs *ifa;
-				if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
-					ifa = ifatbl;
-					while (ifa) {
-						if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
-							InetAddress ip = *(ifa->ifa_addr);
-							if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) {
-								switch(ip.ipScope()) {
-									default: break;
-									case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
-									case InetAddress::IP_SCOPE_GLOBAL:
-									case InetAddress::IP_SCOPE_SHARED:
-									case InetAddress::IP_SCOPE_PRIVATE:
-										for(int x=0;x<(int)portCount;++x) {
-											ip.setPort(ports[x]);
-											localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
-										}
-										break;
-								}
-							}
-						}
-						ifa = ifa->ifa_next;
-					}
-					freeifaddrs(ifatbl);
-				}
-				else {
-					interfacesEnumerated = false;
-				}
-			}
-#endif
-
-#endif
-		} else {
-			for(std::vector<InetAddress>::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i)
-				localIfAddrs.insert(std::pair<InetAddress,std::string>(*i,std::string()));
-		}
-
-		// Default to binding to wildcard if we can't enumerate addresses
-		if (!interfacesEnumerated && localIfAddrs.empty()) {
-			for(int x=0;x<(int)portCount;++x) {
-				localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string()));
-				localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string()));
-			}
-		}
-
-		const unsigned int oldBindingCount = _bindingCount;
-		_bindingCount = 0;
-
-		// Save bindings that are still valid, close those that are not
-		for(unsigned int b=0;b<oldBindingCount;++b) {
-			if (localIfAddrs.find(_bindings[b].address) != localIfAddrs.end()) {
-				if (_bindingCount != b)
-					_bindings[(unsigned int)_bindingCount] = _bindings[b];
-				++_bindingCount;
-			} else {
-				PhySocket *const udps = _bindings[b].udpSock;
-				PhySocket *const tcps = _bindings[b].tcpListenSock;
-				_bindings[b].udpSock = (PhySocket *)0;
-				_bindings[b].tcpListenSock = (PhySocket *)0;
-				phy.close(udps,false);
-				phy.close(tcps,false);
-			}
-		}
-
-		// Create new bindings for those not already bound
-		for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) {
-			unsigned int bi = 0;
-			while (bi != _bindingCount) {
-				if (_bindings[bi].address == ii->first)
-					break;
-				++bi;
-			}
-			if (bi == _bindingCount) {
-				udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
-				tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0);
-				if ((udps)&&(tcps)) {
-#ifdef __LINUX__
-					// Bind Linux sockets to their device so routes that we manage do not override physical routes (wish all platforms had this!)
-					if (ii->second.length() > 0) {
-						char tmp[256];
-						Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
-						int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
-						if (fd >= 0)
-							setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
-						fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps);
-						if (fd >= 0)
-							setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
-					}
-#endif // __LINUX__
-					if (_bindingCount < ZT_BINDER_MAX_BINDINGS) {
-						_bindings[_bindingCount].udpSock = udps;
-						_bindings[_bindingCount].tcpListenSock = tcps;
-						_bindings[_bindingCount].address = ii->first;
-						phy.setIfName(udps,(char*)ii->second.c_str(),(int)ii->second.length());
-						++_bindingCount;
-					}
-				} else {
-					phy.close(udps,false);
-					phy.close(tcps,false);
-				}
-			}
-		}
-	}
-
-	/**
-	 * @return All currently bound local interface addresses
-	 */
-	inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const
-	{
-		std::vector<InetAddress> aa;
-		Mutex::Lock _l(_lock);
-		for(unsigned int b=0,c=_bindingCount;b<c;++b)
-			aa.push_back(_bindings[b].address);
-		return aa;
-	}
-
-	/**
-	 * Send from all bound UDP sockets
-	 */
-	template<typename PHY_HANDLER_TYPE>
-	inline bool udpSendAll(Phy<PHY_HANDLER_TYPE> &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
-	{
-		bool r = false;
-		Mutex::Lock _l(_lock);
-		for(unsigned int b=0,c=_bindingCount;b<c;++b) {
-			if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,ttl);
-			if (phy.udpSend(_bindings[b].udpSock,(const struct sockaddr *)addr,data,len)) r = true;
-			if (ttl) phy.setIp4UdpTtl(_bindings[b].udpSock,255);
-		}
-		return r;
-	}
-
-	/**
-	 * @param addr Address to check
-	 * @return True if this is a bound local interface address
-	 */
-	inline bool isBoundLocalInterfaceAddress(const InetAddress &addr) const
-	{
-		Mutex::Lock _l(_lock);
-		for(unsigned int b=0;b<_bindingCount;++b) {
-			if (_bindings[b].address == addr)
-				return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Quickly check that a UDP socket is valid
-	 *
-	 * @param udpSock UDP socket to check
-	 * @return True if socket is currently bound/allocated
-	 */
-	inline bool isUdpSocketValid(PhySocket *const udpSock)
-	{
-		for(unsigned int b=0,c=_bindingCount;b<c;++b) {
-			if (_bindings[b].udpSock == udpSock)
-				return (b < _bindingCount); // double check atomic which may have changed
-		}
-		return false;
-	}
-
-private:
-	_Binding _bindings[ZT_BINDER_MAX_BINDINGS];
-	std::atomic<unsigned int> _bindingCount;
-	Mutex _lock;
-};
-
-} // namespace ZeroTier
-
-#endif

+ 0 - 287
attic/Http.cpp

@@ -1,287 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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 <stdio.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "Http.hpp"
-#include "Phy.hpp"
-#include "OSUtils.hpp"
-#include "../node/Constants.hpp"
-#include "../node/Utils.hpp"
-
-#ifdef ZT_USE_SYSTEM_HTTP_PARSER
-#include <http_parser.h>
-#else
-#include "../ext/http-parser/http_parser.h"
-#endif
-
-namespace ZeroTier {
-
-namespace {
-
-static int ShttpOnMessageBegin(http_parser *parser);
-static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
-#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
-static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length);
-#else
-static int ShttpOnStatus(http_parser *parser);
-#endif
-static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length);
-static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length);
-static int ShttpOnHeadersComplete(http_parser *parser);
-static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length);
-static int ShttpOnMessageComplete(http_parser *parser);
-
-#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 1)
-static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
-	ShttpOnMessageBegin,
-	ShttpOnUrl,
-	ShttpOnStatus,
-	ShttpOnHeaderField,
-	ShttpOnValue,
-	ShttpOnHeadersComplete,
-	ShttpOnBody,
-	ShttpOnMessageComplete
-};
-#else
-static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
-	ShttpOnMessageBegin,
-	ShttpOnUrl,
-	ShttpOnHeaderField,
-	ShttpOnValue,
-	ShttpOnHeadersComplete,
-	ShttpOnBody,
-	ShttpOnMessageComplete
-};
-#endif
-
-struct HttpPhyHandler
-{
-	// not used
-	inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) {}
-	inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {}
-
-	inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
-	{
-		if (success) {
-			phy->setNotifyWritable(sock,true);
-		} else {
-			*responseBody = "connection failed";
-			error = true;
-			done = true;
-		}
-	}
-
-	inline void phyOnTcpClose(PhySocket *sock,void **uptr)
-	{
-		done = true;
-	}
-
-	inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
-	{
-		lastActivity = OSUtils::now();
-		http_parser_execute(&parser,&HTTP_PARSER_SETTINGS,(const char *)data,len);
-		if ((parser.upgrade)||(parser.http_errno != HPE_OK))
-			phy->close(sock);
-	}
-
-	inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
-	{
-		if (writePtr < (unsigned long)writeBuf.length()) {
-			long n = phy->streamSend(sock,writeBuf.data() + writePtr,(unsigned long)writeBuf.length() - writePtr,true);
-			if (n > 0)
-				writePtr += n;
-		}
-		if (writePtr >= (unsigned long)writeBuf.length())
-			phy->setNotifyWritable(sock,false);
-	}
-
-	inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {}
-#ifdef __UNIX_LIKE__
-	inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
-	inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
-	inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
-	inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
-#endif // __UNIX_LIKE__
-
-	http_parser parser;
-	std::string currentHeaderField;
-	std::string currentHeaderValue;
-	unsigned long messageSize;
-	unsigned long writePtr;
-	uint64_t lastActivity;
-	std::string writeBuf;
-
-	unsigned long maxResponseSize;
-	std::map<std::string,std::string> *responseHeaders;
-	std::string *responseBody;
-	bool error;
-	bool done;
-
-	Phy<HttpPhyHandler *> *phy;
-	PhySocket *sock;
-};
-
-static int ShttpOnMessageBegin(http_parser *parser)
-{
-	return 0;
-}
-static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
-{
-	return 0;
-}
-#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2)
-static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
-#else
-static int ShttpOnStatus(http_parser *parser)
-#endif
-{
-	/*
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	hh->messageSize += (unsigned long)length;
-	if (hh->messageSize > hh->maxResponseSize)
-		return -1;
-	*/
-	return 0;
-}
-static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
-{
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	hh->messageSize += (unsigned long)length;
-	if (hh->messageSize > hh->maxResponseSize)
-		return -1;
-	if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) {
-		(*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
-		hh->currentHeaderField = "";
-		hh->currentHeaderValue = "";
-	}
-	for(size_t i=0;i<length;++i)
-		hh->currentHeaderField.push_back(OSUtils::toLower(ptr[i]));
-	return 0;
-}
-static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length)
-{
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	hh->messageSize += (unsigned long)length;
-	if (hh->messageSize > hh->maxResponseSize)
-		return -1;
-	hh->currentHeaderValue.append(ptr,length);
-	return 0;
-}
-static int ShttpOnHeadersComplete(http_parser *parser)
-{
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length()))
-		(*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
-	return 0;
-}
-static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
-{
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	hh->messageSize += (unsigned long)length;
-	if (hh->messageSize > hh->maxResponseSize)
-		return -1;
-	hh->responseBody->append(ptr,length);
-	return 0;
-}
-static int ShttpOnMessageComplete(http_parser *parser)
-{
-	HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
-	hh->phy->close(hh->sock);
-	return 0;
-}
-
-} // anonymous namespace
-
-unsigned int Http::_do(
-	const char *method,
-	unsigned long maxResponseSize,
-	unsigned long timeout,
-	const struct sockaddr *remoteAddress,
-	const char *path,
-	const std::map<std::string,std::string> &requestHeaders,
-	const void *requestBody,
-	unsigned long requestBodyLength,
-	std::map<std::string,std::string> &responseHeaders,
-	std::string &responseBody)
-{
-	try {
-		responseHeaders.clear();
-		responseBody = "";
-
-		HttpPhyHandler handler;
-
-		http_parser_init(&(handler.parser),HTTP_RESPONSE);
-		handler.parser.data = (void *)&handler;
-		handler.messageSize = 0;
-		handler.writePtr = 0;
-		handler.lastActivity = OSUtils::now();
-
-		try {
-			char tmp[1024];
-			OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path);
-			handler.writeBuf.append(tmp);
-			for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) {
-				OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str());
-				handler.writeBuf.append(tmp);
-			}
-			handler.writeBuf.append("\r\n");
-			if ((requestBody)&&(requestBodyLength))
-				handler.writeBuf.append((const char *)requestBody,requestBodyLength);
-		} catch ( ... ) {
-			responseBody = "request too large";
-			return 0;
-		}
-
-		if (maxResponseSize) {
-			handler.maxResponseSize = maxResponseSize;
-		} else {
-			handler.maxResponseSize = 2147483647;
-		}
-		handler.responseHeaders = &responseHeaders;
-		handler.responseBody = &responseBody;
-		handler.error = false;
-		handler.done = false;
-
-		Phy<HttpPhyHandler *> phy(&handler,true,true);
-
-		bool instantConnect = false;
-		handler.phy = &phy;
-		handler.sock = phy.tcpConnect((const struct sockaddr *)remoteAddress,instantConnect,(void *)0,true);
-		if (!handler.sock) {
-			responseBody = "connection failed (2)";
-			return 0;
-		}
-
-		while (!handler.done) {
-			phy.poll(timeout / 2);
-			if ((timeout)&&((unsigned long)(OSUtils::now() - handler.lastActivity) > timeout)) {
-				phy.close(handler.sock);
-				responseBody = "timed out";
-				return 0;
-			}
-		}
-
-		return ((handler.error) ? 0 : ((handler.parser.http_errno != HPE_OK) ? 0 : handler.parser.status_code));
-	} catch (std::exception &exc) {
-		responseBody = exc.what();
-		return 0;
-	} catch ( ... ) {
-		responseBody = "unknown exception";
-		return 0;
-	}
-}
-
-} // namespace ZeroTier

+ 0 - 182
attic/Http.hpp

@@ -1,182 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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_HTTP_HPP
-#define ZT_HTTP_HPP
-
-#include <string>
-#include <map>
-#include <stdexcept>
-
-#if defined(_WIN32) || defined(_WIN64)
-#include <WinSock2.h>
-#include <WS2tcpip.h>
-#include <Windows.h>
-#else
-#include <unistd.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#endif
-
-namespace ZeroTier {
-
-/**
- * Simple synchronous HTTP client used for updater and cli
- */
-class Http
-{
-public:
-	/**
-	 * Make HTTP GET request
-	 *
-	 * The caller must set all headers, including Host.
-	 *
-	 * @return HTTP status code or 0 on error (responseBody will contain error message)
-	 */
-	static inline unsigned int GET(
-		unsigned long maxResponseSize,
-		unsigned long timeout,
-		const struct sockaddr *remoteAddress,
-		const char *path,
-		const std::map<std::string,std::string> &requestHeaders,
-		std::map<std::string,std::string> &responseHeaders,
-		std::string &responseBody)
-	{
-		return _do(
-			"GET",
-			maxResponseSize,
-			timeout,
-			remoteAddress,
-			path,
-			requestHeaders,
-			(const void *)0,
-			0,
-			responseHeaders,
-			responseBody);
-	}
-
-	/**
-	 * Make HTTP DELETE request
-	 *
-	 * The caller must set all headers, including Host.
-	 *
-	 * @return HTTP status code or 0 on error (responseBody will contain error message)
-	 */
-	static inline unsigned int DEL(
-		unsigned long maxResponseSize,
-		unsigned long timeout,
-		const struct sockaddr *remoteAddress,
-		const char *path,
-		const std::map<std::string,std::string> &requestHeaders,
-		std::map<std::string,std::string> &responseHeaders,
-		std::string &responseBody)
-	{
-		return _do(
-			"DELETE",
-			maxResponseSize,
-			timeout,
-			remoteAddress,
-			path,
-			requestHeaders,
-			(const void *)0,
-			0,
-			responseHeaders,
-			responseBody);
-	}
-
-	/**
-	 * Make HTTP POST request
-	 *
-	 * It is the responsibility of the caller to set all headers. With POST, the
-	 * Content-Length and Content-Type headers must be set or the POST will not
-	 * work.
-	 *
-	 * @return HTTP status code or 0 on error (responseBody will contain error message)
-	 */
-	static inline unsigned int POST(
-		unsigned long maxResponseSize,
-		unsigned long timeout,
-		const struct sockaddr *remoteAddress,
-		const char *path,
-		const std::map<std::string,std::string> &requestHeaders,
-		const void *postData,
-		unsigned long postDataLength,
-		std::map<std::string,std::string> &responseHeaders,
-		std::string &responseBody)
-	{
-		return _do(
-			"POST",
-			maxResponseSize,
-			timeout,
-			remoteAddress,
-			path,
-			requestHeaders,
-			postData,
-			postDataLength,
-			responseHeaders,
-			responseBody);
-	}
-
-	/**
-	 * Make HTTP PUT request
-	 *
-	 * It is the responsibility of the caller to set all headers. With PUT, the
-	 * Content-Length and Content-Type headers must be set or the PUT will not
-	 * work.
-	 *
-	 * @return HTTP status code or 0 on error (responseBody will contain error message)
-	 */
-	static inline unsigned int PUT(
-		unsigned long maxResponseSize,
-		unsigned long timeout,
-		const struct sockaddr *remoteAddress,
-		const char *path,
-		const std::map<std::string,std::string> &requestHeaders,
-		const void *postData,
-		unsigned long postDataLength,
-		std::map<std::string,std::string> &responseHeaders,
-		std::string &responseBody)
-	{
-		return _do(
-			"PUT",
-			maxResponseSize,
-			timeout,
-			remoteAddress,
-			path,
-			requestHeaders,
-			postData,
-			postDataLength,
-			responseHeaders,
-			responseBody);
-	}
-
-private:
-	static unsigned int _do(
-		const char *method,
-		unsigned long maxResponseSize,
-		unsigned long timeout,
-		const struct sockaddr *remoteAddress,
-		const char *path,
-		const std::map<std::string,std::string> &requestHeaders,
-		const void *requestBody,
-		unsigned long requestBodyLength,
-		std::map<std::string,std::string> &responseHeaders,
-		std::string &responseBody);
-};
-
-} // namespace ZeroTier
-
-#endif

+ 0 - 1177
attic/Phy.hpp

@@ -1,1177 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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_PHY_HPP
-#define ZT_PHY_HPP
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <list>
-#include <stdexcept>
-
-#if defined(_WIN32) || defined(_WIN64)
-
-#include <WinSock2.h>
-#include <WS2tcpip.h>
-#include <Windows.h>
-
-#define ZT_PHY_SOCKFD_TYPE SOCKET
-#define ZT_PHY_SOCKFD_NULL (INVALID_SOCKET)
-#define ZT_PHY_SOCKFD_VALID(s) ((s) != INVALID_SOCKET)
-#define ZT_PHY_CLOSE_SOCKET(s) ::closesocket(s)
-#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE)
-#define ZT_PHY_MAX_INTERCEPTS ZT_PHY_MAX_SOCKETS
-#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage
-
-#else // not Windows
-
-#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <netinet/tcp.h>
-
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
-#ifndef IPV6_DONTFRAG
-#define IPV6_DONTFRAG 62
-#endif
-#endif
-
-#define ZT_PHY_SOCKFD_TYPE int
-#define ZT_PHY_SOCKFD_NULL (-1)
-#define ZT_PHY_SOCKFD_VALID(s) ((s) > -1)
-#define ZT_PHY_CLOSE_SOCKET(s) ::close(s)
-#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE)
-#define ZT_PHY_MAX_INTERCEPTS ZT_PHY_MAX_SOCKETS
-#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage
-
-#endif // Windows or not
-
-namespace ZeroTier {
-
-/**
- * Opaque socket type
- */
-typedef void PhySocket;
-
-/**
- * Simple templated non-blocking sockets implementation
- *
- * Yes there is boost::asio and libuv, but I like small binaries and I hate
- * build dependencies. Both drag in a whole bunch of pasta with them.
- *
- * This class is templated on a pointer to a handler class which must
- * implement the following functions:
- *
- * For all platforms:
- *
- * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
- * phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
- * phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
- * phyOnTcpClose(PhySocket *sock,void **uptr)
- * phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
- * phyOnTcpWritable(PhySocket *sock,void **uptr)
- * phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable)
- *
- * On Linux/OSX/Unix only (not required/used on Windows or elsewhere):
- *
- * phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN)
- * phyOnUnixClose(PhySocket *sock,void **uptr)
- * phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len)
- * phyOnUnixWritable(PhySocket *sock,void **uptr)
- *
- * These templates typically refer to function objects. Templates are used to
- * avoid the call overhead of indirection, which is surprisingly high for high
- * bandwidth applications pushing a lot of packets.
- *
- * The 'sock' pointer above is an opaque pointer to a socket. Each socket
- * has a 'uptr' user-settable/modifiable pointer associated with it, which
- * can be set on bind/connect calls and is passed as a void ** to permit
- * resetting at any time. The ACCEPT handler takes two sets of sock and
- * uptr: sockL and uptrL for the listen socket, and sockN and uptrN for
- * the new TCP connection socket that has just been created.
- *
- * Handlers are always called. On outgoing TCP connection, CONNECT is always
- * called on either success or failure followed by DATA and/or WRITABLE as
- * indicated. On socket close, handlers are called unless close() is told
- * explicitly not to call handlers. It is safe to close a socket within a
- * handler, and in that case close() can be told not to call handlers to
- * prevent recursion.
- *
- * This isn't thread-safe with the exception of whack(), which is safe to
- * call from another thread to abort poll().
- */
-template <typename HANDLER_PTR_TYPE>
-class Phy
-{
-private:
-	HANDLER_PTR_TYPE _handler;
-
-	enum PhySocketType
-	{
-		ZT_PHY_SOCKET_CLOSED = 0x00, // socket is closed, will be removed on next poll()
-		ZT_PHY_SOCKET_TCP_OUT_PENDING = 0x01,
-		ZT_PHY_SOCKET_TCP_OUT_CONNECTED = 0x02,
-		ZT_PHY_SOCKET_TCP_IN = 0x03,
-		ZT_PHY_SOCKET_TCP_LISTEN = 0x04,
-		ZT_PHY_SOCKET_UDP = 0x05,
-		ZT_PHY_SOCKET_FD = 0x06,
-		ZT_PHY_SOCKET_UNIX_IN = 0x07,
-		ZT_PHY_SOCKET_UNIX_LISTEN = 0x08
-	};
-
-	struct PhySocketImpl {
-		PhySocketImpl() { memset(ifname, 0, sizeof(ifname)); }
-		PhySocketType type;
-		ZT_PHY_SOCKFD_TYPE sock;
-		void *uptr; // user-settable pointer
-		ZT_PHY_SOCKADDR_STORAGE_TYPE saddr; // remote for TCP_OUT and TCP_IN, local for TCP_LISTEN, RAW, and UDP
-		char ifname[16];
-	};
-
-	std::list<PhySocketImpl> _socks;
-	fd_set _readfds;
-	fd_set _writefds;
-#if defined(_WIN32) || defined(_WIN64)
-	fd_set _exceptfds;
-#endif
-	long _nfds;
-
-	ZT_PHY_SOCKFD_TYPE _whackReceiveSocket;
-	ZT_PHY_SOCKFD_TYPE _whackSendSocket;
-
-	bool _noDelay;
-	bool _noCheck;
-
-public:
-	/**
-	 * @param handler Pointer of type HANDLER_PTR_TYPE to handler
-	 * @param noDelay If true, disable TCP NAGLE algorithm on TCP sockets
-	 * @param noCheck If true, attempt to set UDP SO_NO_CHECK option to disable sending checksums
-	 */
-	Phy(HANDLER_PTR_TYPE handler,bool noDelay,bool noCheck) :
-		_handler(handler)
-	{
-		FD_ZERO(&_readfds);
-		FD_ZERO(&_writefds);
-
-#if defined(_WIN32) || defined(_WIN64)
-		FD_ZERO(&_exceptfds);
-
-		SOCKET pipes[2];
-		{	// hack copied from StackOverflow, behaves a bit like pipe() on *nix systems
-			struct sockaddr_in inaddr;
-			struct sockaddr addr;
-			SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
-			if (lst == INVALID_SOCKET)
-				throw std::runtime_error("unable to create pipes for select() abort");
-			memset(&inaddr, 0, sizeof(inaddr));
-			memset(&addr, 0, sizeof(addr));
-			inaddr.sin_family = AF_INET;
-			inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-			inaddr.sin_port = 0;
-			int yes=1;
-			setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes));
-			bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr));
-			listen(lst,1);
-			int len=sizeof(inaddr);
-			getsockname(lst, &addr,&len);
-			pipes[0]=::socket(AF_INET, SOCK_STREAM,0);
-			if (pipes[0] == INVALID_SOCKET)
-				throw std::runtime_error("unable to create pipes for select() abort");
-			connect(pipes[0],&addr,len);
-			pipes[1]=accept(lst,0,0);
-			closesocket(lst);
-		}
-#else // not Windows
-		int pipes[2];
-		if (::pipe(pipes))
-			throw std::runtime_error("unable to create pipes for select() abort");
-#endif // Windows or not
-
-		_nfds = (pipes[0] > pipes[1]) ? (long)pipes[0] : (long)pipes[1];
-		_whackReceiveSocket = pipes[0];
-		_whackSendSocket = pipes[1];
-		_noDelay = noDelay;
-		_noCheck = noCheck;
-	}
-
-	~Phy()
-	{
-		for(typename std::list<PhySocketImpl>::const_iterator s(_socks.begin());s!=_socks.end();++s) {
-			if (s->type != ZT_PHY_SOCKET_CLOSED)
-				this->close((PhySocket *)&(*s),true);
-		}
-		ZT_PHY_CLOSE_SOCKET(_whackReceiveSocket);
-		ZT_PHY_CLOSE_SOCKET(_whackSendSocket);
-	}
-
-	/**
-	 * @param s Socket object
-	 * @return Underlying OS-type (usually int or long) file descriptor associated with object
-	 */
-	static inline ZT_PHY_SOCKFD_TYPE getDescriptor(PhySocket *s) throw() { return reinterpret_cast<PhySocketImpl *>(s)->sock; }
-
-	/**
-	 * @param s Socket object
-	 * @return Pointer to user object
-	 */
-	static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast<PhySocketImpl *>(s)->uptr); }
-
-	/**
-	 * @param s Socket object
-	 * @param nameBuf Buffer to store name of interface which this Socket object is bound to
-	 * @param buflen Length of buffer to copy name into
-	 */
-	static inline void getIfName(PhySocket *s, char *nameBuf, int buflen)
-	{
-		if (s) {
-			memcpy(nameBuf, reinterpret_cast<PhySocketImpl *>(s)->ifname, buflen);
-		}
-	}
-
-	/**
-	 * @param s Socket object
-	 * @param ifname Buffer containing name of interface that this Socket object is bound to
-	 * @param len Length of name of interface
-	 */
-	static inline void setIfName(PhySocket *s, char *ifname, int len)
-	{
-		if (s) {
-			memcpy(&(reinterpret_cast<PhySocketImpl *>(s)->ifname), ifname, len);
-		}
-	}
-
-	/**
-	 * Whether or not the socket object is in a closed state
-	 *
-	 * @param s Socket object
-	 * @return true if socket is closed, false if otherwise
-	 */
-	inline bool isClosed(PhySocket *s)
-	{
-		PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
-		return sws->type == ZT_PHY_SOCKET_CLOSED;
-	}
-
-	/**
-	 * Get state of socket object
-	 *
-	 * @param s Socket object
-	 * @return State of socket
-	 */
-	inline int getState(PhySocket *s)
-	{
-		PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
-		return sws->type;
-	}
-
-	/**
-	 * In the event that this socket is erased, we need a way to convey to the multipath logic
-	 * that this path is no longer valid.
-	 *
-	 * @param s Socket object
-	 * @return Whether the state of this socket is within an acceptable range of values
-	 */
-	inline bool isValidState(PhySocket *s)
-	{
-		if (s) {
-			PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
-			return sws->type >= ZT_PHY_SOCKET_CLOSED && sws->type <= ZT_PHY_SOCKET_UNIX_LISTEN;
-		}
-		return false;
-	}
-
-	/**
-	 * Cause poll() to stop waiting immediately
-	 *
-	 * This can be used to reset the polling loop after changes that require
-	 * attention, or to shut down a background thread that is waiting, etc.
-	 */
-	inline void whack()
-	{
-#if defined(_WIN32) || defined(_WIN64)
-		::send(_whackSendSocket,(const char *)this,1,0);
-#else
-		(void)(::write(_whackSendSocket,(PhySocket *)this,1));
-#endif
-	}
-
-	/**
-	 * @return Number of open sockets
-	 */
-	inline unsigned long count() const throw() { return _socks.size(); }
-
-	/**
-	 * @return Maximum number of sockets allowed
-	 */
-	inline unsigned long maxCount() const throw() { return ZT_PHY_MAX_SOCKETS; }
-
-	/**
-	 * Wrap a raw file descriptor in a PhySocket structure
-	 *
-	 * This can be used to select/poll on a raw file descriptor as part of this
-	 * class's I/O loop. By default the fd is set for read notification but
-	 * this can be controlled with setNotifyReadable(). When any detected
-	 * condition is present, the phyOnFileDescriptorActivity() callback is
-	 * called with one or both of its arguments 'true'.
-	 *
-	 * The Phy<>::close() method *must* be called when you're done with this
-	 * file descriptor to remove it from the select/poll set, but unlike other
-	 * types of sockets Phy<> does not actually close the underlying fd or
-	 * otherwise manage its life cycle. There is also no close notification
-	 * callback for this fd, since Phy<> doesn't actually perform reading or
-	 * writing or detect error conditions. This is only useful for adding a
-	 * file descriptor to Phy<> to select/poll on it.
-	 *
-	 * @param fd Raw file descriptor
-	 * @param uptr User pointer to supply to callbacks
-	 * @return PhySocket wrapping fd or NULL on failure (out of memory or too many sockets)
-	 */
-	inline PhySocket *wrapSocket(ZT_PHY_SOCKFD_TYPE fd,void *uptr = (void *)0)
-	{
-		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
-			return (PhySocket *)0;
-		try {
-			_socks.push_back(PhySocketImpl());
-		} catch ( ... ) {
-			return (PhySocket *)0;
-		}
-		PhySocketImpl &sws = _socks.back();
-		if ((long)fd > _nfds)
-			_nfds = (long)fd;
-		FD_SET(fd,&_readfds);
-		sws.type = ZT_PHY_SOCKET_UNIX_IN; /* TODO: Type was changed to allow for CBs with new RPC model */
-		sws.sock = fd;
-		sws.uptr = uptr;
-		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
-		// no sockaddr for this socket type, leave saddr null
-		return (PhySocket *)&sws;
-	}
-
-	/**
-	 * Bind a UDP socket
-	 *
-	 * @param localAddress Local endpoint address and port
-	 * @param uptr Initial value of user pointer associated with this socket (default: NULL)
-	 * @param bufferSize Desired socket receive/send buffer size -- will set as close to this as possible (default: 0, leave alone)
-	 * @return Socket or NULL on failure to bind
-	 */
-	inline PhySocket *udpBind(const struct sockaddr *localAddress,void *uptr = (void *)0,int bufferSize = 0)
-	{
-		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
-			return (PhySocket *)0;
-
-		ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_DGRAM,0);
-		if (!ZT_PHY_SOCKFD_VALID(s))
-			return (PhySocket *)0;
-
-		if (bufferSize > 0) {
-			int bs = bufferSize;
-			while (bs >= 65536) {
-				int tmpbs = bs;
-				if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
-					break;
-				bs -= 16384;
-			}
-			bs = bufferSize;
-			while (bs >= 65536) {
-				int tmpbs = bs;
-				if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
-					break;
-				bs -= 16384;
-			}
-		}
-
-#if defined(_WIN32) || defined(_WIN64)
-		{
-			BOOL f;
-			if (localAddress->sa_family == AF_INET6) {
-				f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f));
-				f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f));
-			}
-			f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
-			f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f));
-		}
-#else // not Windows
-		{
-			int f;
-			if (localAddress->sa_family == AF_INET6) {
-				f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
-#ifdef IPV6_MTU_DISCOVER
-				f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f));
-#endif
-#ifdef IPV6_DONTFRAG
-				f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f));
-#endif
-			}
-			f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
-			f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f));
-#ifdef IP_DONTFRAG
-			f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f));
-#endif
-#ifdef IP_MTU_DISCOVER
-			f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f));
-#endif
-#ifdef SO_NO_CHECK
-			// For now at least we only set SO_NO_CHECK on IPv4 sockets since some
-			// IPv6 stacks incorrectly discard zero checksum packets. May remove
-			// this restriction later once broken stuff dies more.
-			if ((localAddress->sa_family == AF_INET)&&(_noCheck)) {
-				f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f));
-			}
-#endif
-		}
-#endif // Windows or not
-
-		if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-
-#if defined(_WIN32) || defined(_WIN64)
-		{ u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); }
-#else
-		fcntl(s,F_SETFL,O_NONBLOCK);
-#endif
-
-		try {
-			_socks.push_back(PhySocketImpl());
-		} catch ( ... ) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-		PhySocketImpl &sws = _socks.back();
-
-		if ((long)s > _nfds)
-			_nfds = (long)s;
-		FD_SET(s,&_readfds);
-		sws.type = ZT_PHY_SOCKET_UDP;
-		sws.sock = s;
-		sws.uptr = uptr;
-		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
-		memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
-
-		return (PhySocket *)&sws;
-	}
-
-	/**
-	 * Set the IP TTL for the next outgoing packet (for IPv4 UDP sockets only)
-	 *
-	 * @param ttl New TTL (0 or >255 will set it to 255)
-	 * @return True on success
-	 */
-	inline bool setIp4UdpTtl(PhySocket *sock,unsigned int ttl)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-#if defined(_WIN32) || defined(_WIN64)
-		DWORD tmp = ((ttl == 0)||(ttl > 255)) ? 255 : (DWORD)ttl;
-		return (::setsockopt(sws.sock,IPPROTO_IP,IP_TTL,(const char *)&tmp,sizeof(tmp)) == 0);
-#else
-		int tmp = ((ttl == 0)||(ttl > 255)) ? 255 : (int)ttl;
-		return (::setsockopt(sws.sock,IPPROTO_IP,IP_TTL,(void *)&tmp,sizeof(tmp)) == 0);
-#endif
-	}
-
-	/**
-	 * Send a UDP packet
-	 *
-	 * @param sock UDP socket
-	 * @param remoteAddress Destination address (must be correct type for socket)
-	 * @param data Data to send
-	 * @param len Length of packet
-	 * @return True if packet appears to have been sent successfully
-	 */
-	inline bool udpSend(PhySocket *sock,const struct sockaddr *remoteAddress,const void *data,unsigned long len)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-#if defined(_WIN32) || defined(_WIN64)
-		return ((long)::sendto(sws.sock,reinterpret_cast<const char *>(data),len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
-#else
-		return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
-#endif
-	}
-
-#ifdef __UNIX_LIKE__
-	/**
-	 * Listen for connections on a Unix domain socket
-	 *
-	 * @param path Path to Unix domain socket
-	 * @param uptr Arbitrary pointer to associate
-	 * @return PhySocket or NULL if cannot bind
-	 */
-	inline PhySocket *unixListen(const char *path,void *uptr = (void *)0)
-	{
-		struct sockaddr_un sun;
-
-		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
-			return (PhySocket *)0;
-
-		memset(&sun,0,sizeof(sun));
-		sun.sun_family = AF_UNIX;
-		if (strlen(path) >= sizeof(sun.sun_path))
-			return (PhySocket *)0;
-		strcpy(sun.sun_path,path);
-
-		ZT_PHY_SOCKFD_TYPE s = ::socket(PF_UNIX,SOCK_STREAM,0);
-		if (!ZT_PHY_SOCKFD_VALID(s))
-			return (PhySocket *)0;
-
-		::fcntl(s,F_SETFL,O_NONBLOCK);
-
-		::unlink(path);
-		if (::bind(s,(struct sockaddr *)&sun,sizeof(struct sockaddr_un)) != 0) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-		if (::listen(s,128) != 0) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-
-		try {
-			_socks.push_back(PhySocketImpl());
-		} catch ( ... ) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-		PhySocketImpl &sws = _socks.back();
-
-		if ((long)s > _nfds)
-			_nfds = (long)s;
-		FD_SET(s,&_readfds);
-		sws.type = ZT_PHY_SOCKET_UNIX_LISTEN;
-		sws.sock = s;
-		sws.uptr = uptr;
-		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
-		memcpy(&(sws.saddr),&sun,sizeof(struct sockaddr_un));
-
-		return (PhySocket *)&sws;
-	}
-#endif // __UNIX_LIKE__
-
-	/**
-	 * Bind a local listen socket to listen for new TCP connections
-	 *
-	 * @param localAddress Local address and port
-	 * @param uptr Initial value of uptr for new socket (default: NULL)
-	 * @return Socket or NULL on failure to bind
-	 */
-	inline PhySocket *tcpListen(const struct sockaddr *localAddress,void *uptr = (void *)0)
-	{
-		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
-			return (PhySocket *)0;
-
-		ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_STREAM,0);
-		if (!ZT_PHY_SOCKFD_VALID(s))
-			return (PhySocket *)0;
-
-#if defined(_WIN32) || defined(_WIN64)
-		{
-			BOOL f;
-			f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f));
-			f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
-			f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
-			u_long iMode=1;
-			ioctlsocket(s,FIONBIO,&iMode);
-		}
-#else
-		{
-			int f;
-			f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
-			f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
-			f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
-			fcntl(s,F_SETFL,O_NONBLOCK);
-		}
-#endif
-
-		if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-
-		if (::listen(s,1024)) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-
-		try {
-			_socks.push_back(PhySocketImpl());
-		} catch ( ... ) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-		PhySocketImpl &sws = _socks.back();
-
-		if ((long)s > _nfds)
-			_nfds = (long)s;
-		FD_SET(s,&_readfds);
-		sws.type = ZT_PHY_SOCKET_TCP_LISTEN;
-		sws.sock = s;
-		sws.uptr = uptr;
-		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
-		memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
-
-		return (PhySocket *)&sws;
-	}
-
-	/**
-	 * Start a non-blocking connect; CONNECT handler is called on success or failure
-	 *
-	 * A return value of NULL indicates a synchronous failure such as a
-	 * failure to open a socket. The TCP connection handler is not called
-	 * in this case.
-	 *
-	 * It is possible on some platforms for an "instant connect" to occur,
-	 * such as when connecting to a loopback address. In this case, the
-	 * 'connected' result parameter will be set to 'true' and if the
-	 * 'callConnectHandler' flag is true (the default) the TCP connect
-	 * handler will be called before the function returns.
-	 *
-	 * These semantics can be a bit confusing, but they're less so than
-	 * the underlying semantics of asynchronous TCP connect.
-	 *
-	 * @param remoteAddress Remote address
-	 * @param connected Result parameter: set to whether an "instant connect" has occurred (true if yes)
-	 * @param uptr Initial value of uptr for new socket (default: NULL)
-	 * @param callConnectHandler If true, call TCP connect handler even if result is known before function exit (default: true)
-	 * @return New socket or NULL on failure
-	 */
-	inline PhySocket *tcpConnect(const struct sockaddr *remoteAddress,bool &connected,void *uptr = (void *)0,bool callConnectHandler = true)
-	{
-		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
-			return (PhySocket *)0;
-
-		ZT_PHY_SOCKFD_TYPE s = ::socket(remoteAddress->sa_family,SOCK_STREAM,0);
-		if (!ZT_PHY_SOCKFD_VALID(s)) {
-			connected = false;
-			return (PhySocket *)0;
-		}
-
-#if defined(_WIN32) || defined(_WIN64)
-		{
-			BOOL f;
-			if (remoteAddress->sa_family == AF_INET6) { f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); }
-			f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
-			f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
-			u_long iMode=1;
-			ioctlsocket(s,FIONBIO,&iMode);
-		}
-#else
-		{
-			int f;
-			if (remoteAddress->sa_family == AF_INET6) { f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); }
-			f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
-			f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
-			fcntl(s,F_SETFL,O_NONBLOCK);
-		}
-#endif
-
-		connected = true;
-		if (::connect(s,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) {
-			connected = false;
-#if defined(_WIN32) || defined(_WIN64)
-			if (WSAGetLastError() != WSAEWOULDBLOCK) {
-#else
-			if (errno != EINPROGRESS) {
-#endif
-				ZT_PHY_CLOSE_SOCKET(s);
-				return (PhySocket *)0;
-			} // else connection is proceeding asynchronously...
-		}
-
-		try {
-			_socks.push_back(PhySocketImpl());
-		} catch ( ... ) {
-			ZT_PHY_CLOSE_SOCKET(s);
-			return (PhySocket *)0;
-		}
-		PhySocketImpl &sws = _socks.back();
-
-		if ((long)s > _nfds)
-			_nfds = (long)s;
-		if (connected) {
-			FD_SET(s,&_readfds);
-			sws.type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED;
-		} else {
-			FD_SET(s,&_writefds);
-#if defined(_WIN32) || defined(_WIN64)
-			FD_SET(s,&_exceptfds);
-#endif
-			sws.type = ZT_PHY_SOCKET_TCP_OUT_PENDING;
-		}
-		sws.sock = s;
-		sws.uptr = uptr;
-		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
-		memcpy(&(sws.saddr),remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
-
-		if ((callConnectHandler)&&(connected)) {
-			try {
-				_handler->phyOnTcpConnect((PhySocket *)&sws,&(sws.uptr),true);
-			} catch ( ... ) {}
-		}
-
-		return (PhySocket *)&sws;
-	}
-
-	/**
-	 * Try to set buffer sizes as close to the given value as possible
-	 *
-	 * This will try the specified value and then lower values in 16K increments
-	 * until one works.
-	 *
-	 * @param sock Socket
-	 * @param receiveBufferSize Desired size of receive buffer
-	 * @param sendBufferSize Desired size of send buffer
-	 */
-	inline void setBufferSizes(const PhySocket *sock,int receiveBufferSize,int sendBufferSize)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-		if (receiveBufferSize > 0) {
-			while (receiveBufferSize > 0) {
-				int tmpbs = receiveBufferSize;
-				if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
-					break;
-				receiveBufferSize -= 16384;
-			}
-		}
-		if (sendBufferSize > 0) {
-			while (sendBufferSize > 0) {
-				int tmpbs = sendBufferSize;
-				if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0)
-					break;
-				sendBufferSize -= 16384;
-			}
-		}
-	}
-
-	/**
-	 * Attempt to send data to a stream socket (non-blocking)
-	 *
-	 * If -1 is returned, the socket should no longer be used as it is now
-	 * destroyed. If callCloseHandler is true, the close handler will be
-	 * called before the function returns.
-	 *
-	 * This can be used with TCP, Unix, or socket pair sockets.
-	 *
-	 * @param sock An open stream socket (other socket types will fail)
-	 * @param data Data to send
-	 * @param len Length of data
-	 * @param callCloseHandler If true, call close handler on socket closing failure condition (default: true)
-	 * @return Number of bytes actually sent or -1 on fatal error (socket closure)
-	 */
-	inline long streamSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-#if defined(_WIN32) || defined(_WIN64)
-		long n = (long)::send(sws.sock,reinterpret_cast<const char *>(data),len,0);
-		if (n == SOCKET_ERROR) {
-				switch(WSAGetLastError()) {
-					case WSAEINTR:
-					case WSAEWOULDBLOCK:
-						return 0;
-					default:
-						this->close(sock,callCloseHandler);
-						return -1;
-				}
-		}
-#else // not Windows
-		long n = (long)::send(sws.sock,data,len,0);
-		if (n < 0) {
-			switch(errno) {
-#ifdef EAGAIN
-				case EAGAIN:
-#endif
-#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) )
-				case EWOULDBLOCK:
-#endif
-#ifdef EINTR
-				case EINTR:
-#endif
-					return 0;
-				default:
-					this->close(sock,callCloseHandler);
-					return -1;
-			}
-		}
-#endif // Windows or not
-		return n;
-	}
-
-#ifdef __UNIX_LIKE__
-	/**
-	 * Attempt to send data to a Unix domain socket connection (non-blocking)
-	 *
-	 * If -1 is returned, the socket should no longer be used as it is now
-	 * destroyed. If callCloseHandler is true, the close handler will be
-	 * called before the function returns.
-	 *
-	 * @param sock An open Unix socket (other socket types will fail)
-	 * @param data Data to send
-	 * @param len Length of data
-	 * @param callCloseHandler If true, call close handler on socket closing failure condition (default: true)
-	 * @return Number of bytes actually sent or -1 on fatal error (socket closure)
-	 */
-	inline long unixSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-		long n = (long)::write(sws.sock,data,len);
-		if (n < 0) {
-			switch(errno) {
-#ifdef EAGAIN
-				case EAGAIN:
-#endif
-#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) )
-				case EWOULDBLOCK:
-#endif
-#ifdef EINTR
-				case EINTR:
-#endif
-					return 0;
-				default:
-					this->close(sock,callCloseHandler);
-					return -1;
-			}
-		}
-		return n;
-	}
-#endif // __UNIX_LIKE__
-
-	/**
-	 * For streams, sets whether we want to be notified that the socket is writable
-	 *
-	 * This can be used with TCP, Unix, or socket pair sockets.
-	 *
-	 * Call whack() if this is being done from another thread and you want
-	 * it to take effect immediately. Otherwise it is only guaranteed to
-	 * take effect on the next poll().
-	 *
-	 * @param sock Stream connection socket
-	 * @param notifyWritable Want writable notifications?
-	 */
-	inline void setNotifyWritable(PhySocket *sock,bool notifyWritable)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-		if (notifyWritable) {
-			FD_SET(sws.sock,&_writefds);
-		} else {
-			FD_CLR(sws.sock,&_writefds);
-		}
-	}
-
-	/**
-	 * Set whether we want to be notified that a socket is readable
-	 *
-	 * This is primarily for raw sockets added with wrapSocket(). It could be
-	 * used with others, but doing so would essentially lock them and prevent
-	 * data from being read from them until this is set to 'true' again.
-	 *
-	 * @param sock Socket to modify
-	 * @param notifyReadable True if socket should be monitored for readability
-	 */
-	inline void setNotifyReadable(PhySocket *sock,bool notifyReadable)
-	{
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-		if (notifyReadable) {
-			FD_SET(sws.sock,&_readfds);
-		} else {
-			FD_CLR(sws.sock,&_readfds);
-		}
-	}
-
-	/**
-	 * Wait for activity and handle one or more events
-	 *
-	 * Note that this is not guaranteed to wait up to 'timeout' even
-	 * if nothing happens, as whack() or other events such as signals
-	 * may cause premature termination.
-	 *
-	 * @param timeout Timeout in milliseconds or 0 for none (forever)
-	 */
-	inline void poll(unsigned long timeout)
-	{
-		char buf[131072];
-		struct sockaddr_storage ss;
-		struct timeval tv;
-		fd_set rfds,wfds,efds;
-
-		memcpy(&rfds,&_readfds,sizeof(rfds));
-		memcpy(&wfds,&_writefds,sizeof(wfds));
-#if defined(_WIN32) || defined(_WIN64)
-		memcpy(&efds,&_exceptfds,sizeof(efds));
-#else
-		FD_ZERO(&efds);
-#endif
-
-		tv.tv_sec = (long)(timeout / 1000);
-		tv.tv_usec = (long)((timeout % 1000) * 1000);
-		if (::select((int)_nfds + 1,&rfds,&wfds,&efds,(timeout > 0) ? &tv : (struct timeval *)0) <= 0)
-			return;
-
-		if (FD_ISSET(_whackReceiveSocket,&rfds)) {
-			char tmp[16];
-#if defined(_WIN32) || defined(_WIN64)
-			::recv(_whackReceiveSocket,tmp,16,0);
-#else
-			::read(_whackReceiveSocket,tmp,16);
-#endif
-		}
-
-		for(typename std::list<PhySocketImpl>::iterator s(_socks.begin());s!=_socks.end();) {
-			switch (s->type) {
-
-				case ZT_PHY_SOCKET_TCP_OUT_PENDING:
-#if defined(_WIN32) || defined(_WIN64)
-					if (FD_ISSET(s->sock,&efds)) {
-						this->close((PhySocket *)&(*s),true);
-					} else // ... if
-#endif
-					if (FD_ISSET(s->sock,&wfds)) {
-						socklen_t slen = sizeof(ss);
-						if (::getpeername(s->sock,(struct sockaddr *)&ss,&slen) != 0) {
-							this->close((PhySocket *)&(*s),true);
-						} else {
-							s->type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED;
-							FD_SET(s->sock,&_readfds);
-							FD_CLR(s->sock,&_writefds);
-#if defined(_WIN32) || defined(_WIN64)
-							FD_CLR(s->sock,&_exceptfds);
-#endif
-							try {
-								_handler->phyOnTcpConnect((PhySocket *)&(*s),&(s->uptr),true);
-							} catch ( ... ) {}
-						}
-					}
-					break;
-
-				case ZT_PHY_SOCKET_TCP_OUT_CONNECTED:
-				case ZT_PHY_SOCKET_TCP_IN: {
-					ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable
-					if (FD_ISSET(sock,&rfds)) {
-						long n = (long)::recv(sock,buf,sizeof(buf),0);
-						if (n <= 0) {
-							this->close((PhySocket *)&(*s),true);
-						} else {
-							try {
-								_handler->phyOnTcpData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n);
-							} catch ( ... ) {}
-						}
-					}
-					if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
-						try {
-							_handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr));
-						} catch ( ... ) {}
-					}
-				}	break;
-
-				case ZT_PHY_SOCKET_TCP_LISTEN:
-					if (FD_ISSET(s->sock,&rfds)) {
-						memset(&ss,0,sizeof(ss));
-						socklen_t slen = sizeof(ss);
-						ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen);
-						if (ZT_PHY_SOCKFD_VALID(newSock)) {
-							if (_socks.size() >= ZT_PHY_MAX_SOCKETS) {
-								ZT_PHY_CLOSE_SOCKET(newSock);
-							} else {
-#if defined(_WIN32) || defined(_WIN64)
-								{ BOOL f = (_noDelay ? TRUE : FALSE); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); }
-								{ u_long iMode=1; ioctlsocket(newSock,FIONBIO,&iMode); }
-#else
-								{ int f = (_noDelay ? 1 : 0); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); }
-								fcntl(newSock,F_SETFL,O_NONBLOCK);
-#endif
-								_socks.push_back(PhySocketImpl());
-								PhySocketImpl &sws = _socks.back();
-								FD_SET(newSock,&_readfds);
-								if ((long)newSock > _nfds)
-									_nfds = (long)newSock;
-								sws.type = ZT_PHY_SOCKET_TCP_IN;
-								sws.sock = newSock;
-								sws.uptr = (void *)0;
-								memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage));
-								try {
-									_handler->phyOnTcpAccept((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr),(const struct sockaddr *)&(sws.saddr));
-								} catch ( ... ) {}
-							}
-						}
-					}
-					break;
-
-				case ZT_PHY_SOCKET_UDP:
-					if (FD_ISSET(s->sock,&rfds)) {
-						for(int k=0;k<1024;++k) {
-							memset(&ss,0,sizeof(ss));
-							socklen_t slen = sizeof(ss);
-							long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen);
-							if (n > 0) {
-								try {
-									_handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&(s->saddr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n);
-								} catch ( ... ) {}
-							} else if (n < 0)
-								break;
-						}
-					}
-					break;
-
-				case ZT_PHY_SOCKET_UNIX_IN: {
-#ifdef __UNIX_LIKE__
-					ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable
-					if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
-						try {
-							_handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr));
-						} catch ( ... ) {}
-					}
-					if (FD_ISSET(sock,&rfds)) {
-						long n = (long)::read(sock,buf,sizeof(buf));
-						if (n <= 0) {
-							this->close((PhySocket *)&(*s),true);
-						} else {
-							try {
-								_handler->phyOnUnixData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n);
-							} catch ( ... ) {}
-						}
-					}
-#endif // __UNIX_LIKE__
-				}	break;
-
-				case ZT_PHY_SOCKET_UNIX_LISTEN:
-#ifdef __UNIX_LIKE__
-					if (FD_ISSET(s->sock,&rfds)) {
-						memset(&ss,0,sizeof(ss));
-						socklen_t slen = sizeof(ss);
-						ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen);
-						if (ZT_PHY_SOCKFD_VALID(newSock)) {
-							if (_socks.size() >= ZT_PHY_MAX_SOCKETS) {
-								ZT_PHY_CLOSE_SOCKET(newSock);
-							} else {
-								fcntl(newSock,F_SETFL,O_NONBLOCK);
-								_socks.push_back(PhySocketImpl());
-								PhySocketImpl &sws = _socks.back();
-								FD_SET(newSock,&_readfds);
-								if ((long)newSock > _nfds)
-									_nfds = (long)newSock;
-								sws.type = ZT_PHY_SOCKET_UNIX_IN;
-								sws.sock = newSock;
-								sws.uptr = (void *)0;
-								memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage));
-								try {
-									//_handler->phyOnUnixAccept((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr));
-								} catch ( ... ) {}
-							}
-						}
-					}
-#endif // __UNIX_LIKE__
-					break;
-
-				case ZT_PHY_SOCKET_FD: {
-					ZT_PHY_SOCKFD_TYPE sock = s->sock;
-					const bool readable = ((FD_ISSET(sock,&rfds))&&(FD_ISSET(sock,&_readfds)));
-					const bool writable = ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds)));
-					if ((readable)||(writable)) {
-						try {
-							//_handler->phyOnFileDescriptorActivity((PhySocket *)&(*s),&(s->uptr),readable,writable);
-						} catch ( ... ) {}
-					}
-				}	break;
-
-				default:
-					break;
-
-			}
-
-			if (s->type == ZT_PHY_SOCKET_CLOSED)
-				_socks.erase(s++);
-			else ++s;
-		}
-	}
-
-	/**
-	 * @param sock Socket to close
-	 * @param callHandlers If true, call handlers for TCP connect (success: false) or close (default: true)
-	 */
-	inline void close(PhySocket *sock,bool callHandlers = true)
-	{
-		if (!sock)
-			return;
-		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
-		if (sws.type == ZT_PHY_SOCKET_CLOSED)
-			return;
-
-		FD_CLR(sws.sock,&_readfds);
-		FD_CLR(sws.sock,&_writefds);
-#if defined(_WIN32) || defined(_WIN64)
-		FD_CLR(sws.sock,&_exceptfds);
-#endif
-
-		if (sws.type != ZT_PHY_SOCKET_FD)
-			ZT_PHY_CLOSE_SOCKET(sws.sock);
-
-#ifdef __UNIX_LIKE__
-		if (sws.type == ZT_PHY_SOCKET_UNIX_LISTEN)
-			::unlink(((struct sockaddr_un *)(&(sws.saddr)))->sun_path);
-#endif // __UNIX_LIKE__
-
-		if (callHandlers) {
-			switch(sws.type) {
-				case ZT_PHY_SOCKET_TCP_OUT_PENDING:
-					try {
-						_handler->phyOnTcpConnect(sock,&(sws.uptr),false);
-					} catch ( ... ) {}
-					break;
-				case ZT_PHY_SOCKET_TCP_OUT_CONNECTED:
-				case ZT_PHY_SOCKET_TCP_IN:
-					try {
-						_handler->phyOnTcpClose(sock,&(sws.uptr));
-					} catch ( ... ) {}
-					break;
-				case ZT_PHY_SOCKET_UNIX_IN:
-#ifdef __UNIX_LIKE__
-					try {
-						_handler->phyOnUnixClose(sock,&(sws.uptr));
-					} catch ( ... ) {}
-#endif // __UNIX_LIKE__
-					break;
-				default:
-					break;
-			}
-		}
-
-		// Causes entry to be deleted from list in poll(), ignored elsewhere
-		sws.type = ZT_PHY_SOCKET_CLOSED;
-
-		if ((long)sws.sock >= (long)_nfds) {
-			long nfds = (long)_whackSendSocket;
-			if ((long)_whackReceiveSocket > nfds)
-				nfds = (long)_whackReceiveSocket;
-			for(typename std::list<PhySocketImpl>::iterator s(_socks.begin());s!=_socks.end();++s) {
-				if ((s->type != ZT_PHY_SOCKET_CLOSED)&&((long)s->sock > nfds))
-					nfds = (long)s->sock;
-			}
-			_nfds = nfds;
-		}
-	}
-};
-
-} // namespace ZeroTier
-
-#endif

+ 0 - 14
attic/PortMapper-libnatpmp.c

@@ -1,14 +0,0 @@
-#define ENABLE_STRNATPMPERR
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
-#define _XOPEN_SOURCE 600
-
-#ifdef __APPLE__
-#ifndef _DARWIN_C_SOURCE
-#define _DARWIN_C_SOURCE
-#endif
-#endif
-
-#include "../ext/libnatpmp/getgateway.c"
-#include "../ext/libnatpmp/wingettimeofday.c"
-#include "../ext/libnatpmp/natpmp.c"

+ 0 - 41
attic/PortMapper-miniupnpc.c

@@ -1,41 +0,0 @@
-#define MINIUPNP_STATICLIB
-#define MINIUPNPC_SET_SOCKET_TIMEOUT
-#define MINIUPNPC_GET_SRC_ADDR
-#define _BSD_SOURCE
-#define _DEFAULT_SOURCE
-#define _XOPEN_SOURCE 600
-#define MINIUPNPC_VERSION_STRING "2.0"
-#define UPNP_VERSION_STRING "UPnP/1.1"
-
-#ifdef __LINUX__
-#define OS_STRING "Linux"
-#endif
-#ifdef __APPLE__
-#define OS_STRING "Darwin"
-#endif
-#ifdef __WINDOWS__
-#define OS_STRING "Windows"
-#endif
-#ifndef OS_STRING
-#define OS_STRING "ZeroTier"
-#endif
-
-#ifdef __APPLE__
-#ifndef _DARWIN_C_SOURCE
-#define _DARWIN_C_SOURCE
-#endif
-#endif
-
-#include "../ext/miniupnpc/connecthostport.c"
-#include "../ext/miniupnpc/igd_desc_parse.c"
-#include "../ext/miniupnpc/minisoap.c"
-#include "../ext/miniupnpc/miniupnpc.c"
-#include "../ext/miniupnpc/miniwget.c"
-#include "../ext/miniupnpc/minixml.c"
-#include "../ext/miniupnpc/portlistingparse.c"
-#include "../ext/miniupnpc/receivedata.c"
-#include "../ext/miniupnpc/upnpcommands.c"
-#include "../ext/miniupnpc/upnpdev.c"
-#include "../ext/miniupnpc/upnperrors.c"
-#include "../ext/miniupnpc/upnpreplyparse.c"
-#include "../ext/miniupnpc/minissdpc.c"

+ 0 - 334
attic/PortMapper.cpp

@@ -1,334 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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.
- */
-/****/
-
-// Uncomment to dump debug messages
-//#define ZT_PORTMAPPER_TRACE 1
-
-#ifdef __ANDROID__
-#include <android/log.h>
-#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__))
-#else
-#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__)
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <string>
-
-#include "../node/Utils.hpp"
-#include "OSUtils.hpp"
-#include "PortMapper.hpp"
-
-// These must be defined to get rid of dynamic export stuff in libminiupnpc and libnatpmp
-#ifdef __WINDOWS__
-#ifndef MINIUPNP_STATICLIB
-#define MINIUPNP_STATICLIB
-#endif
-#ifndef STATICLIB
-#define STATICLIB
-#endif
-#endif
-
-#ifdef ZT_USE_SYSTEM_MINIUPNPC
-#include <miniupnpc/miniupnpc.h>
-#include <miniupnpc/upnpcommands.h>
-#else
-#ifdef __ANDROID__
-#include "miniupnpc.h"
-#include "upnpcommands.h"
-#else
-#include "../ext/miniupnpc/miniupnpc.h"
-#include "../ext/miniupnpc/upnpcommands.h"
-#endif
-#endif
-
-#ifdef ZT_USE_SYSTEM_NATPMP
-#include <natpmp.h>
-#else
-#ifdef __ANDROID__
-#include "natpmp.h"
-#else
-#include "../ext/libnatpmp/natpmp.h"
-#endif
-#endif
-
-namespace ZeroTier {
-
-class PortMapperImpl
-{
-public:
-	PortMapperImpl(int localUdpPortToMap,const char *un) :
-		run(true),
-		localPort(localUdpPortToMap),
-		uniqueName(un)
-	{
-	}
-
-	~PortMapperImpl() {}
-
-	void threadMain()
-		throw()
-	{
-		int mode = 0; // 0 == NAT-PMP, 1 == UPnP
-
-#ifdef ZT_PORTMAPPER_TRACE
-		fprintf(stderr,"PortMapper: started for UDP port %d" ZT_EOL_S,localPort);
-#endif
-
-		while (run) {
-
-			// ---------------------------------------------------------------------
-			// NAT-PMP mode (preferred)
-			// ---------------------------------------------------------------------
-			if (mode == 0) {
-			  natpmp_t natpmp;
-			  natpmpresp_t response;
-				int r = 0;
-
-				bool natPmpSuccess = false;
-				for(int tries=0;tries<60;++tries) {
-					int tryPort = (int)localPort + tries;
-					if (tryPort >= 65535)
-						tryPort = (tryPort - 65535) + 1025;
-
-					memset(&natpmp,0,sizeof(natpmp));
-					memset(&response,0,sizeof(response));
-
-					if (initnatpmp(&natpmp,0,0) != 0) {
-						mode = 1;
-						closenatpmp(&natpmp);
-#ifdef ZT_PORTMAPPER_TRACE
-						PM_TRACE("PortMapper: NAT-PMP: init failed, switching to UPnP mode" ZT_EOL_S);
-#endif
-						break;
-					}
-
-					InetAddress publicAddress;
-					sendpublicaddressrequest(&natpmp);
-					int64_t myTimeout = OSUtils::now() + 5000;
-					do {
-						fd_set fds;
-						struct timeval timeout;
-						FD_ZERO(&fds);
-						FD_SET(natpmp.s, &fds);
-						getnatpmprequesttimeout(&natpmp, &timeout);
-						select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
-						r = readnatpmpresponseorretry(&natpmp, &response);
-						if (OSUtils::now() >= myTimeout)
-							break;
-					} while (r == NATPMP_TRYAGAIN);
-					if (r == 0) {
-						publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0);
-					} else {
-#ifdef ZT_PORTMAPPER_TRACE
-						PM_TRACE("PortMapper: NAT-PMP: request for external address failed, aborting..." ZT_EOL_S);
-#endif
-						closenatpmp(&natpmp);
-						break;
-					}
-
-				  sendnewportmappingrequest(&natpmp,NATPMP_PROTOCOL_UDP,localPort,tryPort,(ZT_PORTMAPPER_REFRESH_DELAY * 2) / 1000);
-					myTimeout = OSUtils::now() + 10000;
-					do {
-				    fd_set fds;
-				    struct timeval timeout;
-				    FD_ZERO(&fds);
-				    FD_SET(natpmp.s, &fds);
-				    getnatpmprequesttimeout(&natpmp, &timeout);
-				    select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
-				    r = readnatpmpresponseorretry(&natpmp, &response);
-						if (OSUtils::now() >= myTimeout)
-							break;
-				  } while (r == NATPMP_TRYAGAIN);
-					if (r == 0) {
-						publicAddress.setPort(response.pnu.newportmapping.mappedpublicport);
-#ifdef ZT_PORTMAPPER_TRACE
-						char paddr[128];
-						PM_TRACE("PortMapper: NAT-PMP: mapped %u to %s" ZT_EOL_S,(unsigned int)localPort,publicAddress.toString(paddr));
-#endif
-						Mutex::Lock sl(surface_l);
-						surface.clear();
-						surface.push_back(publicAddress);
-						natPmpSuccess = true;
-						closenatpmp(&natpmp);
-						break;
-					} else {
-						closenatpmp(&natpmp);
-						// continue
-					}
-				}
-
-				if (!natPmpSuccess) {
-					mode = 1;
-#ifdef ZT_PORTMAPPER_TRACE
-					PM_TRACE("PortMapper: NAT-PMP: request failed, switching to UPnP mode" ZT_EOL_S);
-#endif
-				}
-			}
-			// ---------------------------------------------------------------------
-
-			// ---------------------------------------------------------------------
-			// UPnP mode
-			// ---------------------------------------------------------------------
-			if (mode == 1) {
-				char lanaddr[4096];
-				char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P
-				char inport[16];
-				char outport[16];
-				struct UPNPUrls urls;
-				struct IGDdatas data;
-
-				int upnpError = 0;
-				UPNPDev *devlist = upnpDiscoverAll(5000,(const char *)0,(const char *)0,0,0,2,&upnpError);
-				if (devlist) {
-
-#ifdef ZT_PORTMAPPER_TRACE
-					{
-						UPNPDev *dev = devlist;
-						while (dev) {
-							PM_TRACE("PortMapper: found UPnP device at URL '%s': %s" ZT_EOL_S,dev->descURL,dev->st);
-							dev = dev->pNext;
-						}
-					}
-#endif
-
-					memset(lanaddr,0,sizeof(lanaddr));
-					memset(externalip,0,sizeof(externalip));
-					memset(&urls,0,sizeof(urls));
-					memset(&data,0,sizeof(data));
-					OSUtils::ztsnprintf(inport,sizeof(inport),"%d",localPort);
-
-					if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) {
-#ifdef ZT_PORTMAPPER_TRACE
-						PM_TRACE("PortMapper: UPnP: my LAN IP address: %s" ZT_EOL_S,lanaddr);
-#endif
-						if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) {
-#ifdef ZT_PORTMAPPER_TRACE
-							PM_TRACE("PortMapper: UPnP: my external IP address: %s" ZT_EOL_S,externalip);
-#endif
-
-							for(int tries=0;tries<60;++tries) {
-								int tryPort = (int)localPort + tries;
-								if (tryPort >= 65535)
-									tryPort = (tryPort - 65535) + 1025;
-								OSUtils::ztsnprintf(outport,sizeof(outport),"%u",tryPort);
-
-								// First check and see if this port is already mapped to the
-								// same unique name. If so, keep this mapping and don't try
-								// to map again since this can break buggy routers. But don't
-								// fail if this command fails since not all routers support it.
-								{
-									char haveIntClient[128]; // 128 == big enough for all these as per miniupnpc "documentation"
-									char haveIntPort[128];
-									char haveDesc[128];
-									char haveEnabled[128];
-									char haveLeaseDuration[128];
-									memset(haveIntClient,0,sizeof(haveIntClient));
-									memset(haveIntPort,0,sizeof(haveIntPort));
-									memset(haveDesc,0,sizeof(haveDesc));
-									memset(haveEnabled,0,sizeof(haveEnabled));
-									memset(haveLeaseDuration,0,sizeof(haveLeaseDuration));
-									if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) {
-#ifdef ZT_PORTMAPPER_TRACE
-										PM_TRACE("PortMapper: UPnP: reusing previously reserved external port: %s" ZT_EOL_S,outport);
-#endif
-										Mutex::Lock sl(surface_l);
-										surface.clear();
-										InetAddress tmp(externalip);
-										tmp.setPort(tryPort);
-										surface.push_back(tmp);
-										break;
-									}
-								}
-
-								// Try to map this port
-								int mapResult = 0;
-								if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) {
-#ifdef ZT_PORTMAPPER_TRACE
-									PM_TRACE("PortMapper: UPnP: reserved external port: %s" ZT_EOL_S,outport);
-#endif
-									Mutex::Lock sl(surface_l);
-									surface.clear();
-									InetAddress tmp(externalip);
-									tmp.setPort(tryPort);
-									surface.push_back(tmp);
-									break;
-								} else {
-#ifdef ZT_PORTMAPPER_TRACE
-									PM_TRACE("PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d" ZT_EOL_S,outport,mapResult);
-#endif
-									Thread::sleep(1000);
-								}
-							}
-
-						} else {
-							mode = 0;
-#ifdef ZT_PORTMAPPER_TRACE
-							PM_TRACE("PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode" ZT_EOL_S);
-#endif
-						}
-					} else {
-						mode = 0;
-#ifdef ZT_PORTMAPPER_TRACE
-						PM_TRACE("PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode" ZT_EOL_S);
-#endif
-					}
-
-					freeUPNPDevlist(devlist);
-
-				} else {
-					mode = 0;
-#ifdef ZT_PORTMAPPER_TRACE
-					PM_TRACE("PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d" ZT_EOL_S,upnpError);
-#endif
-				}
-			}
-			// ---------------------------------------------------------------------
-
-#ifdef ZT_PORTMAPPER_TRACE
-			PM_TRACE("UPNPClient: rescanning in %d ms" ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY);
-#endif
-			Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY);
-		}
-
-		delete this;
-	}
-
-	volatile bool run;
-	int localPort;
-	std::string uniqueName;
-
-	Mutex surface_l;
-	std::vector<InetAddress> surface;
-};
-
-PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName)
-{
-	_impl = new PortMapperImpl(localUdpPortToMap,uniqueName);
-	Thread::start(_impl);
-}
-
-PortMapper::~PortMapper()
-{
-	_impl->run = false;
-}
-
-std::vector<InetAddress> PortMapper::get() const
-{
-	Mutex::Lock _l(_impl->surface_l);
-	return _impl->surface;
-}
-
-} // namespace ZeroTier

+ 0 - 62
attic/PortMapper.hpp

@@ -1,62 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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_PORTMAPPER_HPP
-#define ZT_PORTMAPPER_HPP
-
-#include <vector>
-
-#include "../node/Constants.hpp"
-#include "../node/InetAddress.hpp"
-#include "../node/Mutex.hpp"
-#include "Thread.hpp"
-
-/**
- * How frequently should we refresh our UPNP/NAT-PnP/whatever state?
- */
-#define ZT_PORTMAPPER_REFRESH_DELAY 120000
-
-namespace ZeroTier {
-
-class PortMapperImpl;
-
-/**
- * UPnP/NAT-PnP port mapping "daemon"
- */
-class PortMapper
-{
-	friend class PortMapperImpl;
-
-public:
-	/**
-	 * Create and start port mapper service
-	 *
-	 * @param localUdpPortToMap Port we want visible to the outside world
-	 * @param name Unique name of this endpoint (based on ZeroTier address)
-	 */
-	PortMapper(int localUdpPortToMap,const char *uniqueName);
-
-	~PortMapper();
-
-	/**
-	 * @return All current external mappings for our port
-	 */
-	std::vector<InetAddress> get() const;
-
-private:
-	PortMapperImpl *_impl;
-};
-
-} // namespace ZeroTier
-
-#endif

+ 0 - 182
attic/Root.hpp

@@ -1,182 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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_ROOT_HPP
-#define ZT_ROOT_HPP
-
-#include "Constants.hpp"
-#include "Str.hpp"
-#include "ECC384.hpp"
-#include "Locator.hpp"
-#include "InetAddress.hpp"
-#include "Utils.hpp"
-#include "Identity.hpp"
-#include "Mutex.hpp"
-
-namespace ZeroTier {
-
-/**
- * A root entry pointing to a node capable of global identity lookup and indirect transit
- *
- * Root entries point to DNS records that contain TXT entries that decode to Locator objects
- * pointing to actual root nodes. A default root identity and static addresses can also be
- * provided as fallback if DNS is not available.
- *
- * Note that root identities can change if DNS returns a different result, but that DNS entries
- * are authenticated using their own signature scheme. This allows a root DNS name to serve
- * up different roots based on factors like location or relative load of different roots.
- *
- * It's also possible to create a root with no DNS and no DNS validator public key. This root
- * will be a static entry pointing to a single root identity and set of physical addresses.
- */
-class Root
-{
-public:
-	ZT_ALWAYS_INLINE Root() : _dnsPublicKeySize(0) {}
-
-	/**
-	 * Create a new root entry
-	 *
-	 * @param dn DNS name
-	 * @param dnspk DNS public key for record validation
-	 * @param dnspksize Size of DNS public key (currently always the size of a NIST P-384 point compressed public key)
-	 * @param dflId Default identity if DNS is not available
-	 * @param dflAddrs Default IP addresses if DNS is not available
-	 */
-	template<typename S>
-	ZT_ALWAYS_INLINE Root(S dn,const uint8_t *const dnspk,const unsigned int dnspksize,const Identity &dflId,const std::vector<InetAddress> &dflAddrs) :
-		_defaultIdentity(dflId),
-		_defaultAddresses(dflAddrs),
-		_dnsName(dn),
-		_dnsPublicKeySize(dnspksize)
-	{
-		if (dnspksize != 0) {
-			if (dnspksize > sizeof(_dnsPublicKey))
-				throw ZT_EXCEPTION_INVALID_ARGUMENT;
-			memcpy(_dnsPublicKey,dnspk,dnspksize);
-		}
-	}
-
-	/**
-	 * @return Current identity (either default or latest locator)
-	 */
-	ZT_ALWAYS_INLINE const Identity id() const
-	{
-		if (_lastFetchedLocator.id())
-			return _lastFetchedLocator.id();
-		return _defaultIdentity;
-	}
-
-	/**
-	 * @param id Identity to check
-	 * @return True if identity equals this root's current identity
-	 */
-	ZT_ALWAYS_INLINE bool is(const Identity &id) const
-	{
-		return ((_lastFetchedLocator.id()) ? (id == _lastFetchedLocator.id()) : (id == _defaultIdentity));
-	}
-
-	/**
-	 * @return Current ZeroTier address (either default or latest locator)
-	 */
-	ZT_ALWAYS_INLINE const Address address() const
-	{
-		if (_lastFetchedLocator.id())
-			return _lastFetchedLocator.id().address();
-		return _defaultIdentity.address();
-	}
-
-	/**
-	 * @return DNS name for this root or empty string if static entry with no DNS
-	 */
-	ZT_ALWAYS_INLINE const Str dnsName() const { return _dnsName; }
-
-	/**
-	 * @return Latest locator or NIL locator object if none
-	 */
-	ZT_ALWAYS_INLINE Locator locator() const { return _lastFetchedLocator; }
-
-	/**
-	 * @return Timestamp of latest retrieved locator or 0 if none
-	 */
-	ZT_ALWAYS_INLINE int64_t locatorTimestamp() const { return _lastFetchedLocator.timestamp(); }
-
-	/**
-	 * Update locator, returning true if new locator is valid and newer than existing
-	 */
-	ZT_ALWAYS_INLINE bool updateLocator(const Locator &loc)
-	{
-		if (!loc.verify())
-			return false;
-		if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) {
-			_lastFetchedLocator = loc;
-			return true;
-		}
-		return false;
-	}
-
-	/**
-	 * Update this root's locator from a series of TXT records
-	 */
-	template<typename I>
-	ZT_ALWAYS_INLINE bool updateLocatorFromTxt(I start,I end)
-	{
-		try {
-			if (_dnsPublicKeySize != ZT_ECC384_PUBLIC_KEY_SIZE)
-				return false;
-			Locator loc;
-			if (!loc.decodeTxtRecords(start,end,_dnsPublicKey)) // also does verify()
-				return false;
-			if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) {
-				_lastFetchedLocator = loc;
-				return true;
-			}
-			return false;
-		} catch ( ... ) {}
-		return false;
-	}
-
-	/**
-	 * Pick a random physical IP for this root with the given address family
-	 *
-	 * @param addressFamily AF_INET or AF_INET6
-	 * @return Address or InetAddress::NIL if no addresses exist for the given family
-	 */
-	ZT_ALWAYS_INLINE const InetAddress &pickPhysical(const int addressFamily) const
-	{
-		std::vector<const InetAddress *> pickList;
-		const std::vector<InetAddress> *const av = (_lastFetchedLocator) ? &(_lastFetchedLocator.phy()) : &_defaultAddresses;
-		for(std::vector<InetAddress>::const_iterator i(av->begin());i!=av->end();++i) {
-			if (addressFamily == (int)i->ss_family) {
-				pickList.push_back(&(*i));
-			}
-		}
-		if (pickList.size() == 1)
-			return *pickList[0];
-		else if (pickList.size() > 1)
-			return *pickList[(unsigned long)Utils::random() % (unsigned long)pickList.size()];
-		return InetAddress::NIL;
-	}
-
-private:
-	Identity _defaultIdentity;
-	std::vector<InetAddress> _defaultAddresses;
-	Str _dnsName;
-	Locator _lastFetchedLocator;
-	unsigned int _dnsPublicKeySize;
-	uint8_t _dnsPublicKey[ZT_ECC384_PUBLIC_KEY_SIZE];
-};
-
-} // namespace ZeroTier
-
-#endif

+ 0 - 18
attic/linux-old-glibc-compat.c

@@ -1,18 +0,0 @@
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-__asm__(".symver memcpy,memcpy@GLIBC_2.2.5");
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern void *__wrap_memcpy(void *dest,const void *src,size_t n)
-{
-  return memcpy(dest,src,n);
-}
-
-#ifdef __cplusplus
-}
-#endif

+ 0 - 30
attic/listaddrinfo.go

@@ -1,30 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"net"
-)
-
-func main() {
-	ifs, err := net.Interfaces()
-	if err != nil {
-		fmt.Printf("Error: %s\n", err.Error())
-		return
-	}
-	for _, i := range ifs {
-		fmt.Printf("name: %s\n", i.Name)
-		fmt.Printf("hwaddr: %s\n", i.HardwareAddr.String())
-		fmt.Printf("index: %d\n", i.Index)
-		fmt.Printf("addrs:\n")
-		addrs, _ := i.Addrs()
-		for _, a := range addrs {
-			fmt.Printf("  %s\n", a.String())
-		}
-		fmt.Printf("multicast:\n")
-		mc, _ := i.MulticastAddrs()
-		for _, m := range mc {
-			fmt.Printf("  %s\n", m.String())
-		}
-		fmt.Printf("\n")
-	}
-}

+ 0 - 1542
attic/one.cpp

@@ -1,1542 +0,0 @@
-/*
- * Copyright (c)2019 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: 2023-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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
-#include <time.h>
-#include <errno.h>
-
-#include "node/Constants.hpp"
-
-#ifdef __WINDOWS__
-#include <WinSock2.h>
-#include <Windows.h>
-#include <tchar.h>
-#include <wchar.h>
-#include <lmcons.h>
-#include <newdev.h>
-#include <atlbase.h>
-#include "osdep/WindowsEthernetTap.hpp"
-#include "windows/ZeroTierOne/ServiceInstaller.h"
-#include "windows/ZeroTierOne/ServiceBase.h"
-#include "windows/ZeroTierOne/ZeroTierOneService.h"
-#else
-#include <unistd.h>
-#include <pwd.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <dirent.h>
-#include <signal.h>
-#ifdef __LINUX__
-#include <sys/prctl.h>
-#include <sys/syscall.h>
-#include <sys/wait.h>
-#ifndef ZT_NO_CAPABILITIES
-#include <linux/capability.h>
-#include <linux/securebits.h>
-#endif
-#endif
-#endif
-
-#include <string>
-#include <stdexcept>
-#include <iostream>
-#include <sstream>
-#include <algorithm>
-
-#include "version.h"
-#include "include/ZeroTierOne.h"
-
-#include "node/Identity.hpp"
-#include "node/CertificateOfMembership.hpp"
-#include "node/Utils.hpp"
-#include "node/NetworkController.hpp"
-#include "node/Buffer.hpp"
-
-#include "osdep/OSUtils.hpp"
-#include "osdep/Http.hpp"
-#include "osdep/Thread.hpp"
-
-#include "service/OneService.hpp"
-
-#include "ext/json/json.hpp"
-
-#define ZT_PID_PATH "zerotier-one.pid"
-
-using namespace ZeroTier;
-
-static OneService *volatile zt1Service = (OneService *)0;
-
-#define PROGRAM_NAME "ZeroTier One"
-#define COPYRIGHT_NOTICE "Copyright (c) 2019 ZeroTier, Inc."
-#define LICENSE_GRANT "Licensed under the ZeroTier BSL 1.1 (see LICENSE.txt)"
-
-/****************************************************************************/
-/* zerotier-cli personality                                                 */
-/****************************************************************************/
-
-// This is getting deprecated soon in favor of the stuff in cli/
-
-static void cliPrintHelp(const char *pn,FILE *out)
-{
-	fprintf(out,
-		"%s version %d.%d.%d build %d" ZT_EOL_S,
-		PROGRAM_NAME,
-		ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD);
-	fprintf(out,
-		COPYRIGHT_NOTICE ZT_EOL_S
-		LICENSE_GRANT ZT_EOL_S);
-	fprintf(out,"Usage: %s [-switches] <command/path> [<args>]" ZT_EOL_S"" ZT_EOL_S,pn);
-	fprintf(out,"Available switches:" ZT_EOL_S);
-	fprintf(out,"  -h                      - Display this help" ZT_EOL_S);
-	fprintf(out,"  -v                      - Show version" ZT_EOL_S);
-	fprintf(out,"  -j                      - Display full raw JSON output" ZT_EOL_S);
-	fprintf(out,"  -D<path>                - ZeroTier home path for parameter auto-detect" ZT_EOL_S);
-	fprintf(out,"  -p<port>                - HTTP port (default: auto)" ZT_EOL_S);
-	fprintf(out,"  -T<token>               - Authentication token (default: auto)" ZT_EOL_S);
-	fprintf(out,ZT_EOL_S"Available commands:" ZT_EOL_S);
-	fprintf(out,"  info                    - Display status info" ZT_EOL_S);
-	fprintf(out,"  listpeers               - List all peers" ZT_EOL_S);
-	fprintf(out,"  peers                   - List all peers (prettier)" ZT_EOL_S);
-	fprintf(out,"  listnetworks            - List all networks" ZT_EOL_S);
-	fprintf(out,"  join <network>          - Join a network" ZT_EOL_S);
-	fprintf(out,"  leave <network>         - Leave a network" ZT_EOL_S);
-	fprintf(out,"  set <network> <setting> - Set a network setting" ZT_EOL_S);
-	fprintf(out,"  get <network> <setting> - Get a network setting" ZT_EOL_S);
-	fprintf(out,ZT_EOL_S"Available settings:" ZT_EOL_S);
-	fprintf(out,"  Settings to use with [get/set] may include property names from " ZT_EOL_S);
-	fprintf(out,"  the JSON output of \"zerotier-cli -j listnetworks\". Additionally, " ZT_EOL_S);
-	fprintf(out,"  (ip, ip4, ip6, ip6plane, and ip6prefix can be used). For instance:" ZT_EOL_S);
-	fprintf(out,"  zerotier-cli get <nwid> ip6plane will return the 6PLANE address" ZT_EOL_S);
-	fprintf(out,"  assigned to this node." ZT_EOL_S);
-}
-
-static std::string cliFixJsonCRs(const std::string &s)
-{
-	std::string r;
-	for(std::string::const_iterator c(s.begin());c!=s.end();++c) {
-		if (*c == '\n')
-			r.append(ZT_EOL_S);
-		else r.push_back(*c);
-	}
-	return r;
-}
-
-#ifdef __WINDOWS__
-static int cli(int argc, _TCHAR* argv[])
-#else
-static int cli(int argc,char **argv)
-#endif
-{
-	unsigned int port = 0;
-	std::string homeDir,command,arg1,arg2,authToken;
-	std::string ip("127.0.0.1");
-	bool json = false;
-	for(int i=1;i<argc;++i) {
-		if (argv[i][0] == '-') {
-			switch(argv[i][1]) {
-
-				case 'q': // ignore -q used to invoke this personality
-					if (argv[i][2]) {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-				case 'j':
-					if (argv[i][2]) {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					json = true;
-					break;
-
-				case 'p':
-					port = Utils::strToUInt(argv[i] + 2);
-					if ((port > 0xffff)||(port == 0)) {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-				case 'D':
-					if (argv[i][2]) {
-						homeDir = argv[i] + 2;
-					} else {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-				case 'H':
-					if (argv[i][2]) {
-						ip = argv[i] + 2;
-					} else {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-				case 'T':
-					if (argv[i][2]) {
-						authToken = argv[i] + 2;
-					} else {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-				case 'v':
-					if (argv[i][2]) {
-						cliPrintHelp(argv[0],stdout);
-						return 1;
-					}
-					printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
-					return 0;
-
-				case 'h':
-				case '?':
-				default:
-					cliPrintHelp(argv[0],stdout);
-					return 0;
-			}
-		} else {
-			if (arg1.length())
-				arg2 = argv[i];
-			else if (command.length())
-				arg1 = argv[i];
-			else command = argv[i];
-		}
-	}
-	if (!homeDir.length())
-		homeDir = OneService::platformDefaultHomePath();
-
-	if ((!port)||(!authToken.length())) {
-		if (!homeDir.length()) {
-			fprintf(stderr,"%s: missing port or authentication token and no home directory specified to auto-detect" ZT_EOL_S,argv[0]);
-			return 2;
-		}
-
-		if (!port) {
-			std::string portStr;
-			OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr);
-			port = Utils::strToUInt(portStr.c_str());
-			if ((port == 0)||(port > 0xffff)) {
-				fprintf(stderr,"%s: missing port and zerotier-one.port not found in %s" ZT_EOL_S,argv[0],homeDir.c_str());
-				return 2;
-			}
-		}
-
-		if (!authToken.length()) {
-			OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken);
-#ifdef __UNIX_LIKE__
-			if (!authToken.length()) {
-				const char *hd = getenv("HOME");
-				if (hd) {
-					char p[4096];
-#ifdef __APPLE__
-					OSUtils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd);
-#else
-					OSUtils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd);
-#endif
-					OSUtils::readFile(p,authToken);
-				}
-			}
-#endif
-			if (!authToken.length()) {
-				fprintf(stderr,"%s: missing authentication token and authtoken.secret not found (or readable) in %s" ZT_EOL_S,argv[0],homeDir.c_str());
-				return 2;
-			}
-		}
-	}
-
-	InetAddress addr;
-	{
-		char addrtmp[256];
-		OSUtils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
-		addr = InetAddress(addrtmp);
-	}
-
-	std::map<std::string,std::string> requestHeaders;
-	std::map<std::string,std::string> responseHeaders;
-	std::string responseBody;
-
-	requestHeaders["X-ZT1-Auth"] = authToken;
-
-	if ((command.length() > 0)&&(command[0] == '/')) {
-		unsigned int scode = Http::GET(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			command.c_str(),
-			requestHeaders,
-			responseHeaders,
-			responseBody);
-		if (scode == 200) {
-			printf("%s", cliFixJsonCRs(responseBody).c_str());
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if ((command == "info")||(command == "status")) {
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
-
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-
-		if (scode == 200) {
-			if (json) {
-				printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
-			} else {
-				if (j.is_object()) {
-					printf("200 info %s %s %s" ZT_EOL_S,
-						OSUtils::jsonString(j["address"],"-").c_str(),
-						OSUtils::jsonString(j["version"],"-").c_str(),
-						((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE")));
-				}
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "listpeers") {
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
-
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-
-		if (scode == 200) {
-			if (json) {
-				printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
-			} else {
-				printf("200 listpeers <ztaddr> <path> <latency> <version> <role>" ZT_EOL_S);
-				if (j.is_array()) {
-					for(unsigned long k=0;k<j.size();++k) {
-						nlohmann::json &p = j[k];
-						std::string bestPath;
-						nlohmann::json &paths = p["paths"];
-						if (paths.is_array()) {
-							for(unsigned long i=0;i<paths.size();++i) {
-								nlohmann::json &path = paths[i];
-								if (path["preferred"]) {
-									char tmp[256];
-									std::string addr = path["address"];
-									const int64_t now = OSUtils::now();
-									OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s;%lld;%lld",addr.c_str(),now - (int64_t)path["lastSend"],now - (int64_t)path["lastReceive"]);
-									bestPath = tmp;
-									break;
-								}
-							}
-						}
-						if (bestPath.length() == 0) bestPath = "-";
-						char ver[128];
-						int64_t vmaj = p["versionMajor"];
-						int64_t vmin = p["versionMinor"];
-						int64_t vrev = p["versionRev"];
-						if (vmaj >= 0) {
-							OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
-						} else {
-							ver[0] = '-';
-							ver[1] = (char)0;
-						}
-						printf("200 listpeers %s %s %d %s %s" ZT_EOL_S,
-							OSUtils::jsonString(p["address"],"-").c_str(),
-							bestPath.c_str(),
-							(int)OSUtils::jsonInt(p["latency"],0),
-							ver,
-							OSUtils::jsonString(p["role"],"-").c_str());
-					}
-				}
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "peers") {
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
-
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-
-		if (scode == 200) {
-			if (json) {
-				printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
-			} else {
-				printf("200 peers\n<ztaddr>   <ver>  <role> <lat> <link> <lastTX> <lastRX> <path>" ZT_EOL_S);
-				if (j.is_array()) {
-					for(unsigned long k=0;k<j.size();++k) {
-						nlohmann::json &p = j[k];
-						std::string bestPath;
-						nlohmann::json &paths = p["paths"];
-						if (paths.is_array()) {
-							for(unsigned long i=0;i<paths.size();++i) {
-								nlohmann::json &path = paths[i];
-								if (path["preferred"]) {
-									char tmp[256];
-									std::string addr = path["address"];
-									const int64_t now = OSUtils::now();
-									OSUtils::ztsnprintf(tmp,sizeof(tmp),"%-8lld %-8lld %s",now - (int64_t)path["lastSend"],now - (int64_t)path["lastReceive"],addr.c_str());
-									bestPath = std::string("DIRECT ") + tmp;
-									break;
-								}
-							}
-						}
-						if (bestPath.length() == 0) bestPath = "RELAY";
-						char ver[128];
-						int64_t vmaj = p["versionMajor"];
-						int64_t vmin = p["versionMinor"];
-						int64_t vrev = p["versionRev"];
-						if (vmaj >= 0) {
-							OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
-						} else {
-							ver[0] = '-';
-							ver[1] = (char)0;
-						}
-						printf("%s %-6s %-6s %5d %s" ZT_EOL_S,
-							OSUtils::jsonString(p["address"],"-").c_str(),
-							ver,
-							OSUtils::jsonString(p["role"],"-").c_str(),
-							(int)OSUtils::jsonInt(p["latency"],0),
-							bestPath.c_str());
-					}
-				}
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "listnetworks") {
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
-
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-
-		if (scode == 200) {
-			if (json) {
-				printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str());
-			} else {
-				printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
-				if (j.is_array()) {
-					for(unsigned long i=0;i<j.size();++i) {
-						nlohmann::json &n = j[i];
-						if (n.is_object()) {
-							std::string aa;
-							nlohmann::json &assignedAddresses = n["assignedAddresses"];
-							if (assignedAddresses.is_array()) {
-								for(unsigned long j=0;j<assignedAddresses.size();++j) {
-									nlohmann::json &addr = assignedAddresses[j];
-									if (addr.is_string()) {
-										if (aa.length() > 0) aa.push_back(',');
-										aa.append(addr.get<std::string>());
-									}
-								}
-							}
-							if (aa.length() == 0) aa = "-";
-							printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
-								OSUtils::jsonString(n["nwid"],"-").c_str(),
-								OSUtils::jsonString(n["name"],"-").c_str(),
-								OSUtils::jsonString(n["mac"],"-").c_str(),
-								OSUtils::jsonString(n["status"],"-").c_str(),
-								OSUtils::jsonString(n["type"],"-").c_str(),
-								OSUtils::jsonString(n["portDeviceName"],"-").c_str(),
-								aa.c_str());
-						}
-					}
-				}
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "join") {
-		if (arg1.length() != 16) {
-			printf("invalid network id" ZT_EOL_S);
-			return 2;
-		}
-		requestHeaders["Content-Type"] = "application/json";
-		requestHeaders["Content-Length"] = "2";
-		unsigned int scode = Http::POST(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			(std::string("/network/") + arg1).c_str(),
-			requestHeaders,
-			"{}",
-			2,
-			responseHeaders,
-			responseBody);
-		if (scode == 200) {
-			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-			} else {
-				printf("200 join OK" ZT_EOL_S);
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "leave") {
-		if (arg1.length() != 16) {
-			printf("invalid network id" ZT_EOL_S);
-			return 2;
-		}
-		unsigned int scode = Http::DEL(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			(std::string("/network/") + arg1).c_str(),
-			requestHeaders,
-			responseHeaders,
-			responseBody);
-		if (scode == 200) {
-			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-			} else {
-				printf("200 leave OK" ZT_EOL_S);
-			}
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else if (command == "set") {
-		if (arg1.length() != 16) {
-			fprintf(stderr,"invalid format: must be a 16-digit (network) ID\n");
-			return 2;
-		}
-		if (!arg2.length()) {
-			fprintf(stderr,"invalid format: include a property name to set\n");
-			return 2;
-		}
-		std::size_t eqidx = arg2.find('=');
-		if (eqidx != std::string::npos) {
-			if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) {
-				char jsons[1024];
-				OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"%s\":%s}",
-					arg2.substr(0,eqidx).c_str(),
-					(((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false"));
-				char cl[128];
-				OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons));
-				requestHeaders["Content-Type"] = "application/json";
-				requestHeaders["Content-Length"] = cl;
-				unsigned int scode = Http::POST(
-					1024 * 1024 * 16,
-					60000,
-					(const struct sockaddr *)&addr,
-					(std::string("/network/") + arg1).c_str(),
-					requestHeaders,
-					jsons,
-					(unsigned long)strlen(jsons),
-					responseHeaders,
-					responseBody);
-				if (scode == 200) {
-					printf("%s",cliFixJsonCRs(responseBody).c_str());
-					return 0;
-				} else {
-					printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-					return 1;
-				}
-			}
-		} else {
-			cliPrintHelp(argv[0],stderr);
-			return 2;
-		}
-	} else if (command == "get") {
-		if (arg1.length() != 16) {
-			fprintf(stderr,"invalid format: must be a 16-digit (network) ID\n");
-			return 2;
-		}
-		if (!arg2.length()) {
-			fprintf(stderr,"invalid format: include a property name to get\n");
-			return 2;
-		}
-		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
-		if (scode == 0) {
-			printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str());
-			return 1;
-		}
-		nlohmann::json j;
-		try {
-			j = OSUtils::jsonParse(responseBody);
-		} catch (std::exception &exc) {
-			printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what());
-			return 1;
-		} catch ( ... ) {
-			printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str());
-			return 1;
-		}
-		bool bNetworkFound = false;
-		if (j.is_array()) {
-			for(unsigned long i=0;i<j.size();++i) {
-				nlohmann::json &n = j[i];
-				if (n.is_object()) {
-					if (n["id"] == arg1) {
-						bNetworkFound = true;
-						std::string aa;
-						if (arg2 != "ip" && arg2 != "ip4" && arg2 != "ip6" && arg2 != "ip6plane" && arg2 != "ip6prefix") {
-							aa.append(OSUtils::jsonString(n[arg2],"-")); // Standard network property field
-							if (aa == "-") {
-								printf("error, unknown property name\n");
-								break;
-							}
-							printf("%s\n",aa.c_str());
-							break;
-						}
-						nlohmann::json &assignedAddresses = n["assignedAddresses"];
-						if (assignedAddresses.is_array()) {
-							int matchingIdxs[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
-							int addressCountOfType = 0;
-							for (int k = 0; k<std::min(ZT_MAX_ZT_ASSIGNED_ADDRESSES, (int)assignedAddresses.size());++k) {
-								nlohmann::json &addr = assignedAddresses[k];
-								if ((arg2 == "ip4" && addr.get<std::string>().find(".") != std::string::npos)
-									|| ((arg2.find("ip6") == 0) && addr.get<std::string>().find(":") != std::string::npos)
-									|| (arg2 == "ip")
-									) {
-									matchingIdxs[addressCountOfType++] = k;
-								}
-							}
-							for (int k=0; k<addressCountOfType; k++) {
-								nlohmann::json &addr = assignedAddresses[matchingIdxs[k]];
-								if (!addr.is_string()) {
-									continue;
-								}
-								if (arg2.find("ip6p") == 0) {
-									if (arg2 == "ip6plane") {
-										if (addr.get<std::string>().find("fc") == 0) {
-											aa.append(addr.get<std::string>().substr(0,addr.get<std::string>().find("/")));
-											if (k < addressCountOfType-1) aa.append("\n");
-										}
-									}
-									if (arg2 == "ip6prefix") {
-										if (addr.get<std::string>().find("fc") == 0) {
-											aa.append(addr.get<std::string>().substr(0,addr.get<std::string>().find("/")).substr(0,24));
-											if (k < addressCountOfType-1) aa.append("\n");
-										}
-									}
-								}
-								else {
-									aa.append(addr.get<std::string>().substr(0,addr.get<std::string>().find("/")));
-									if (k < addressCountOfType-1) aa.append("\n");
-								}
-							}
-						}
-						printf("%s\n",aa.c_str());
-					}
-				}
-			}
-		}
-		if (!bNetworkFound) {
-			fprintf(stderr,"unknown network ID, check that you are a member of the network\n");
-		}
-		if (scode == 200) {
-			return 0;
-		} else {
-			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
-			return 1;
-		}
-	} else {
-		cliPrintHelp(argv[0],stderr);
-		return 0;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-/* zerotier-idtool personality                                              */
-/****************************************************************************/
-
-static void idtoolPrintHelp(FILE *out,const char *pn)
-{
-	fprintf(out,
-		"%s version %d.%d.%d" ZT_EOL_S,
-		PROGRAM_NAME,
-		ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION);
-	fprintf(out,
-		COPYRIGHT_NOTICE ZT_EOL_S
-		LICENSE_GRANT ZT_EOL_S);
-	fprintf(out,"Usage: %s <command> [<args>]" ZT_EOL_S"" ZT_EOL_S"Commands:" ZT_EOL_S,pn);
-	fprintf(out,"  generate [<identity.secret>] [<identity.public>] [<vanity>]" ZT_EOL_S);
-	fprintf(out,"  validate <identity.secret/public>" ZT_EOL_S);
-	fprintf(out,"  getpublic <identity.secret>" ZT_EOL_S);
-	fprintf(out,"  sign <identity.secret> <file>" ZT_EOL_S);
-	fprintf(out,"  verify <identity.secret/public> <file> <signature>" ZT_EOL_S);
-}
-
-static Identity getIdFromArg(char *arg)
-{
-	Identity id;
-	if ((strlen(arg) > 32)&&(arg[10] == ':')) { // identity is a literal on the command line
-		if (id.fromString(arg))
-			return id;
-	} else { // identity is to be read from a file
-		std::string idser;
-		if (OSUtils::readFile(arg,idser)) {
-			if (id.fromString(idser.c_str()))
-				return id;
-		}
-	}
-	return Identity();
-}
-
-#ifdef __WINDOWS__
-static int idtool(int argc, _TCHAR* argv[])
-#else
-static int idtool(int argc,char **argv)
-#endif
-{
-	if (argc < 2) {
-		idtoolPrintHelp(stdout,argv[0]);
-		return 1;
-	}
-
-	if (!strcmp(argv[1],"generate")) {
-		uint64_t vanity = 0;
-		int vanityBits = 0;
-		if (argc >= 5) {
-			vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL;
-			vanityBits = 4 * (int)strlen(argv[4]);
-			if (vanityBits > 40)
-				vanityBits = 40;
-		}
-
-		Identity id;
-		for(;;) {
-			id.generate(Identity::C25519);
-			if ((id.address().toInt() >> (40 - vanityBits)) == vanity) {
-				if (vanityBits > 0) {
-					fprintf(stderr,"vanity address: found %.10llx !\n",(unsigned long long)id.address().toInt());
-				}
-				break;
-			} else {
-				fprintf(stderr,"vanity address: tried %.10llx looking for first %d bits of %.10llx\n",(unsigned long long)id.address().toInt(),vanityBits,(unsigned long long)(vanity << (40 - vanityBits)));
-			}
-		}
-
-		char idtmp[1024];
-		std::string idser = id.toString(true,idtmp);
-		if (argc >= 3) {
-			if (!OSUtils::writeFile(argv[2],idser)) {
-				fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[2]);
-				return 1;
-			} else printf("%s written" ZT_EOL_S,argv[2]);
-			if (argc >= 4) {
-				idser = id.toString(false,idtmp);
-				if (!OSUtils::writeFile(argv[3],idser)) {
-					fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]);
-					return 1;
-				} else printf("%s written" ZT_EOL_S,argv[3]);
-			}
-		} else printf("%s",idser.c_str());
-	} else if (!strcmp(argv[1],"validate")) {
-		if (argc < 3) {
-			idtoolPrintHelp(stdout,argv[0]);
-			return 1;
-		}
-
-		Identity id = getIdFromArg(argv[2]);
-		if (!id) {
-			fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
-			return 1;
-		}
-
-		if (!id.locallyValidate()) {
-			fprintf(stderr,"%s FAILED validation." ZT_EOL_S,argv[2]);
-			return 1;
-		} else printf("%s is a valid identity" ZT_EOL_S,argv[2]);
-	} else if (!strcmp(argv[1],"getpublic")) {
-		if (argc < 3) {
-			idtoolPrintHelp(stdout,argv[0]);
-			return 1;
-		}
-
-		Identity id = getIdFromArg(argv[2]);
-		if (!id) {
-			fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
-			return 1;
-		}
-
-		char idtmp[1024];
-		printf("%s",id.toString(false,idtmp));
-	} else if (!strcmp(argv[1],"sign")) {
-		if (argc < 4) {
-			idtoolPrintHelp(stdout,argv[0]);
-			return 1;
-		}
-
-		Identity id = getIdFromArg(argv[2]);
-		if (!id) {
-			fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
-			return 1;
-		}
-
-		if (!id.hasPrivate()) {
-			fprintf(stderr,"%s does not contain a private key (must use private to sign)" ZT_EOL_S,argv[2]);
-			return 1;
-		}
-
-		std::string inf;
-		if (!OSUtils::readFile(argv[3],inf)) {
-			fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]);
-			return 1;
-		}
-		uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE];
-		const unsigned int siglen = id.sign(inf.data(),(unsigned int)inf.length(),signature,sizeof(signature));
-		char hexbuf[256];
-		printf("%s",Utils::hex(signature,siglen,hexbuf));
-	} else if (!strcmp(argv[1],"verify")) {
-		if (argc < 5) {
-			idtoolPrintHelp(stdout,argv[0]);
-			return 1;
-		}
-
-		Identity id = getIdFromArg(argv[2]);
-		if (!id) {
-			fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]);
-			return 1;
-		}
-
-		std::string inf;
-		if (!OSUtils::readFile(argv[3],inf)) {
-			fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]);
-			return 1;
-		}
-
-		char buf[4096];
-		std::string signature(buf,Utils::unhex(argv[4],buf,(unsigned int)sizeof(buf)));
-		if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
-			printf("%s signature valid" ZT_EOL_S,argv[3]);
-		} else {
-			signature.clear();
-			if (OSUtils::readFile(argv[4],signature)) {
-				signature.assign(buf,Utils::unhex(signature.c_str(),buf,(unsigned int)sizeof(buf)));
-				if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) {
-					printf("%s signature valid" ZT_EOL_S,argv[3]);
-				} else {
-					fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
-					return 1;
-				}
-			} else {
-				fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]);
-				return 1;
-			}
-		}
-	} else {
-		idtoolPrintHelp(stdout,argv[0]);
-		return 1;
-	}
-
-	return 0;
-}
-
-/****************************************************************************/
-/* Unix helper functions and signal handlers                                */
-/****************************************************************************/
-
-#ifdef __UNIX_LIKE__
-static void _sighandlerHup(int sig)
-{
-}
-static void _sighandlerQuit(int sig)
-{
-	OneService *s = zt1Service;
-	if (s)
-		s->terminate();
-	else exit(0);
-}
-#endif
-
-// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system
-#if defined(__LINUX__) && !defined(ZT_NO_CAPABILITIES)
-#ifndef PR_CAP_AMBIENT
-#define PR_CAP_AMBIENT 47
-#define PR_CAP_AMBIENT_IS_SET 1
-#define PR_CAP_AMBIENT_RAISE 2
-#define PR_CAP_AMBIENT_LOWER 3
-#define PR_CAP_AMBIENT_CLEAR_ALL 4
-#endif
-#define ZT_LINUX_USER "zerotier-one"
-#define ZT_HAVE_DROP_PRIVILEGES 1
-namespace {
-
-// libc doesn't export capset, it is instead located in libcap
-// We ignore libcap and call it manually.
-struct cap_header_struct {
-	__u32 version;
-	int pid;
-};
-struct cap_data_struct {
-	__u32 effective;
-	__u32 permitted;
-	__u32 inheritable;
-};
-static inline int _zt_capset(cap_header_struct* hdrp, cap_data_struct* datap) { return syscall(SYS_capset, hdrp, datap); }
-
-static void _notDropping(const char *procName,const std::string &homeDir)
-{
-	struct stat buf;
-	if (lstat(homeDir.c_str(),&buf) < 0) {
-		if (buf.st_uid != 0 || buf.st_gid != 0) {
-			fprintf(stderr, "%s: FATAL: failed to drop privileges and can't run as root since privileges were previously dropped (home directory not owned by root)" ZT_EOL_S,procName);
-			exit(1);
-		}
-	}
-	fprintf(stderr, "%s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root" ZT_EOL_S,procName);
-}
-
-static int _setCapabilities(int flags)
-{
-	cap_header_struct capheader = {_LINUX_CAPABILITY_VERSION_1, 0};
-	cap_data_struct capdata;
-	capdata.inheritable = capdata.permitted = capdata.effective = flags;
-	return _zt_capset(&capheader, &capdata);
-}
-
-static void _recursiveChown(const char *path,uid_t uid,gid_t gid)
-{
-	struct dirent de;
-	struct dirent *dptr;
-	lchown(path,uid,gid);
-	DIR *d = opendir(path);
-	if (!d)
-		return;
-	dptr = (struct dirent *)0;
-	for(;;) {
-		if (readdir_r(d,&de,&dptr) != 0)
-			break;
-		if (!dptr)
-			break;
-		if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
-			std::string p(path);
-			p.push_back(ZT_PATH_SEPARATOR);
-			p.append(dptr->d_name);
-			_recursiveChown(p.c_str(),uid,gid); // will just fail and return on regular files
-		}
-	}
-	closedir(d);
-}
-
-static void dropPrivileges(const char *procName,const std::string &homeDir)
-{
-	if (getuid() != 0)
-		return;
-
-	// dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
-	// and CAP_NET_RAW capabilities.
-	struct passwd *targetUser = getpwnam(ZT_LINUX_USER);
-	if (!targetUser)
-		return;
-
-	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) {
-		// Kernel has no support for ambient capabilities.
-		_notDropping(procName,homeDir);
-		return;
-	}
-	if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NOROOT) < 0) {
-		_notDropping(procName,homeDir);
-		return;
-	}
-
-	// Change ownership of our home directory if everything looks good (does nothing if already chown'd)
-	_recursiveChown(homeDir.c_str(),targetUser->pw_uid,targetUser->pw_gid);
-
-	if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
-		_notDropping(procName,homeDir);
-		return;
-	}
-
-	int oldDumpable = prctl(PR_GET_DUMPABLE);
-	if (prctl(PR_SET_DUMPABLE, 0) < 0) {
-		// Disable ptracing. Otherwise there is a small window when previous
-		// compromised ZeroTier process could ptrace us, when we still have CAP_SETUID.
-		// (this is mitigated anyway on most distros by ptrace_scope=1)
-		fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
-		exit(1);
-	}
-
-	// Relinquish root
-	if (setgid(targetUser->pw_gid) < 0) {
-		perror("setgid");
-		exit(1);
-	}
-	if (setuid(targetUser->pw_uid) < 0) {
-		perror("setuid");
-		exit(1);
-	}
-
-	if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE)) < 0) {
-		fprintf(stderr,"%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S,procName);
-		exit(1);
-	}
-
-	if (prctl(PR_SET_DUMPABLE, oldDumpable) < 0) {
-		fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
-		exit(1);
-	}
-
-	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) < 0) {
-		fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_ADMIN) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
-		exit(1);
-	}
-	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0) < 0) {
-		fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_RAW) failed while attempting to relinquish root permissions" ZT_EOL_S,procName);
-		exit(1);
-	}
-}
-
-} // anonymous namespace
-#endif // __LINUX__
-
-/****************************************************************************/
-/* Windows helper functions and signal handlers                             */
-/****************************************************************************/
-
-#ifdef __WINDOWS__
-// Console signal handler routine to allow CTRL+C to work, mostly for testing
-static BOOL WINAPI _winConsoleCtrlHandler(DWORD dwCtrlType)
-{
-	switch(dwCtrlType) {
-		case CTRL_C_EVENT:
-		case CTRL_BREAK_EVENT:
-		case CTRL_CLOSE_EVENT:
-		case CTRL_SHUTDOWN_EVENT:
-			OneService *s = zt1Service;
-			if (s)
-				s->terminate();
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static void _winPokeAHole()
-{
-	char myPath[MAX_PATH];
-	DWORD ps = GetModuleFileNameA(NULL,myPath,sizeof(myPath));
-	if ((ps > 0)&&(ps < (DWORD)sizeof(myPath))) {
-		STARTUPINFOA startupInfo;
-		PROCESS_INFORMATION processInfo;
-
-		startupInfo.cb = sizeof(startupInfo);
-		memset(&startupInfo,0,sizeof(STARTUPINFOA));
-		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall delete rule name=\"ZeroTier One\" program=\"") + myPath + "\"").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
-			WaitForSingleObject(processInfo.hProcess,INFINITE);
-			CloseHandle(processInfo.hProcess);
-			CloseHandle(processInfo.hThread);
-		}
-
-		startupInfo.cb = sizeof(startupInfo);
-		memset(&startupInfo,0,sizeof(STARTUPINFOA));
-		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=in action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
-			WaitForSingleObject(processInfo.hProcess,INFINITE);
-			CloseHandle(processInfo.hProcess);
-			CloseHandle(processInfo.hThread);
-		}
-
-		startupInfo.cb = sizeof(startupInfo);
-		memset(&startupInfo,0,sizeof(STARTUPINFOA));
-		memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
-		if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=out action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
-			WaitForSingleObject(processInfo.hProcess,INFINITE);
-			CloseHandle(processInfo.hProcess);
-			CloseHandle(processInfo.hThread);
-		}
-	}
-}
-
-// Returns true if this is running as the local administrator
-static BOOL IsCurrentUserLocalAdministrator(void)
-{
-	BOOL   fReturn         = FALSE;
-	DWORD  dwStatus;
-	DWORD  dwAccessMask;
-	DWORD  dwAccessDesired;
-	DWORD  dwACLSize;
-	DWORD  dwStructureSize = sizeof(PRIVILEGE_SET);
-	PACL   pACL            = NULL;
-	PSID   psidAdmin       = NULL;
-
-	HANDLE hToken              = NULL;
-	HANDLE hImpersonationToken = NULL;
-
-	PRIVILEGE_SET   ps;
-	GENERIC_MAPPING GenericMapping;
-
-	PSECURITY_DESCRIPTOR     psdAdmin           = NULL;
-	SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
-
-	const DWORD ACCESS_READ  = 1;
-	const DWORD ACCESS_WRITE = 2;
-
-	__try
-	{
-		if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,TRUE,&hToken))
-		{
-			if (GetLastError() != ERROR_NO_TOKEN)
-				__leave;
-			if (!OpenProcessToken(GetCurrentProcess(),TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
-				__leave;
-		}
-		if (!DuplicateToken (hToken, SecurityImpersonation,&hImpersonationToken))
-			__leave;
-		if (!AllocateAndInitializeSid(&SystemSidAuthority, 2,
-			SECURITY_BUILTIN_DOMAIN_RID,
-			DOMAIN_ALIAS_RID_ADMINS,
-			0, 0, 0, 0, 0, 0, &psidAdmin))
-			__leave;
-		psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
-		if (psdAdmin == NULL)
-			__leave;
-		if (!InitializeSecurityDescriptor(psdAdmin,SECURITY_DESCRIPTOR_REVISION))
-			__leave;
-		dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD);
-		pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
-		if (pACL == NULL)
-			__leave;
-		if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
-			__leave;
-		dwAccessMask= ACCESS_READ | ACCESS_WRITE;
-		if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin))
-			__leave;
-		if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
-			__leave;
-
-		SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
-		SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
-
-		if (!IsValidSecurityDescriptor(psdAdmin))
-			__leave;
-		dwAccessDesired = ACCESS_READ;
-
-		GenericMapping.GenericRead    = ACCESS_READ;
-		GenericMapping.GenericWrite   = ACCESS_WRITE;
-		GenericMapping.GenericExecute = 0;
-		GenericMapping.GenericAll     = ACCESS_READ | ACCESS_WRITE;
-
-		if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
-			&GenericMapping, &ps, &dwStructureSize, &dwStatus,
-			&fReturn))
-		{
-			fReturn = FALSE;
-			__leave;
-		}
-	}
-	__finally
-	{
-		// Clean up.
-		if (pACL) LocalFree(pACL);
-		if (psdAdmin) LocalFree(psdAdmin);
-		if (psidAdmin) FreeSid(psidAdmin);
-		if (hImpersonationToken) CloseHandle (hImpersonationToken);
-		if (hToken) CloseHandle (hToken);
-	}
-
-	return fReturn;
-}
-#endif // __WINDOWS__
-
-/****************************************************************************/
-/* main() and friends                                                       */
-/****************************************************************************/
-
-static void printHelp(const char *cn,FILE *out)
-{
-	fprintf(out,
-		"%s version %d.%d.%d" ZT_EOL_S,
-		PROGRAM_NAME,
-		ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION);
-	fprintf(out,
-		COPYRIGHT_NOTICE ZT_EOL_S
-		LICENSE_GRANT ZT_EOL_S);
-	fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn);
-	fprintf(out,"Available switches:" ZT_EOL_S);
-	fprintf(out,"  -h                - Display this help" ZT_EOL_S);
-	fprintf(out,"  -v                - Show version" ZT_EOL_S);
-	fprintf(out,"  -U                - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S);
-	fprintf(out,"  -p<port>          - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S);
-
-#ifdef __UNIX_LIKE__
-	fprintf(out,"  -d                - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S);
-#endif // __UNIX_LIKE__
-
-#ifdef __WINDOWS__
-	fprintf(out,"  -C                - Run from command line instead of as service (Windows)" ZT_EOL_S);
-	fprintf(out,"  -I                - Install Windows service (Windows)" ZT_EOL_S);
-	fprintf(out,"  -R                - Uninstall Windows service (Windows)" ZT_EOL_S);
-	fprintf(out,"  -D                - Remove all instances of Windows tap device (Windows)" ZT_EOL_S);
-#endif // __WINDOWS__
-
-	fprintf(out,"  -i                - Generate and manage identities (zerotier-idtool)" ZT_EOL_S);
-	fprintf(out,"  -q                - Query API (zerotier-cli)" ZT_EOL_S);
-}
-
-class _OneServiceRunner
-{
-public:
-	_OneServiceRunner(const char *pn,const std::string &hd,unsigned int p) : progname(pn),returnValue(0),port(p),homeDir(hd) {}
-	void threadMain()
-		throw()
-	{
-		try {
-			for(;;) {
-				zt1Service = OneService::newInstance(homeDir.c_str(),port);
-				switch(zt1Service->run()) {
-					case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
-					case OneService::ONE_NORMAL_TERMINATION:
-						break;
-					case OneService::ONE_UNRECOVERABLE_ERROR:
-						fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,progname,zt1Service->fatalErrorMessage().c_str());
-						returnValue = 1;
-						break;
-					case OneService::ONE_IDENTITY_COLLISION: {
-						delete zt1Service;
-						zt1Service = (OneService *)0;
-						std::string oldid;
-						OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid);
-						if (oldid.length()) {
-							OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid);
-							OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str());
-							OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str());
-						}
-					}	continue; // restart!
-				}
-				break; // terminate loop -- normally we don't keep restarting
-			}
-
-			delete zt1Service;
-			zt1Service = (OneService *)0;
-		} catch ( ... ) {
-			fprintf(stderr,"%s: unexpected exception starting main OneService instance" ZT_EOL_S,progname);
-			returnValue = 1;
-		}
-	}
-	const char *progname;
-	unsigned int returnValue;
-	unsigned int port;
-	const std::string &homeDir;
-};
-
-#ifdef __WINDOWS__
-int __cdecl _tmain(int argc, _TCHAR* argv[])
-#else
-int main(int argc,char **argv)
-#endif
-{
-#ifdef __UNIX_LIKE__
-	signal(SIGHUP,&_sighandlerHup);
-	signal(SIGPIPE,SIG_IGN);
-	signal(SIGIO,SIG_IGN);
-	signal(SIGUSR1,SIG_IGN);
-	signal(SIGUSR2,SIG_IGN);
-	signal(SIGALRM,SIG_IGN);
-	signal(SIGINT,&_sighandlerQuit);
-	signal(SIGTERM,&_sighandlerQuit);
-	signal(SIGQUIT,&_sighandlerQuit);
-	signal(SIGINT,&_sighandlerQuit);
-
-	/* Ensure that there are no inherited file descriptors open from a previous
-	 * incarnation. This is a hack to ensure that GitHub issue #61 or variants
-	 * of it do not return, and should not do anything otherwise bad. */
-	{
-		int mfd = STDIN_FILENO;
-		if (STDOUT_FILENO > mfd) mfd = STDOUT_FILENO;
-		if (STDERR_FILENO > mfd) mfd = STDERR_FILENO;
-		for(int f=mfd+1;f<1024;++f)
-			::close(f);
-	}
-
-	bool runAsDaemon = false;
-#endif // __UNIX_LIKE__
-
-#ifdef __WINDOWS__
-	{
-		WSADATA wsaData;
-		WSAStartup(MAKEWORD(2,2),&wsaData);
-	}
-
-#ifdef ZT_WIN_RUN_IN_CONSOLE
-	bool winRunFromCommandLine = true;
-#else
-	bool winRunFromCommandLine = false;
-#endif
-#endif // __WINDOWS__
-
-	if ((strstr(argv[0],"zerotier-idtool"))||(strstr(argv[0],"ZEROTIER-IDTOOL")))
-		return idtool(argc,argv);
-	if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
-		return cli(argc,argv);
-
-	std::string homeDir;
-	unsigned int port = ZT_DEFAULT_PORT;
-	bool skipRootCheck = false;
-
-	for(int i=1;i<argc;++i) {
-		if (argv[i][0] == '-') {
-			switch(argv[i][1]) {
-
-				case 'p': // port -- for both UDP and TCP, packets and control plane
-					port = Utils::strToUInt(argv[i] + 2);
-					if (port > 0xffff) {
-						printHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
-#ifdef __UNIX_LIKE__
-				case 'd': // Run in background as daemon
-					runAsDaemon = true;
-					break;
-#endif // __UNIX_LIKE__
-
-				case 'U':
-					skipRootCheck = true;
-					break;
-
-				case 'v': // Display version
-					printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
-					return 0;
-
-				case 'i': // Invoke idtool personality
-					if (argv[i][2]) {
-						printHelp(argv[0],stdout);
-						return 0;
-					} else return idtool(argc-1,argv+1);
-
-				case 'q': // Invoke cli personality
-					if (argv[i][2]) {
-						printHelp(argv[0],stdout);
-						return 0;
-					} else return cli(argc,argv);
-
-#ifdef __WINDOWS__
-				case 'C': // Run from command line instead of as Windows service
-					winRunFromCommandLine = true;
-					break;
-
-				case 'I': { // Install this binary as a Windows service
-						if (IsCurrentUserLocalAdministrator() != TRUE) {
-							fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
-							return 1;
-						}
-						std::string ret(InstallService(ZT_SERVICE_NAME,ZT_SERVICE_DISPLAY_NAME,ZT_SERVICE_START_TYPE,ZT_SERVICE_DEPENDENCIES,ZT_SERVICE_ACCOUNT,ZT_SERVICE_PASSWORD));
-						if (ret.length()) {
-							fprintf(stderr,"%s: unable to install service: %s" ZT_EOL_S,argv[0],ret.c_str());
-							return 3;
-						}
-						return 0;
-					} break;
-
-				case 'R': { // Uninstall this binary as Windows service
-						if (IsCurrentUserLocalAdministrator() != TRUE) {
-							fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
-							return 1;
-						}
-						std::string ret(UninstallService(ZT_SERVICE_NAME));
-						if (ret.length()) {
-							fprintf(stderr,"%s: unable to uninstall service: %s" ZT_EOL_S,argv[0],ret.c_str());
-							return 3;
-						}
-						return 0;
-					} break;
-
-				case 'D': {
-						std::string err = WindowsEthernetTap::destroyAllPersistentTapDevices();
-						if (err.length() > 0) {
-							fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s" ZT_EOL_S,argv[0],err.c_str());
-							return 3;
-						}
-						return 0;
-					} break;
-#endif // __WINDOWS__
-
-				case 'h':
-				case '?':
-				default:
-					printHelp(argv[0],stdout);
-					return 0;
-			}
-		} else {
-			if (homeDir.length()) {
-				printHelp(argv[0],stdout);
-				return 0;
-			} else {
-				homeDir = argv[i];
-			}
-		}
-	}
-
-	if (!homeDir.length())
-		homeDir = OneService::platformDefaultHomePath();
-	if (!homeDir.length()) {
-		fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]);
-		return 1;
-	} else {
-		std::vector<std::string> hpsp(OSUtils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
-		std::string ptmp;
-		if (homeDir[0] == ZT_PATH_SEPARATOR)
-			ptmp.push_back(ZT_PATH_SEPARATOR);
-		for(std::vector<std::string>::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) {
-			if (ptmp.length() > 0)
-				ptmp.push_back(ZT_PATH_SEPARATOR);
-			ptmp.append(*pi);
-			if ((*pi != ".")&&(*pi != "..")) {
-				if (!OSUtils::mkdir(ptmp))
-					throw std::runtime_error("home path does not exist, and could not create");
-			}
-		}
-	}
-
-	// This can be removed once the new controller code has been around for many versions
-	if (OSUtils::fileExists((homeDir + ZT_PATH_SEPARATOR_S + "controller.db").c_str(),true)) {
-		fprintf(stderr,"%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S,argv[0],homeDir.c_str());
-		return 1;
-	}
-
-#ifdef __UNIX_LIKE__
-#ifndef ZT_ONE_NO_ROOT_CHECK
-	if ((!skipRootCheck)&&(getuid() != 0)) {
-		fprintf(stderr,"%s: must be run as root (uid 0)" ZT_EOL_S,argv[0]);
-		return 1;
-	}
-#endif // !ZT_ONE_NO_ROOT_CHECK
-	if (runAsDaemon) {
-		long p = (long)fork();
-		if (p < 0) {
-			fprintf(stderr,"%s: could not fork" ZT_EOL_S,argv[0]);
-			return 1;
-		} else if (p > 0)
-			return 0; // forked
-		// else p == 0, so we are daemonized
-	}
-#endif // __UNIX_LIKE__
-
-#ifdef __WINDOWS__
-	// Uninstall legacy tap devices. New devices will automatically be installed and configured
-	// when tap instances are created.
-	WindowsEthernetTap::destroyAllLegacyPersistentTapDevices();
-
-	if (winRunFromCommandLine) {
-		// Running in "interactive" mode (mostly for debugging)
-		if (IsCurrentUserLocalAdministrator() != TRUE) {
-			if (!skipRootCheck) {
-				fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]);
-				return 1;
-			}
-		} else {
-			_winPokeAHole();
-		}
-		SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE);
-		// continues on to ordinary command line execution code below...
-	} else {
-		// Running from service manager
-		_winPokeAHole();
-		ZeroTierOneService zt1WindowsService;
-		if (CServiceBase::Run(zt1WindowsService) == TRUE) {
-			return 0;
-		} else {
-			fprintf(stderr,"%s: unable to start service (try -h for help)" ZT_EOL_S,argv[0]);
-			return 1;
-		}
-	}
-#endif // __WINDOWS__
-
-#ifdef __UNIX_LIKE__
-#ifdef ZT_HAVE_DROP_PRIVILEGES
-	if (!skipRootCheck)
-		dropPrivileges(argv[0],homeDir);
-#endif
-
-	std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH);
-	{
-		// Write .pid file to home folder
-		FILE *pf = fopen(pidPath.c_str(),"w");
-		if (pf) {
-			fprintf(pf,"%ld",(long)getpid());
-			fclose(pf);
-		}
-	}
-#endif // __UNIX_LIKE__
-
-	_OneServiceRunner thr(argv[0],homeDir,port);
-	thr.threadMain();
-	//Thread::join(Thread::start(&thr));
-
-#ifdef __UNIX_LIKE__
-	OSUtils::rm(pidPath.c_str());
-#endif
-
-	return thr.returnValue;
-}