Kaynağa Gözat

Merge branch 'edge' of http://git.int.zerotier.com/zerotier/ZeroTierOne into edge

Grant Limberg 5 yıl önce
ebeveyn
işleme
8e1a88c2fb
100 değiştirilmiş dosya ile 6273 ekleme ve 8012 silme
  1. 5 1
      .gitignore
  2. 0 3
      .idea/.gitignore
  3. 0 9
      .idea/ZeroTierOne.iml
  4. 0 5
      .idea/codeStyles/codeStyleConfig.xml
  5. 0 11
      .idea/dictionaries/api.xml
  6. 0 10
      .idea/inspectionProfiles/Project_Default.xml
  7. 0 6
      .idea/misc.xml
  8. 0 8
      .idea/modules.xml
  9. 0 6
      .idea/vcs.xml
  10. 0 29
      .idea/watcherTasks.xml
  11. 28 53
      CMakeLists.txt
  12. 0 461
      attic/Binder.hpp
  13. 0 287
      attic/Http.cpp
  14. 0 182
      attic/Http.hpp
  15. 0 1177
      attic/Phy.hpp
  16. 0 14
      attic/PortMapper-libnatpmp.c
  17. 0 41
      attic/PortMapper-miniupnpc.c
  18. 0 334
      attic/PortMapper.cpp
  19. 0 62
      attic/PortMapper.hpp
  20. 0 182
      attic/Root.hpp
  21. 0 18
      attic/linux-old-glibc-compat.c
  22. 0 30
      attic/listaddrinfo.go
  23. 0 1542
      attic/one.cpp
  24. 111 0
      attic/webview/.clang-format
  25. 256 0
      attic/webview/.clang-tidy
  26. 1 0
      attic/webview/.gitattributes
  27. 7 0
      attic/webview/.gitignore
  28. 19 0
      attic/webview/.travis.yml
  29. 21 0
      attic/webview/LICENSE
  30. 28 0
      attic/webview/Makefile
  31. 39 0
      attic/webview/example.cc
  32. 15 0
      attic/webview/example/example.go
  33. 3 0
      attic/webview/go.mod
  34. 1 0
      attic/webview/webview.cc
  35. 138 0
      attic/webview/webview.go
  36. 1248 0
      attic/webview/webview.h
  37. 38 0
      attic/webview/webview_test.cc
  38. 2 0
      controller/EmbeddedNetworkController.cpp
  39. 3 3
      controller/LFDB.cpp
  40. 0 47
      go/cmd/zerotier/cli/addroot.go
  41. 2 6
      go/cmd/zerotier/cli/help.go
  42. 0 137
      go/cmd/zerotier/cli/locator.go
  43. 0 45
      go/cmd/zerotier/cli/roots.go
  44. 1 0
      go/cmd/zerotier/cli/service.go
  45. 0 2
      go/cmd/zerotier/zerotier.go
  46. 1 0
      go/go.mod
  47. 2 0
      go/go.sum
  48. 7 162
      go/native/GoGlue.cpp
  49. 9 74
      go/native/GoGlue.h
  50. 4 47
      go/pkg/zerotier/api.go
  51. 42 13
      go/pkg/zerotier/identity.go
  52. 1 1
      go/pkg/zerotier/localconfig.go
  53. 0 191
      go/pkg/zerotier/locator.go
  54. 8 136
      go/pkg/zerotier/node.go
  55. 0 9
      go/pkg/zerotier/path.go
  56. 4 16
      go/pkg/zerotier/root.go
  57. 0 13
      go/vendor/golang.org/x/sys/windows/asm_windows_386.s
  58. 0 13
      go/vendor/golang.org/x/sys/windows/asm_windows_amd64.s
  59. 0 11
      go/vendor/golang.org/x/sys/windows/asm_windows_arm.s
  60. 15 7
      go/vendor/golang.org/x/sys/windows/dll_windows.go
  61. 3 4
      go/vendor/golang.org/x/sys/windows/empty.s
  62. 2 3
      go/vendor/golang.org/x/sys/windows/mkerrors.bash
  63. 27 0
      go/vendor/golang.org/x/sys/windows/mkknownfolderids.bash
  64. 1 1
      go/vendor/golang.org/x/sys/windows/mksyscall.go
  65. 583 31
      go/vendor/golang.org/x/sys/windows/security_windows.go
  66. 11 0
      go/vendor/golang.org/x/sys/windows/service.go
  67. 228 10
      go/vendor/golang.org/x/sys/windows/syscall_windows.go
  68. 203 16
      go/vendor/golang.org/x/sys/windows/types_windows.go
  69. 1 1
      go/vendor/golang.org/x/sys/windows/zerrors_windows.go
  70. 149 0
      go/vendor/golang.org/x/sys/windows/zknownfolderids_windows.go
  71. 574 291
      go/vendor/golang.org/x/sys/windows/zsyscall_windows.go
  72. 1 1
      go/vendor/modules.txt
  73. 159 400
      include/ZeroTierCore.h
  74. 0 168
      node/AES-aesni.c
  75. 3 29
      node/AES.cpp
  76. 159 59
      node/AES.hpp
  77. 98 17
      node/Address.hpp
  78. 10 5
      node/AtomicCounter.hpp
  79. 116 0
      node/Buf.cpp
  80. 472 0
      node/Buf.hpp
  81. 46 54
      node/C25519.cpp
  82. 2 2
      node/C25519.hpp
  83. 5 16
      node/CMakeLists.txt
  84. 15 15
      node/Capability.hpp
  85. 17 17
      node/CertificateOfMembership.hpp
  86. 23 23
      node/CertificateOfOwnership.hpp
  87. 22 432
      node/Constants.hpp
  88. 1 1
      node/Credential.cpp
  89. 4 4
      node/Credential.hpp
  90. 23 23
      node/Dictionary.hpp
  91. 6 7
      node/ECC384.cpp
  92. 248 0
      node/Endpoint.hpp
  93. 16 16
      node/Hashtable.hpp
  94. 212 8
      node/Identity.cpp
  95. 129 174
      node/Identity.hpp
  96. 332 594
      node/IncomingPacket.cpp
  97. 1 34
      node/IncomingPacket.hpp
  98. 61 63
      node/InetAddress.cpp
  99. 145 89
      node/InetAddress.hpp
  100. 106 0
      node/Locator.cpp

+ 5 - 1
.gitignore

@@ -1,5 +1,9 @@
-build/
+/build
+/cmake-build-debug
+/cmake-build-release
 /version.h
+/.idea
+/go/.idea
 .DS_Store
 .Trashes
 *.swp

+ 0 - 3
.idea/.gitignore

@@ -1,3 +0,0 @@
-
-# Default ignored files
-/workspace.xml

+ 0 - 9
.idea/ZeroTierOne.iml

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module type="WEB_MODULE" version="4">
-  <component name="Go" enabled="true" />
-  <component name="NewModuleRootManager">
-    <content url="file://$MODULE_DIR$" />
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>

+ 0 - 5
.idea/codeStyles/codeStyleConfig.xml

@@ -1,5 +0,0 @@
-<component name="ProjectCodeStyleConfiguration">
-  <state>
-    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
-  </state>
-</component>

+ 0 - 11
.idea/dictionaries/api.xml

@@ -1,11 +0,0 @@
-<component name="ProjectDictionaryState">
-  <dictionary name="api">
-    <words>
-      <w>apisocket</w>
-      <w>nwid</w>
-      <w>secrand</w>
-      <w>sockaddr</w>
-      <w>unmarshals</w>
-    </words>
-  </dictionary>
-</component>

+ 0 - 10
.idea/inspectionProfiles/Project_Default.xml

@@ -1,10 +0,0 @@
-<component name="InspectionProjectProfileManager">
-  <profile version="1.0">
-    <option name="myName" value="Project Default" />
-    <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
-      <option name="processCode" value="true" />
-      <option name="processLiterals" value="true" />
-      <option name="processComments" value="true" />
-    </inspection_tool>
-  </profile>
-</component>

+ 0 - 6
.idea/misc.xml

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="JavaScriptSettings">
-    <option name="languageLevel" value="ES6" />
-  </component>
-</project>

+ 0 - 8
.idea/modules.xml

@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ProjectModuleManager">
-    <modules>
-      <module fileurl="file://$PROJECT_DIR$/.idea/ZeroTierOne.iml" filepath="$PROJECT_DIR$/.idea/ZeroTierOne.iml" />
-    </modules>
-  </component>
-</project>

+ 0 - 6
.idea/vcs.xml

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="VcsDirectoryMappings">
-    <mapping directory="$PROJECT_DIR$" vcs="Git" />
-  </component>
-</project>

+ 0 - 29
.idea/watcherTasks.xml

@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="ProjectTasksOptions">
-    <TaskOptions isEnabled="true">
-      <option name="arguments" value="fmt $FilePath$" />
-      <option name="checkSyntaxErrors" value="true" />
-      <option name="description" />
-      <option name="exitCodeBehavior" value="ERROR" />
-      <option name="fileExtension" value="go" />
-      <option name="immediateSync" value="false" />
-      <option name="name" value="go fmt" />
-      <option name="output" value="$FilePath$" />
-      <option name="outputFilters">
-        <array />
-      </option>
-      <option name="outputFromStdout" value="false" />
-      <option name="program" value="$GoExecPath$" />
-      <option name="runOnExternalChanges" value="false" />
-      <option name="scopeName" value="Project Files" />
-      <option name="trackOnlyRoot" value="true" />
-      <option name="workingDir" value="$ProjectFileDir$" />
-      <envs>
-        <env name="GOROOT" value="$GOROOT$" />
-        <env name="GOPATH" value="$GOPATH$" />
-        <env name="PATH" value="$GoBinDirs$" />
-      </envs>
-    </TaskOptions>
-  </component>
-</project>

+ 28 - 53
CMakeLists.txt

@@ -1,4 +1,5 @@
 cmake_minimum_required (VERSION 3.8)
+project(zerotier DESCRIPTION "ZeroTier Network Hypervisor" LANGUAGES CXX C)
 
 if(${CMAKE_VERSION} VERSION_LESS 3.15)
 	cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
@@ -7,33 +8,32 @@ else()
 endif()
 
 if(WIN32)
-	# If building on Windows, set minimum target to Windows 7
 	set(CMAKE_SYSTEM_VERSION "7" CACHE STRING INTERNAL FORCE)
 endif(WIN32)
+set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version")
 
 set(ZEROTIER_ONE_VERSION_MAJOR 2 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_MINOR 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_REVISION 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_BUILD 0 CACHE INTERNAL "")
 
+configure_file(
+	${CMAKE_SOURCE_DIR}/version.h.in
+	${CMAKE_BINARY_DIR}/version.h
+)
+
 set(default_build_type "Release")
-if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
-	set(default_build_type "Debug")
-endif()
+#if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
+#	set(default_build_type "Debug")
+#endif()
 
 if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
 	message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
-	set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
-		STRING "Choose the type of build." FORCE)
-	# Set the possible values of build type for cmake-gui
-	set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
-		"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+	set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE)
+	set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
 endif()
 
 option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF)
-option(ZT_TRACE "Trace Messages" OFF)
-option(ZT_DEBUG_TRACE "Debug Trace Messages" OFF)
-
 if (BUILD_CENTRAL_CONTROLLER)
 	find_package(PostgreSQL REQUIRED)
 	set(ENABLE_SSL_SUPPORT OFF)
@@ -45,25 +45,21 @@ if (BUILD_CENTRAL_CONTROLLER)
 	add_subdirectory("ext/librabbitmq")
 endif(BUILD_CENTRAL_CONTROLLER)
 
-set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version")
-
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 	add_definitions(-DZT_TRACE)
 endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
 
-project(zerotier
-	DESCRIPTION "ZeroTier Network Hypervisor"
-	LANGUAGES CXX C)
-
 if(WIN32)
+	message("++ Setting Windows Compiler Flags ${CMAKE_BUILD_TYPE}")
 	add_definitions(-DNOMINMAX)
 else(WIN32)
 	if(APPLE)
 
-		message("Setting macOS Compiler Flags ${CMAKE_BUILD_TYPE}")
+		message("++ Setting MacOS Compiler Flags ${CMAKE_BUILD_TYPE}")
 		add_compile_options(
 			-Wall
 			-Wno-deprecated
+			-Wno-unused-function
 			-mmacosx-version-min=10.9
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:DEBUG>:-O0>
@@ -79,17 +75,13 @@ else(WIN32)
 			$<$<CONFIG:RELEASE>:-flto>
 		)
 
-	elseif (
-		CMAKE_SYSTEM_NAME MATCHES "Linux" OR
-		CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR
-		CMAKE_SYSTEM_NAME MATCHES "OpenBSD" OR
-		CMAKE_SYSTEM_NAME MATCHES "NetBSD"
-	)
+	else(APPLE)
 
-		message("Setting Linux/BSD Compiler Flags (${CMAKE_BUILD_TYPE})")
+		message("++ Setting Linux/BSD/Posix Compiler Flags (${CMAKE_BUILD_TYPE})")
 		add_compile_options(
 			-Wall
 			-Wno-deprecated
+			-Wno-unused-function
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:DEBUG>:-O0>
 			$<$<CONFIG:RELEASE>:-O3>
@@ -110,10 +102,9 @@ if (
 	CMAKE_SYSTEM_PROCESSOR MATCHES "i586" OR
 	CMAKE_SYSTEM_PROCESSOR MATCHES "i686"
 )
-	message("Adding SSE and AES-NI flags for processor ${CMAKE_SYSTEM_PROCESSOR}")
+	message("++ Adding SSE and AES-NI flags for processor ${CMAKE_SYSTEM_PROCESSOR}")
 	add_compile_options(
 		-maes
-		-mmmx
 		-mrdrnd
 		-mpclmul
 		-msse
@@ -126,9 +117,6 @@ endif()
 if(ZT_TRACE)
 	add_definitions(-DZT_TRACE)
 endif()
-if(ZT_DEBUG_TRACE)
-	add_definitions(-DZT_DEBUG_TRACE)
-endif()
 
 add_subdirectory(node)
 add_subdirectory(controller)
@@ -136,12 +124,6 @@ add_subdirectory(osdep)
 add_subdirectory(root)
 add_subdirectory(go/native)
 
-#if(WIN32)
-#	add_subdirectory("windows/WinUI")
-#	add_subdirectory("windows/copyutil")
-#	add_definitions(-DNOMINMAX)
-#endif(WIN32)
-
 set(
 	zt_osdep
 	zt_core
@@ -149,25 +131,18 @@ set(
 	zt_go_native
 )
 
-configure_file(
-	${CMAKE_SOURCE_DIR}/version.h.in
-	${CMAKE_BINARY_DIR}/version.h
-)
-
-#set(src
-#	one.cpp
-#	"ext/http-parser/http_parser.c"
-#)
-#set(headers
-#	"ext/http-parser/http_parser.h"
-#)
-
 if(WIN32)
 	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 else(WIN32)
 	set(libs ${libs} pthread)
 endif(WIN32)
 
+#if(WIN32)
+#	add_subdirectory("windows/WinUI")
+#	add_subdirectory("windows/copyutil")
+#	add_definitions(-DNOMINMAX)
+#endif(WIN32)
+
 #if(WIN32)
 #	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 #	set(src
@@ -203,6 +178,6 @@ add_custom_command(
 )
 add_custom_target(build_zerotier ALL DEPENDS zerotier)
 
-add_executable(zerotier-selftest selftest.cpp)
-target_link_libraries(zerotier-selftest ${libs} zt_core zt_osdep)
-target_compile_features(zerotier-selftest PUBLIC cxx_std_11)
+#add_executable(zerotier-selftest selftest.cpp)
+#target_link_libraries(zerotier-selftest ${libs} zt_core zt_osdep)
+#target_compile_features(zerotier-selftest PUBLIC cxx_std_11)

+ 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;
-}

+ 111 - 0
attic/webview/.clang-format

@@ -0,0 +1,111 @@
+---
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: false
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:   
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  AfterExternBlock: false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+  SplitEmptyFunction: true
+  SplitEmptyRecord: true
+  SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeInheritanceComma: false
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeColon
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:   
+  - foreach
+  - Q_FOREACH
+  - BOOST_FOREACH
+IncludeBlocks:   Preserve
+IncludeCategories: 
+  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
+    Priority:        2
+  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
+    Priority:        3
+  - Regex:           '.*'
+    Priority:        1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth:     2
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Cpp11
+TabWidth:        8
+UseTab:          Never
+...
+

+ 256 - 0
attic/webview/.clang-tidy

@@ -0,0 +1,256 @@
+---
+Checks:          'clang-diagnostic-*,clang-analyzer-*,*'
+HeaderFilterRegex: ''
+AnalyzeTemporaryDtors: false
+User:            serge
+CheckOptions:    
+  - key:             bugprone-argument-comment.StrictMode
+    value:           '0'
+  - key:             bugprone-assert-side-effect.AssertMacros
+    value:           assert
+  - key:             bugprone-assert-side-effect.CheckFunctionCalls
+    value:           '0'
+  - key:             bugprone-dangling-handle.HandleClasses
+    value:           'std::basic_string_view;std::experimental::basic_string_view'
+  - key:             bugprone-string-constructor.LargeLengthThreshold
+    value:           '8388608'
+  - key:             bugprone-string-constructor.WarnOnLargeLength
+    value:           '1'
+  - key:             cert-dcl59-cpp.HeaderFileExtensions
+    value:           ',h,hh,hpp,hxx'
+  - key:             cert-err09-cpp.CheckThrowTemporaries
+    value:           '1'
+  - key:             cert-err61-cpp.CheckThrowTemporaries
+    value:           '1'
+  - key:             cert-oop11-cpp.IncludeStyle
+    value:           llvm
+  - key:             cppcoreguidelines-no-malloc.Allocations
+    value:           '::malloc;::calloc'
+  - key:             cppcoreguidelines-no-malloc.Deallocations
+    value:           '::free'
+  - key:             cppcoreguidelines-no-malloc.Reallocations
+    value:           '::realloc'
+  - key:             cppcoreguidelines-owning-memory.LegacyResourceConsumers
+    value:           '::free;::realloc;::freopen;::fclose'
+  - key:             cppcoreguidelines-owning-memory.LegacyResourceProducers
+    value:           '::malloc;::aligned_alloc;::realloc;::calloc;::fopen;::freopen;::tmpfile'
+  - key:             cppcoreguidelines-pro-bounds-constant-array-index.GslHeader
+    value:           ''
+  - key:             cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle
+    value:           '0'
+  - key:             cppcoreguidelines-pro-type-member-init.IgnoreArrays
+    value:           '0'
+  - key:             cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
+    value:           '0'
+  - key:             cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
+    value:           '0'
+  - key:             google-build-namespaces.HeaderFileExtensions
+    value:           ',h,hh,hpp,hxx'
+  - key:             google-global-names-in-headers.HeaderFileExtensions
+    value:           ',h,hh,hpp,hxx'
+  - key:             google-readability-braces-around-statements.ShortStatementLines
+    value:           '1'
+  - key:             google-readability-function-size.BranchThreshold
+    value:           '4294967295'
+  - key:             google-readability-function-size.LineThreshold
+    value:           '4294967295'
+  - key:             google-readability-function-size.NestingThreshold
+    value:           '4294967295'
+  - key:             google-readability-function-size.ParameterThreshold
+    value:           '4294967295'
+  - key:             google-readability-function-size.StatementThreshold
+    value:           '800'
+  - key:             google-readability-namespace-comments.ShortNamespaceLines
+    value:           '10'
+  - key:             google-readability-namespace-comments.SpacesBeforeComments
+    value:           '2'
+  - key:             google-runtime-int.SignedTypePrefix
+    value:           int
+  - key:             google-runtime-int.TypeSuffix
+    value:           ''
+  - key:             google-runtime-int.UnsignedTypePrefix
+    value:           uint
+  - key:             google-runtime-references.WhiteListTypes
+    value:           ''
+  - key:             hicpp-braces-around-statements.ShortStatementLines
+    value:           '0'
+  - key:             hicpp-function-size.BranchThreshold
+    value:           '4294967295'
+  - key:             hicpp-function-size.LineThreshold
+    value:           '4294967295'
+  - key:             hicpp-function-size.NestingThreshold
+    value:           '4294967295'
+  - key:             hicpp-function-size.ParameterThreshold
+    value:           '4294967295'
+  - key:             hicpp-function-size.StatementThreshold
+    value:           '800'
+  - key:             hicpp-member-init.IgnoreArrays
+    value:           '0'
+  - key:             hicpp-move-const-arg.CheckTriviallyCopyableMove
+    value:           '1'
+  - key:             hicpp-named-parameter.IgnoreFailedSplit
+    value:           '0'
+  - key:             hicpp-no-malloc.Allocations
+    value:           '::malloc;::calloc'
+  - key:             hicpp-no-malloc.Deallocations
+    value:           '::free'
+  - key:             hicpp-no-malloc.Reallocations
+    value:           '::realloc'
+  - key:             hicpp-special-member-functions.AllowMissingMoveFunctions
+    value:           '0'
+  - key:             hicpp-special-member-functions.AllowSoleDefaultDtor
+    value:           '0'
+  - key:             hicpp-use-auto.RemoveStars
+    value:           '0'
+  - key:             hicpp-use-emplace.ContainersWithPushBack
+    value:           '::std::vector;::std::list;::std::deque'
+  - key:             hicpp-use-emplace.SmartPointers
+    value:           '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
+  - key:             hicpp-use-emplace.TupleMakeFunctions
+    value:           '::std::make_pair;::std::make_tuple'
+  - key:             hicpp-use-emplace.TupleTypes
+    value:           '::std::pair;::std::tuple'
+  - key:             hicpp-use-equals-default.IgnoreMacros
+    value:           '1'
+  - key:             hicpp-use-noexcept.ReplacementString
+    value:           ''
+  - key:             hicpp-use-noexcept.UseNoexceptFalse
+    value:           '1'
+  - key:             hicpp-use-nullptr.NullMacros
+    value:           ''
+  - key:             llvm-namespace-comment.ShortNamespaceLines
+    value:           '1'
+  - key:             llvm-namespace-comment.SpacesBeforeComments
+    value:           '1'
+  - key:             misc-definitions-in-headers.HeaderFileExtensions
+    value:           ',h,hh,hpp,hxx'
+  - key:             misc-definitions-in-headers.UseHeaderFileExtension
+    value:           '1'
+  - key:             misc-misplaced-widening-cast.CheckImplicitCasts
+    value:           '0'
+  - key:             misc-sizeof-expression.WarnOnSizeOfCompareToConstant
+    value:           '1'
+  - key:             misc-sizeof-expression.WarnOnSizeOfConstant
+    value:           '1'
+  - key:             misc-sizeof-expression.WarnOnSizeOfThis
+    value:           '1'
+  - key:             misc-suspicious-enum-usage.StrictMode
+    value:           '0'
+  - key:             misc-suspicious-missing-comma.MaxConcatenatedTokens
+    value:           '5'
+  - key:             misc-suspicious-missing-comma.RatioThreshold
+    value:           '0.200000'
+  - key:             misc-suspicious-missing-comma.SizeThreshold
+    value:           '5'
+  - key:             misc-suspicious-string-compare.StringCompareLikeFunctions
+    value:           ''
+  - key:             misc-suspicious-string-compare.WarnOnImplicitComparison
+    value:           '1'
+  - key:             misc-suspicious-string-compare.WarnOnLogicalNotComparison
+    value:           '0'
+  - key:             misc-throw-by-value-catch-by-reference.CheckThrowTemporaries
+    value:           '1'
+  - key:             modernize-loop-convert.MaxCopySize
+    value:           '16'
+  - key:             modernize-loop-convert.MinConfidence
+    value:           reasonable
+  - key:             modernize-loop-convert.NamingStyle
+    value:           CamelCase
+  - key:             modernize-make-shared.IgnoreMacros
+    value:           '1'
+  - key:             modernize-make-shared.IncludeStyle
+    value:           '0'
+  - key:             modernize-make-shared.MakeSmartPtrFunction
+    value:           'std::make_shared'
+  - key:             modernize-make-shared.MakeSmartPtrFunctionHeader
+    value:           memory
+  - key:             modernize-make-unique.IgnoreMacros
+    value:           '1'
+  - key:             modernize-make-unique.IncludeStyle
+    value:           '0'
+  - key:             modernize-make-unique.MakeSmartPtrFunction
+    value:           'std::make_unique'
+  - key:             modernize-make-unique.MakeSmartPtrFunctionHeader
+    value:           memory
+  - key:             modernize-pass-by-value.IncludeStyle
+    value:           llvm
+  - key:             modernize-pass-by-value.ValuesOnly
+    value:           '0'
+  - key:             modernize-raw-string-literal.ReplaceShorterLiterals
+    value:           '0'
+  - key:             modernize-replace-auto-ptr.IncludeStyle
+    value:           llvm
+  - key:             modernize-replace-random-shuffle.IncludeStyle
+    value:           llvm
+  - key:             modernize-use-auto.RemoveStars
+    value:           '0'
+  - key:             modernize-use-default-member-init.IgnoreMacros
+    value:           '1'
+  - key:             modernize-use-default-member-init.UseAssignment
+    value:           '0'
+  - key:             modernize-use-emplace.ContainersWithPushBack
+    value:           '::std::vector;::std::list;::std::deque'
+  - key:             modernize-use-emplace.SmartPointers
+    value:           '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
+  - key:             modernize-use-emplace.TupleMakeFunctions
+    value:           '::std::make_pair;::std::make_tuple'
+  - key:             modernize-use-emplace.TupleTypes
+    value:           '::std::pair;::std::tuple'
+  - key:             modernize-use-equals-default.IgnoreMacros
+    value:           '1'
+  - key:             modernize-use-noexcept.ReplacementString
+    value:           ''
+  - key:             modernize-use-noexcept.UseNoexceptFalse
+    value:           '1'
+  - key:             modernize-use-nullptr.NullMacros
+    value:           'NULL'
+  - key:             modernize-use-transparent-functors.SafeMode
+    value:           '0'
+  - key:             modernize-use-using.IgnoreMacros
+    value:           '1'
+  - key:             objc-forbidden-subclassing.ForbiddenSuperClassNames
+    value:           'ABNewPersonViewController;ABPeoplePickerNavigationController;ABPersonViewController;ABUnknownPersonViewController;NSHashTable;NSMapTable;NSPointerArray;NSPointerFunctions;NSTimer;UIActionSheet;UIAlertView;UIImagePickerController;UITextInputMode;UIWebView'
+  - key:             objc-property-declaration.Acronyms
+    value:           'ASCII;PDF;XML;HTML;URL;RTF;HTTP;TIFF;JPG;PNG;GIF;LZW;ROM;RGB;CMYK;MIDI;FTP'
+  - key:             performance-faster-string-find.StringLikeClasses
+    value:           'std::basic_string'
+  - key:             performance-for-range-copy.WarnOnAllAutoCopies
+    value:           '0'
+  - key:             performance-inefficient-string-concatenation.StrictMode
+    value:           '0'
+  - key:             performance-inefficient-vector-operation.VectorLikeClasses
+    value:           '::std::vector'
+  - key:             performance-move-const-arg.CheckTriviallyCopyableMove
+    value:           '1'
+  - key:             performance-move-constructor-init.IncludeStyle
+    value:           llvm
+  - key:             performance-type-promotion-in-math-fn.IncludeStyle
+    value:           llvm
+  - key:             performance-unnecessary-value-param.IncludeStyle
+    value:           llvm
+  - key:             readability-braces-around-statements.ShortStatementLines
+    value:           '0'
+  - key:             readability-function-size.BranchThreshold
+    value:           '4294967295'
+  - key:             readability-function-size.LineThreshold
+    value:           '4294967295'
+  - key:             readability-function-size.NestingThreshold
+    value:           '4294967295'
+  - key:             readability-function-size.ParameterThreshold
+    value:           '4294967295'
+  - key:             readability-function-size.StatementThreshold
+    value:           '800'
+  - key:             readability-identifier-naming.IgnoreFailedSplit
+    value:           '0'
+  - key:             readability-implicit-bool-conversion.AllowIntegerConditions
+    value:           '0'
+  - key:             readability-implicit-bool-conversion.AllowPointerConditions
+    value:           '0'
+  - key:             readability-simplify-boolean-expr.ChainedConditionalAssignment
+    value:           '0'
+  - key:             readability-simplify-boolean-expr.ChainedConditionalReturn
+    value:           '0'
+  - key:             readability-static-accessed-through-instance.NameSpecifierNestingThreshold
+    value:           '3'
+...
+

+ 1 - 0
attic/webview/.gitattributes

@@ -0,0 +1 @@
+*.h linguist-language=c

+ 7 - 0
attic/webview/.gitignore

@@ -0,0 +1,7 @@
+# Build atrifacts
+/build
+/examples/minimal-go/minimal-go
+/examples/minimal/minimal
+/examples/minimal/minimal.exe
+/examples/minimal/build
+/examples/timer-cxx/build

+ 19 - 0
attic/webview/.travis.yml

@@ -0,0 +1,19 @@
+language: go
+
+go: 
+ - 1.x
+
+matrix:
+  include:
+    - os: linux
+      before_install:
+        - sudo add-apt-repository ppa:webkit-team/ppa -y
+        - sudo apt-get update
+        - sudo apt-get install libwebkit2gtk-4.0-dev -y
+      env: WEBVIEW=gtk
+    - os: osx
+      osx_image: xcode8.3
+      env: WEBVIEW=cocoa
+
+script:
+  - make example

+ 21 - 0
attic/webview/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Serge Zaitsev
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 28 - 0
attic/webview/Makefile

@@ -0,0 +1,28 @@
+WEBVIEW_gtk_FLAGS = -DWEBVIEW_GTK -std=c++14 -Wall -Wextra -pedantic $(shell pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0)
+WEBVIEW_cocoa_FLAGS = -DWEBVIEW_COCOA -std=c++14 -Wall -Wextra -pedantic -framework WebKit -mmacosx-version-min=10.11 -DOBJC_OLD_DISPATCH_PROTOTYPES
+WEBVIEW_mshtml_FLAGS := -DWEBVIEW_MSHTML -std=c++14 -luser32 -lole32 -loleaut32 -lcomctl32 -luuid -static
+WEBVIEW_edge_FLAGS := -DWEBVIEW_EDGE
+
+all:
+	@echo "make WEBVIEW=... test - build and run tests"
+	@echo "make WEBVIEW=... lint - run clang-tidy checkers"
+	@echo "make WEBVIEW=... fmt	- run clang-format for all sources"
+
+fmt: webview.h
+	clang-format -i $^
+
+check-env:
+ifndef WEBVIEW_$(WEBVIEW)_FLAGS
+	$(error "Unknown WEBVIEW value, use WEBVIEW=gtk|cocoa|mshtml|edge")
+endif
+
+lint: check-env
+	clang-tidy example.cc -- $(WEBVIEW_$(WEBVIEW)_FLAGS)
+
+example: check-env example.cc webview.h
+	$(CXX) example.cc $(WEBVIEW_$(WEBVIEW)_FLAGS) -o example
+
+test: check-env
+	$(CXX) webview_test.cc $(WEBVIEW_$(WEBVIEW)_FLAGS) -o webview_test
+	./webview_test
+	rm webview_test

+ 39 - 0
attic/webview/example.cc

@@ -0,0 +1,39 @@
+// +build ignore
+
+#include "webview.h"
+
+#ifdef _WIN32
+int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                     LPSTR lpCmdLine, int nCmdShow)
+#else
+int main()
+#endif
+{
+  webview::webview w(true, nullptr);
+  w.set_title("Example");
+  w.set_size(480, 320, true);
+  w.bind("noop", [](std::string s) -> std::string { printf("%s\n", s.c_str());return s; });
+  w.bind("add", [](std::string s) -> std::string {
+    auto a = std::stoi(webview::json_parse(s, "", 0));
+    auto b = std::stoi(webview::json_parse(s, "", 1));
+    return std::to_string(a + b);
+  });
+  w.navigate(R"(data:text/html,
+    <!doctype html>
+    <html>
+      <body>hello</body>
+      <script>
+        window.onload = function() {
+          noop('hello').then(function(res) {
+            console.log('noop res', res);
+          });
+          add(1, 2).then(function(res) {
+            console.log('add res', res);
+          });
+        };
+      </script>
+    </html>
+  )");
+  w.run();
+  return 0;
+}

+ 15 - 0
attic/webview/example/example.go

@@ -0,0 +1,15 @@
+package main
+
+import (
+	"github.com/zserge/webview"
+)
+
+func main() {
+	w := webview.New(true)
+	w.Navigate("https://github.com")
+	w.SetTitle("Hello")
+	w.Dispatch(func() {
+		println("Hello dispatch")
+	})
+	w.Run()
+}

+ 3 - 0
attic/webview/go.mod

@@ -0,0 +1,3 @@
+module github.com/zserge/webview
+
+go 1.13

+ 1 - 0
attic/webview/webview.cc

@@ -0,0 +1 @@
+#include "webview.h"

+ 138 - 0
attic/webview/webview.go

@@ -0,0 +1,138 @@
+package webview
+
+/*
+#cgo linux openbsd freebsd CXXFLAGS: -DWEBVIEW_GTK -std=c++14
+#cgo linux openbsd freebsd pkg-config: gtk+-3.0 webkit2gtk-4.0
+
+#cgo darwin CXXFLAGS: -DWEBVIEW_COCOA -std=c++14 -DOBJC_OLD_DISPATCH_PROTOTYPES
+#cgo darwin LDFLAGS: -framework WebKit
+
+#cgo windows CXXFLAGS: -DWEBVIEW_MSHTML
+#cgo windows LDFLAGS: -lole32 -lcomctl32 -loleaut32 -luuid -lgdi32
+
+#define WEBVIEW_HEADER
+#include "webview.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+extern void _webviewDispatchGoCallback(void *);
+static inline void _webview_dispatch_cb(webview_t w, void *arg) {
+	_webviewDispatchGoCallback(arg);
+}
+static inline void CgoWebViewDispatch(webview_t w, uintptr_t arg) {
+	webview_dispatch(w, _webview_dispatch_cb, (void *)arg);
+}
+*/
+import "C"
+import (
+	"runtime"
+	"sync"
+	"unsafe"
+)
+
+func init() {
+	// Ensure that main.main is called from the main thread
+	runtime.LockOSThread()
+}
+
+type WebView interface {
+	Run()
+	Terminate()
+	Dispatch(f func())
+	Navigate(url string)
+	SetTitle(title string)
+	Window() unsafe.Pointer
+	Init(js string)
+	Eval(js string)
+	Destroy()
+	/*
+		SetBounds(x, y, width, height int)
+		Bounds() (x, y, width, height int)
+		Bind(name string, v interface{})
+	*/
+}
+
+type webview struct {
+	w C.webview_t
+}
+
+var (
+	m        sync.Mutex
+	index    uintptr
+	dispatch = map[uintptr]func(){}
+)
+
+func boolToInt(b bool) C.int {
+	if b {
+		return 1
+	}
+	return 0
+}
+
+func New(debug bool) WebView { return NewWindow(debug, nil) }
+
+func NewWindow(debug bool, window unsafe.Pointer) WebView {
+	w := &webview{}
+q
+	return w
+}
+
+func (w *webview) Destroy() {
+	C.webview_destroy(w.w)
+}
+
+func (w *webview) Run() {
+	C.webview_run(w.w)
+}
+
+func (w *webview) Terminate() {
+	C.webview_terminate(w.w)
+}
+
+func (w *webview) Window() unsafe.Pointer {
+	return C.webview_get_window(w.w)
+}
+
+func (w *webview) Navigate(url string) {
+	s := C.CString(url)
+	defer C.free(unsafe.Pointer(s))
+	C.webview_navigate(w.w, s)
+}
+
+func (w *webview) SetTitle(title string) {
+	s := C.CString(title)
+	defer C.free(unsafe.Pointer(s))
+	C.webview_set_title(w.w, s)
+}
+
+func (w *webview) Init(js string) {
+	s := C.CString(js)
+	defer C.free(unsafe.Pointer(s))
+	C.webview_init(w.w, s)
+}
+
+func (w *webview) Eval(js string) {
+	s := C.CString(js)
+	defer C.free(unsafe.Pointer(s))
+	C.webview_eval(w.w, s)
+}
+
+func (w *webview) Dispatch(f func()) {
+	m.Lock()
+	for ; dispatch[index] != nil; index++ {
+	}
+	dispatch[index] = f
+	m.Unlock()
+	C.CgoWebViewDispatch(w.w, C.uintptr_t(index))
+}
+
+//export _webviewDispatchGoCallback
+func _webviewDispatchGoCallback(index unsafe.Pointer) {
+	var f func()
+	m.Lock()
+	f = dispatch[uintptr(index)]
+	delete(dispatch, uintptr(index))
+	m.Unlock()
+	f()
+}

+ 1248 - 0
attic/webview/webview.h

@@ -0,0 +1,1248 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 Serge Zaitsev
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef WEBVIEW_H
+#define WEBVIEW_H
+
+#ifndef WEBVIEW_API
+#define WEBVIEW_API extern
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void *webview_t;
+
+// Create a new webview instance
+WEBVIEW_API webview_t webview_create(int debug, void *wnd);
+
+// Destroy a webview
+WEBVIEW_API void webview_destroy(webview_t w);
+
+// Run the main loop
+WEBVIEW_API void webview_run(webview_t w);
+
+// Stop the main loop
+WEBVIEW_API void webview_terminate(webview_t w);
+
+// Post a function to be executed on the main thread
+WEBVIEW_API void
+webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg);
+
+WEBVIEW_API void *webview_get_window(webview_t w);
+
+WEBVIEW_API void webview_set_title(webview_t w, const char *title);
+
+WEBVIEW_API void webview_set_bounds(webview_t w, int x, int y, int width,
+                                    int height, int flags);
+WEBVIEW_API void webview_get_bounds(webview_t w, int *x, int *y, int *width,
+                                    int *height, int *flags);
+
+WEBVIEW_API void webview_navigate(webview_t w, const char *url);
+WEBVIEW_API void webview_init(webview_t w, const char *js);
+WEBVIEW_API void webview_eval(webview_t w, const char *js);
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifndef WEBVIEW_HEADER
+
+#if !defined(WEBVIEW_GTK) && !defined(WEBVIEW_COCOA) &&                        \
+    !defined(WEBVIEW_MSHTML) && !defined(WEBVIEW_EDGE)
+#error "please, specify webview backend"
+#endif
+
+#include <atomic>
+#include <functional>
+#include <future>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <cstring>
+
+namespace webview {
+using dispatch_fn_t = std::function<void()>;
+using msg_cb_t = std::function<void(const char *msg)>;
+
+inline std::string url_encode(std::string s) {
+  std::string encoded;
+  for (unsigned int i = 0; i < s.length(); i++) {
+    auto c = s[i];
+    if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
+      encoded = encoded + c;
+    } else {
+      char hex[4];
+      snprintf(hex, sizeof(hex), "%%%02x", c);
+      encoded = encoded + hex;
+    }
+  }
+  return encoded;
+}
+
+inline std::string url_decode(std::string s) {
+  std::string decoded;
+  for (unsigned int i = 0; i < s.length(); i++) {
+    if (s[i] == '%') {
+      int n;
+      sscanf(s.substr(i + 1, 2).c_str(), "%x", &n);
+      decoded = decoded + static_cast<char>(n);
+      i = i + 2;
+    } else if (s[i] == '+') {
+      decoded = decoded + ' ';
+    } else {
+      decoded = decoded + s[i];
+    }
+  }
+  return decoded;
+}
+
+inline std::string html_from_uri(std::string s) {
+  if (s.substr(0, 15) == "data:text/html,") {
+    return url_decode(s.substr(15));
+  }
+  return "";
+}
+
+inline int json_parse_c(const char *s, size_t sz, const char *key, size_t keysz,
+                        const char **value, size_t *valuesz) {
+  enum {
+    JSON_STATE_VALUE,
+    JSON_STATE_LITERAL,
+    JSON_STATE_STRING,
+    JSON_STATE_ESCAPE,
+    JSON_STATE_UTF8
+  } state = JSON_STATE_VALUE;
+  const char *k = NULL;
+  int index = 1;
+  int depth = 0;
+  int utf8_bytes = 0;
+
+  if (key == NULL) {
+    index = keysz;
+    keysz = 0;
+  }
+
+  *value = NULL;
+  *valuesz = 0;
+
+  for (; sz > 0; s++, sz--) {
+    enum {
+      JSON_ACTION_NONE,
+      JSON_ACTION_START,
+      JSON_ACTION_END,
+      JSON_ACTION_START_STRUCT,
+      JSON_ACTION_END_STRUCT
+    } action = JSON_ACTION_NONE;
+    unsigned char c = *s;
+    switch (state) {
+    case JSON_STATE_VALUE:
+      if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' ||
+          c == ':') {
+        continue;
+      } else if (c == '"') {
+        action = JSON_ACTION_START;
+        state = JSON_STATE_STRING;
+      } else if (c == '{' || c == '[') {
+        action = JSON_ACTION_START_STRUCT;
+      } else if (c == '}' || c == ']') {
+        action = JSON_ACTION_END_STRUCT;
+      } else if (c == 't' || c == 'f' || c == 'n' || c == '-' ||
+                 (c >= '0' && c <= '9')) {
+        action = JSON_ACTION_START;
+        state = JSON_STATE_LITERAL;
+      } else {
+        return -1;
+      }
+      break;
+    case JSON_STATE_LITERAL:
+      if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == ',' ||
+          c == ']' || c == '}' || c == ':') {
+        state = JSON_STATE_VALUE;
+        s--;
+        sz++;
+        action = JSON_ACTION_END;
+      } else if (c < 32 || c > 126) {
+        return -1;
+      } // fallthrough
+    case JSON_STATE_STRING:
+      if (c < 32 || (c > 126 && c < 192)) {
+        return -1;
+      } else if (c == '"') {
+        action = JSON_ACTION_END;
+        state = JSON_STATE_VALUE;
+      } else if (c == '\\') {
+        state = JSON_STATE_ESCAPE;
+      } else if (c >= 192 && c < 224) {
+        utf8_bytes = 1;
+        state = JSON_STATE_UTF8;
+      } else if (c >= 224 && c < 240) {
+        utf8_bytes = 2;
+        state = JSON_STATE_UTF8;
+      } else if (c >= 240 && c < 247) {
+        utf8_bytes = 3;
+        state = JSON_STATE_UTF8;
+      } else if (c >= 128 && c < 192) {
+        return -1;
+      }
+      break;
+    case JSON_STATE_ESCAPE:
+      if (c == '"' || c == '\\' || c == '/' || c == 'b' || c == 'f' ||
+          c == 'n' || c == 'r' || c == 't' || c == 'u') {
+        state = JSON_STATE_STRING;
+      } else {
+        return -1;
+      }
+      break;
+    case JSON_STATE_UTF8:
+      if (c < 128 || c > 191) {
+        return -1;
+      }
+      utf8_bytes--;
+      if (utf8_bytes == 0) {
+        state = JSON_STATE_STRING;
+      }
+      break;
+    default:
+      return -1;
+    }
+
+    if (action == JSON_ACTION_END_STRUCT) {
+      depth--;
+    }
+
+    if (depth == 1) {
+      if (action == JSON_ACTION_START || action == JSON_ACTION_START_STRUCT) {
+        if (index == 0) {
+          *value = s;
+        } else if (keysz > 0 && index == 1) {
+          k = s;
+        } else {
+          index--;
+        }
+      } else if (action == JSON_ACTION_END ||
+                 action == JSON_ACTION_END_STRUCT) {
+        if (*value != NULL && index == 0) {
+          *valuesz = (size_t)(s + 1 - *value);
+          return 0;
+        } else if (keysz > 0 && k != NULL) {
+          if (keysz == (size_t)(s - k - 1) && memcmp(key, k + 1, keysz) == 0) {
+            index = 0;
+          } else {
+            index = 2;
+          }
+          k = NULL;
+        }
+      }
+    }
+
+    if (action == JSON_ACTION_START_STRUCT) {
+      depth++;
+    }
+  }
+  return -1;
+}
+
+inline std::string json_escape(std::string s) {
+  // TODO: implement
+  return '"' + s + '"';
+}
+
+inline int json_unescape(const char *s, size_t n, char *out) {
+  int r = 0;
+  if (*s++ != '"') {
+    return -1;
+  }
+  while (n > 2) {
+    char c = *s;
+    if (c == '\\') {
+      s++;
+      n--;
+      switch (*s) {
+      case 'b':
+        c = '\b';
+        break;
+      case 'f':
+        c = '\f';
+        break;
+      case 'n':
+        c = '\n';
+        break;
+      case 'r':
+        c = '\r';
+        break;
+      case 't':
+        c = '\t';
+        break;
+      case '\\':
+        c = '\\';
+        break;
+      case '/':
+        c = '/';
+        break;
+      case '\"':
+        c = '\"';
+        break;
+      default: // TODO: support unicode decoding
+        return -1;
+      }
+    }
+    if (out != NULL) {
+      *out++ = c;
+    }
+    s++;
+    n--;
+    r++;
+  }
+  if (*s != '"') {
+    return -1;
+  }
+  if (out != NULL) {
+    *out = '\0';
+  }
+  return r;
+}
+
+inline std::string json_parse(std::string s, std::string key, int index) {
+  const char *value;
+  size_t value_sz;
+  if (key == "") {
+    json_parse_c(s.c_str(), s.length(), nullptr, index, &value, &value_sz);
+  } else {
+    json_parse_c(s.c_str(), s.length(), key.c_str(), key.length(), &value,
+                 &value_sz);
+  }
+  if (value != nullptr) {
+    if (value[0] != '"') {
+      return std::string(value, value_sz);
+    }
+    int n = json_unescape(value, value_sz, nullptr);
+    if (n > 0) {
+      char *decoded = new char[n];
+      json_unescape(value, value_sz, decoded);
+      auto result = std::string(decoded, n);
+      delete[] decoded;
+      return result;
+    }
+  }
+  return "";
+}
+
+} // namespace webview
+
+#if defined(WEBVIEW_GTK)
+//
+// ====================================================================
+//
+// This implementation uses webkit2gtk backend. It requires gtk+3.0 and
+// webkit2gtk-4.0 libraries. Proper compiler flags can be retrieved via:
+//
+//   pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0
+//
+// ====================================================================
+//
+#include <JavaScriptCore/JavaScript.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+namespace webview {
+
+class browser_engine {
+public:
+  browser_engine(msg_cb_t cb, bool debug, void *window)
+      : m_cb(cb), m_window(static_cast<GtkWidget *>(window)) {
+    gtk_init_check(0, NULL);
+    m_window = static_cast<GtkWidget *>(window);
+    if (m_window == nullptr) {
+      m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    }
+    g_signal_connect(G_OBJECT(m_window), "destroy",
+                     G_CALLBACK(+[](GtkWidget *w, gpointer arg) {
+                       static_cast<browser_engine *>(arg)->terminate();
+                     }),
+                     this);
+    // Initialize webview widget
+    m_webview = webkit_web_view_new();
+    WebKitUserContentManager *manager =
+        webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
+    g_signal_connect(manager, "script-message-received::external",
+                     G_CALLBACK(+[](WebKitUserContentManager *m,
+                                    WebKitJavascriptResult *r, gpointer arg) {
+                       auto *w = static_cast<browser_engine *>(arg);
+#if WEBKIT_MAJOR_VERSION >= 2 && WEBKIT_MINOR_VERSION >= 22
+                       JSCValue *value =
+                           webkit_javascript_result_get_js_value(r);
+                       char *s = jsc_value_to_string(value);
+#else
+                       JSGlobalContextRef ctx =
+                           webkit_javascript_result_get_global_context(r);
+                       JSValueRef value = webkit_javascript_result_get_value(r);
+                       JSStringRef js = JSValueToStringCopy(ctx, value, NULL);
+                       size_t n = JSStringGetMaximumUTF8CStringSize(js);
+                       char *s = g_new(char, n);
+                       JSStringGetUTF8CString(js, s, n);
+                       JSStringRelease(js);
+#endif
+                       w->m_cb(s);
+                       g_free(s);
+                     }),
+                     this);
+    webkit_user_content_manager_register_script_message_handler(manager,
+                                                                "external");
+    init("window.external={invoke:function(s){window.webkit.messageHandlers."
+         "external.postMessage(s);}}");
+
+    gtk_container_add(GTK_CONTAINER(m_window), GTK_WIDGET(m_webview));
+    gtk_widget_grab_focus(GTK_WIDGET(m_webview));
+
+    if (debug) {
+      WebKitSettings *settings =
+          webkit_web_view_get_settings(WEBKIT_WEB_VIEW(m_webview));
+      webkit_settings_set_enable_write_console_messages_to_stdout(settings,
+                                                                  true);
+      webkit_settings_set_enable_developer_extras(settings, true);
+    }
+
+    gtk_widget_show_all(m_window);
+  }
+  void run() { gtk_main(); }
+  void terminate() { gtk_main_quit(); }
+  void dispatch(std::function<void()> f) {
+    g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int {
+                      (*static_cast<dispatch_fn_t *>(f))();
+                      return G_SOURCE_REMOVE;
+                    }),
+                    new std::function<void()>(f),
+                    [](void *f) { delete static_cast<dispatch_fn_t *>(f); });
+  }
+
+  void set_title(const char *title) {
+    gtk_window_set_title(GTK_WINDOW(m_window), title);
+  }
+
+  void set_size(int width, int height, bool resizable) {
+    gtk_window_set_resizable(GTK_WINDOW(m_window), !!resizable);
+    if (resizable) {
+      gtk_window_set_default_size(GTK_WINDOW(m_window), width, height);
+    } else {
+      gtk_widget_set_size_request(m_window, width, height);
+    }
+  }
+
+  void navigate(const char *url) {
+    webkit_web_view_load_uri(WEBKIT_WEB_VIEW(m_webview), url);
+  }
+
+  void init(const char *js) {
+    WebKitUserContentManager *manager =
+        webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(m_webview));
+    webkit_user_content_manager_add_script(
+        manager, webkit_user_script_new(
+                     js, WEBKIT_USER_CONTENT_INJECT_TOP_FRAME,
+                     WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START, NULL, NULL));
+  }
+
+  void eval(const char *js) {
+    webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(m_webview), js, NULL, NULL,
+                                   NULL);
+  }
+
+protected:
+  std::function<void(const char *)> m_cb;
+  GtkWidget *m_window;
+  GtkWidget *m_webview;
+};
+
+} // namespace webview
+
+#elif defined(WEBVIEW_COCOA)
+
+//
+// ====================================================================
+//
+// This implementation uses Cocoa WKWebView backend on macOS. It is
+// written using ObjC runtime and uses WKWebView class as a browser runtime.
+// You should pass "-framework Webkit" flag to the compiler.
+//
+// ====================================================================
+//
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <objc/objc-runtime.h>
+
+#define NSBackingStoreBuffered 2
+
+#define NSWindowStyleMaskResizable 8
+#define NSWindowStyleMaskMiniaturizable 4
+#define NSWindowStyleMaskTitled 1
+#define NSWindowStyleMaskClosable 2
+
+#define NSApplicationActivationPolicyRegular 0
+
+#define WKUserScriptInjectionTimeAtDocumentStart 0
+
+namespace webview {
+
+id operator"" _cls(const char *s, std::size_t sz) {
+  return (id)objc_getClass(s);
+}
+SEL operator"" _sel(const char *s, std::size_t sz) {
+  return sel_registerName(s);
+}
+id operator"" _str(const char *s, std::size_t sz) {
+  return objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, s);
+}
+
+class browser_engine {
+public:
+  browser_engine(msg_cb_t cb, bool debug, void *window) : m_cb(cb) {
+    // Application
+    id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel);
+    objc_msgSend(app, "setActivationPolicy:"_sel,
+                 NSApplicationActivationPolicyRegular);
+
+    // Delegate
+    auto cls = objc_allocateClassPair((Class) "NSObject"_cls, "AppDelegate", 0);
+    class_addProtocol(cls, objc_getProtocol("NSApplicationDelegate"));
+    class_addProtocol(cls, objc_getProtocol("WKScriptMessageHandler"));
+    class_addMethod(
+        cls, "applicationShouldTerminateAfterLastWindowClosed:"_sel,
+        (IMP)(+[](id self, SEL cmd, id notification) -> BOOL { return 1; }),
+        "c@:@");
+    class_addMethod(
+        cls, "userContentController:didReceiveScriptMessage:"_sel,
+        (IMP)(+[](id self, SEL cmd, id notification, id msg) {
+          auto w = (browser_engine *)objc_getAssociatedObject(self, "webview");
+          w->m_cb((const char *)objc_msgSend(objc_msgSend(msg, "body"_sel),
+                                             "UTF8String"_sel));
+        }),
+        "v@:@@");
+    objc_registerClassPair(cls);
+
+    auto delegate = objc_msgSend((id)cls, "new"_sel);
+    objc_setAssociatedObject(delegate, "webview", (id)this,
+                             OBJC_ASSOCIATION_ASSIGN);
+    objc_msgSend(app, sel_registerName("setDelegate:"), delegate);
+
+    // Main window
+    if (window == nullptr) {
+      m_window = objc_msgSend("NSWindow"_cls, "alloc"_sel);
+      m_window = objc_msgSend(
+          m_window, "initWithContentRect:styleMask:backing:defer:"_sel,
+          CGRectMake(0, 0, 0, 0), 0, NSBackingStoreBuffered, 0);
+      set_size(480, 320, true);
+    } else {
+      m_window = (id)window;
+    }
+
+    // Webview
+    auto config = objc_msgSend("WKWebViewConfiguration"_cls, "new"_sel);
+    m_manager = objc_msgSend(config, "userContentController"_sel);
+    m_webview = objc_msgSend("WKWebView"_cls, "alloc"_sel);
+    objc_msgSend(m_webview, "initWithFrame:configuration:"_sel,
+                 CGRectMake(0, 0, 0, 0), config);
+    objc_msgSend(m_manager, "addScriptMessageHandler:name:"_sel, delegate,
+                 "external"_str);
+    init(R"script(
+                      window.external = {
+                        invoke: function(s) {
+                          window.webkit.messageHandlers.external.postMessage(s);
+                        },
+                      };
+                     )script");
+    if (debug) {
+      objc_msgSend(objc_msgSend(config, "preferences"_sel),
+                   "setValue:forKey:"_sel, 1, "developerExtrasEnabled"_str);
+    }
+    objc_msgSend(m_window, "setContentView:"_sel, m_webview);
+    objc_msgSend(m_window, "makeKeyAndOrderFront:"_sel, nullptr);
+  }
+  ~browser_engine() { objc_msgSend(m_window, "close"_sel); }
+  void terminate() { objc_msgSend("NSApp"_cls, "terminate:"_sel, nullptr); }
+  void run() {
+    id app = objc_msgSend("NSApplication"_cls, "sharedApplication"_sel);
+    dispatch([&]() { objc_msgSend(app, "activateIgnoringOtherApps:"_sel, 1); });
+    objc_msgSend(app, "run"_sel);
+  }
+  void dispatch(std::function<void()> f) {
+    dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f),
+                     (dispatch_function_t)([](void *arg) {
+                       auto f = static_cast<dispatch_fn_t *>(arg);
+                       (*f)();
+                       delete f;
+                     }));
+  }
+  void set_title(const char *title) {
+    objc_msgSend(
+        m_window, "setTitle:"_sel,
+        objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, title));
+  }
+  void set_size(int width, int height, bool resizable) {
+    auto style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
+                 NSWindowStyleMaskMiniaturizable;
+    if (resizable) {
+      style = style | NSWindowStyleMaskResizable;
+    }
+    objc_msgSend(m_window, "setStyleMask:"_sel, style);
+    objc_msgSend(m_window, "setFrame:display:animate:"_sel,
+                 CGRectMake(0, 0, width, height), 1, 0);
+  }
+  void navigate(const char *url) {
+    auto nsurl = objc_msgSend(
+        "NSURL"_cls, "URLWithString:"_sel,
+        objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, url));
+    objc_msgSend(
+        m_webview, "loadRequest:"_sel,
+        objc_msgSend("NSURLRequest"_cls, "requestWithURL:"_sel, nsurl));
+  }
+  void init(const char *js) {
+    objc_msgSend(
+        m_manager, "addUserScript:"_sel,
+        objc_msgSend(
+            objc_msgSend("WKUserScript"_cls, "alloc"_sel),
+            "initWithSource:injectionTime:forMainFrameOnly:"_sel,
+            objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js),
+            WKUserScriptInjectionTimeAtDocumentStart, 1));
+  }
+  void eval(const char *js) {
+    objc_msgSend(m_webview, "evaluateJavaScript:completionHandler:"_sel,
+                 objc_msgSend("NSString"_cls, "stringWithUTF8String:"_sel, js),
+                 nullptr);
+  }
+
+protected:
+  id m_window;
+  id m_webview;
+  id m_manager;
+  msg_cb_t m_cb;
+};
+
+} // namespace webview
+
+#elif defined(WEBVIEW_MSHTML) || defined(WEBVIEW_EDGE)
+
+//
+// ====================================================================
+//
+// This implementation uses Win32 API to create a native window. It can
+// use either MSHTML or EdgeHTML backend as a browser engine.
+//
+// ====================================================================
+//
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#pragma comment(lib, "user32.lib")
+namespace webview {
+class browser_window {
+public:
+  browser_window(msg_cb_t cb, void *window) : m_cb(cb) {
+    if (window == nullptr) {
+      WNDCLASSEX wc;
+      ZeroMemory(&wc, sizeof(WNDCLASSEX));
+      wc.cbSize = sizeof(WNDCLASSEX);
+      wc.hInstance = GetModuleHandle(nullptr);
+      wc.lpszClassName = "webview";
+      wc.lpfnWndProc =
+          (WNDPROC)(+[](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> int {
+            auto w = (browser_window *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+            switch (msg) {
+            case WM_SIZE:
+              w->resize();
+              break;
+            case WM_CLOSE:
+              DestroyWindow(hwnd);
+              break;
+            case WM_DESTROY:
+              w->terminate();
+              break;
+            default:
+              return DefWindowProc(hwnd, msg, wp, lp);
+            }
+            return 0;
+          });
+      RegisterClassEx(&wc);
+      m_window = CreateWindow("webview", "", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
+                              CW_USEDEFAULT, 640, 480, nullptr, nullptr,
+                              GetModuleHandle(nullptr), nullptr);
+      SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
+    } else {
+      m_window = *(static_cast<HWND *>(window));
+    }
+
+    ShowWindow(m_window, SW_SHOW);
+    UpdateWindow(m_window);
+    SetFocus(m_window);
+  }
+
+  void run() {
+    MSG msg;
+    BOOL res;
+    while ((res = GetMessage(&msg, nullptr, 0, 0)) != -1) {
+      if (msg.hwnd) {
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+        continue;
+      }
+      if (msg.message == WM_APP) {
+        auto f = (dispatch_fn_t *)(msg.lParam);
+        (*f)();
+        delete f;
+      } else if (msg.message == WM_QUIT) {
+        return;
+      }
+    }
+  }
+
+  void terminate() { PostQuitMessage(0); }
+  void dispatch(dispatch_fn_t f) {
+    PostThreadMessage(m_main_thread, WM_APP, 0, (LPARAM) new dispatch_fn_t(f));
+  }
+
+  void set_title(const char *title) { SetWindowText(m_window, title); }
+
+  void set_size(int width, int height, bool resizable) {
+    RECT r;
+    r.left = 50;
+    r.top = 50;
+    r.right = width;
+    r.bottom = height;
+    AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, 0);
+    SetWindowPos(m_window, NULL, r.left, r.top, r.right - r.left,
+                 r.bottom - r.top,
+                 SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
+  }
+
+protected:
+  virtual void resize() {}
+  HWND m_window;
+  DWORD m_main_thread = GetCurrentThreadId();
+  msg_cb_t m_cb;
+};
+} // namespace webview
+
+#if defined(WEBVIEW_MSHTML)
+#include <exdisp.h>
+#include <exdispid.h>
+#include <mshtmhst.h>
+#include <mshtml.h>
+#include <shobjidl.h>
+#pragma comment(lib, "ole32.lib")
+#pragma comment(lib, "oleaut32.lib")
+
+#define DISPID_EXTERNAL_INVOKE 0x1000
+
+namespace webview {
+class browser_engine : public browser_window,
+                       public IOleClientSite,
+                       public IOleInPlaceSite,
+                       public IOleInPlaceFrame,
+                       public IDocHostUIHandler,
+                       public DWebBrowserEvents2 {
+public:
+  browser_engine(msg_cb_t cb, bool debug, void *window)
+      : browser_window(cb, window) {
+    RECT rect;
+    LPCLASSFACTORY cf = nullptr;
+    IOleObject *obj = nullptr;
+
+    fix_ie_compat_mode();
+
+    OleInitialize(nullptr);
+    CoGetClassObject(CLSID_WebBrowser,
+                     CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, nullptr,
+                     IID_IClassFactory, (void **)&cf);
+    cf->CreateInstance(nullptr, IID_IOleObject, (void **)&obj);
+    cf->Release();
+
+    obj->SetClientSite(this);
+    OleSetContainedObject(obj, TRUE);
+    GetWindowRect(m_window, &rect);
+    obj->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, -1, m_window, &rect);
+    obj->QueryInterface(IID_IWebBrowser2, (void **)&m_webview);
+
+    IConnectionPointContainer *cpc;
+    IConnectionPoint *cp;
+    DWORD cookie;
+    m_webview->QueryInterface(IID_IConnectionPointContainer, (void **)&cpc);
+    cpc->FindConnectionPoint(DIID_DWebBrowserEvents2, &cp);
+    cpc->Release();
+    cp->Advise(static_cast<IOleClientSite *>(this), &cookie);
+
+    resize();
+    navigate("about:blank");
+  }
+
+  ~browser_engine() { OleUninitialize(); }
+
+  void navigate(const char *url) {
+    VARIANT v;
+    DWORD size = MultiByteToWideChar(CP_UTF8, 0, url, -1, 0, 0);
+    WCHAR *ws = (WCHAR *)GlobalAlloc(GMEM_FIXED, sizeof(WCHAR) * size);
+    MultiByteToWideChar(CP_UTF8, 0, url, -1, ws, size);
+    VariantInit(&v);
+    v.vt = VT_BSTR;
+    v.bstrVal = SysAllocString(ws);
+    m_webview->Navigate2(&v, nullptr, nullptr, nullptr, nullptr);
+    VariantClear(&v);
+  }
+
+  void eval(const char *js) {
+    // TODO
+  }
+
+private:
+  IWebBrowser2 *m_webview;
+
+  int fix_ie_compat_mode() {
+    const char *WEBVIEW_KEY_FEATURE_BROWSER_EMULATION =
+        "Software\\Microsoft\\Internet "
+        "Explorer\\Main\\FeatureControl\\FEATURE_BROWSER_EMULATION";
+    HKEY hKey;
+    DWORD ie_version = 11000;
+    TCHAR appname[MAX_PATH + 1];
+    TCHAR *p;
+    if (GetModuleFileName(NULL, appname, MAX_PATH + 1) == 0) {
+      return -1;
+    }
+    for (p = &appname[strlen(appname) - 1]; p != appname && *p != '\\'; p--) {
+    }
+    p++;
+    if (RegCreateKey(HKEY_CURRENT_USER, WEBVIEW_KEY_FEATURE_BROWSER_EMULATION,
+                     &hKey) != ERROR_SUCCESS) {
+      return -1;
+    }
+    if (RegSetValueEx(hKey, p, 0, REG_DWORD, (BYTE *)&ie_version,
+                      sizeof(ie_version)) != ERROR_SUCCESS) {
+      RegCloseKey(hKey);
+      return -1;
+    }
+    RegCloseKey(hKey);
+    return 0;
+  }
+
+  // Inheruted via browser_window
+  void resize() override {
+    RECT rect;
+    GetClientRect(m_window, &rect);
+    m_webview->put_Left(0);
+    m_webview->put_Top(0);
+    m_webview->put_Width(rect.right);
+    m_webview->put_Height(rect.bottom);
+    m_webview->put_Visible(VARIANT_TRUE);
+  }
+
+  // Inherited via IUnknown
+  ULONG __stdcall AddRef(void) override { return 1; }
+  ULONG __stdcall Release(void) override { return 1; }
+  HRESULT __stdcall QueryInterface(REFIID riid, void **obj) override {
+    if (riid == IID_IUnknown || riid == IID_IOleClientSite) {
+      *obj = static_cast<IOleClientSite *>(this);
+      return S_OK;
+    }
+    if (riid == IID_IOleInPlaceSite) {
+      *obj = static_cast<IOleInPlaceSite *>(this);
+      return S_OK;
+    }
+    if (riid == IID_IDocHostUIHandler) {
+      *obj = static_cast<IDocHostUIHandler *>(this);
+      return S_OK;
+    }
+    if (riid == IID_IDispatch || riid == DIID_DWebBrowserEvents2) {
+      *obj = static_cast<IDispatch *>(this);
+      return S_OK;
+    }
+    *obj = nullptr;
+    return E_NOINTERFACE;
+  }
+
+  // Inherited via IOleClientSite
+  HRESULT __stdcall SaveObject(void) override { return E_NOTIMPL; }
+  HRESULT __stdcall GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker,
+                               IMoniker **ppmk) override {
+    return E_NOTIMPL;
+  }
+  HRESULT __stdcall GetContainer(IOleContainer **ppContainer) override {
+    *ppContainer = nullptr;
+    return E_NOINTERFACE;
+  }
+  HRESULT __stdcall ShowObject(void) override { return S_OK; }
+  HRESULT __stdcall OnShowWindow(BOOL fShow) override { return S_OK; }
+  HRESULT __stdcall RequestNewObjectLayout(void) override { return E_NOTIMPL; }
+
+  // Inherited via IOleInPlaceSite
+  HRESULT __stdcall GetWindow(HWND *phwnd) override {
+    *phwnd = m_window;
+    return S_OK;
+  }
+  HRESULT __stdcall ContextSensitiveHelp(BOOL fEnterMode) override {
+    return E_NOTIMPL;
+  }
+  HRESULT __stdcall CanInPlaceActivate(void) override { return S_OK; }
+  HRESULT __stdcall OnInPlaceActivate(void) override { return S_OK; }
+  HRESULT __stdcall OnUIActivate(void) override { return S_OK; }
+  HRESULT __stdcall GetWindowContext(
+      IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc,
+      LPRECT lprcPosRect, LPRECT lprcClipRect,
+      LPOLEINPLACEFRAMEINFO lpFrameInfo) override {
+    *ppFrame = static_cast<IOleInPlaceFrame *>(this);
+    *ppDoc = nullptr;
+    lpFrameInfo->fMDIApp = FALSE;
+    lpFrameInfo->hwndFrame = m_window;
+    lpFrameInfo->haccel = 0;
+    lpFrameInfo->cAccelEntries = 0;
+    return S_OK;
+  }
+  HRESULT __stdcall Scroll(SIZE scrollExtant) override { return E_NOTIMPL; }
+  HRESULT __stdcall OnUIDeactivate(BOOL fUndoable) override { return S_OK; }
+  HRESULT __stdcall OnInPlaceDeactivate(void) override { return S_OK; }
+  HRESULT __stdcall DiscardUndoState(void) override { return E_NOTIMPL; }
+  HRESULT __stdcall DeactivateAndUndo(void) override { return E_NOTIMPL; }
+  HRESULT __stdcall OnPosRectChange(LPCRECT lprcPosRect) override {
+    IOleInPlaceObject *inplace;
+    m_webview->QueryInterface(IID_IOleInPlaceObject, (void **)&inplace);
+    inplace->SetObjectRects(lprcPosRect, lprcPosRect);
+    return S_OK;
+  }
+
+  // Inherited via IDocHostUIHandler
+  HRESULT __stdcall ShowContextMenu(DWORD dwID, POINT *ppt,
+                                    IUnknown *pcmdtReserved,
+                                    IDispatch *pdispReserved) override {
+    return S_OK;
+  }
+  HRESULT __stdcall GetHostInfo(DOCHOSTUIINFO *pInfo) override {
+    pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
+    pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER;
+    return S_OK;
+  }
+  HRESULT __stdcall ShowUI(DWORD dwID, IOleInPlaceActiveObject *pActiveObject,
+                           IOleCommandTarget *pCommandTarget,
+                           IOleInPlaceFrame *pFrame,
+                           IOleInPlaceUIWindow *pDoc) override {
+    return S_OK;
+  }
+  HRESULT __stdcall HideUI(void) override { return S_OK; }
+  HRESULT __stdcall UpdateUI(void) override { return S_OK; }
+  HRESULT __stdcall EnableModeless(BOOL fEnable) override { return S_OK; }
+  HRESULT __stdcall OnDocWindowActivate(BOOL fActivate) override {
+    return S_OK;
+  }
+  HRESULT __stdcall OnFrameWindowActivate(BOOL fActivate) override {
+    return S_OK;
+  }
+  HRESULT __stdcall ResizeBorder(LPCRECT prcBorder,
+                                 IOleInPlaceUIWindow *pUIWindow,
+                                 BOOL fRameWindow) override {
+    return S_OK;
+  }
+  HRESULT __stdcall GetOptionKeyPath(LPOLESTR *pchKey, DWORD dw) override {
+    return S_FALSE;
+  }
+  HRESULT __stdcall GetDropTarget(IDropTarget *pDropTarget,
+                                  IDropTarget **ppDropTarget) override {
+    return E_NOTIMPL;
+  }
+  HRESULT __stdcall GetExternal(IDispatch **ppDispatch) override {
+    *ppDispatch = static_cast<IDispatch *>(this);
+    return S_OK;
+  }
+  HRESULT __stdcall TranslateUrl(DWORD dwTranslate, LPWSTR pchURLIn,
+                                 LPWSTR *ppchURLOut) override {
+    *ppchURLOut = nullptr;
+    return S_FALSE;
+  }
+  HRESULT __stdcall FilterDataObject(IDataObject *pDO,
+                                     IDataObject **ppDORet) override {
+    *ppDORet = nullptr;
+    return S_FALSE;
+  }
+  HRESULT __stdcall TranslateAcceleratorA(LPMSG lpMsg,
+                                          const GUID *pguidCmdGroup,
+                                          DWORD nCmdID) {
+    return S_FALSE;
+  }
+
+  // Inherited via IOleInPlaceFrame
+  HRESULT __stdcall GetBorder(LPRECT lprectBorder) override { return S_OK; }
+  HRESULT __stdcall RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) override {
+    return S_OK;
+  }
+  HRESULT __stdcall SetBorderSpace(LPCBORDERWIDTHS pborderwidths) override {
+    return S_OK;
+  }
+  HRESULT __stdcall SetActiveObject(IOleInPlaceActiveObject *pActiveObject,
+                                    LPCOLESTR pszObjName) override {
+    return S_OK;
+  }
+  HRESULT __stdcall InsertMenus(HMENU hmenuShared,
+                                LPOLEMENUGROUPWIDTHS lpMenuWidths) override {
+    return S_OK;
+  }
+  HRESULT __stdcall SetMenu(HMENU hmenuShared, HOLEMENU holemenu,
+                            HWND hwndActiveObject) override {
+    return S_OK;
+  }
+  HRESULT __stdcall RemoveMenus(HMENU hmenuShared) override { return S_OK; }
+  HRESULT __stdcall SetStatusText(LPCOLESTR pszStatusText) override {
+    return S_OK;
+  }
+  HRESULT __stdcall TranslateAcceleratorA(LPMSG lpmsg, WORD wID) {
+    return S_OK;
+  }
+
+  // Inherited via IDispatch
+  HRESULT __stdcall GetTypeInfoCount(UINT *pctinfo) override { return S_OK; }
+  HRESULT __stdcall GetTypeInfo(UINT iTInfo, LCID lcid,
+                                ITypeInfo **ppTInfo) override {
+    return S_OK;
+  }
+  HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
+                                  LCID lcid, DISPID *rgDispId) override {
+    *rgDispId = DISPID_EXTERNAL_INVOKE;
+    return S_OK;
+  }
+  HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
+                           WORD wFlags, DISPPARAMS *pDispParams,
+                           VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
+                           UINT *puArgErr) override {
+    if (dispIdMember == DISPID_NAVIGATECOMPLETE2) {
+    } else if (dispIdMember == DISPID_DOCUMENTCOMPLETE) {
+    } else if (dispIdMember == DISPID_EXTERNAL_INVOKE) {
+    }
+    return S_OK;
+  }
+};
+} // namespace webview
+
+#elif defined(WEBVIEW_EDGE)
+#include <objbase.h>
+#include <winrt/Windows.Foundation.h>
+#include <winrt/Windows.Web.UI.Interop.h>
+
+#pragma comment(lib, "windowsapp")
+
+namespace webview {
+
+using namespace winrt;
+using namespace Windows::Foundation;
+using namespace Windows::Web::UI;
+using namespace Windows::Web::UI::Interop;
+
+class browser_engine : public browser_window {
+public:
+  browser_engine(msg_cb_t cb, bool debug, void *window)
+      : browser_window(cb, window) {
+    init_apartment(winrt::apartment_type::single_threaded);
+    m_process = WebViewControlProcess();
+    auto op = m_process.CreateWebViewControlAsync(
+        reinterpret_cast<int64_t>(m_window), Rect());
+    if (op.Status() != AsyncStatus::Completed) {
+      handle h(CreateEvent(nullptr, false, false, nullptr));
+      op.Completed([h = h.get()](auto, auto) { SetEvent(h); });
+      HANDLE hs[] = {h.get()};
+      DWORD i;
+      CoWaitForMultipleHandles(COWAIT_DISPATCH_WINDOW_MESSAGES |
+                                   COWAIT_DISPATCH_CALLS |
+                                   COWAIT_INPUTAVAILABLE,
+                               INFINITE, 1, hs, &i);
+    }
+    m_webview = op.GetResults();
+    m_webview.Settings().IsScriptNotifyAllowed(true);
+    m_webview.IsVisible(true);
+    m_webview.ScriptNotify([=](auto const &sender, auto const &args) {
+      std::string s = winrt::to_string(args.Value());
+      m_cb(s.c_str());
+    });
+    m_webview.NavigationStarting([=](auto const &sender, auto const &args) {
+      m_webview.AddInitializeScript(winrt::to_hstring(init_js));
+    });
+    init("window.external.invoke = s => window.external.notify(s)");
+    resize();
+  }
+
+  void navigate(const char *url) {
+    Uri uri(winrt::to_hstring(url));
+    // TODO: if url starts with 'data:text/html,' prefix then use it as a string
+    m_webview.Navigate(uri);
+    // m_webview.NavigateToString(winrt::to_hstring(url));
+  }
+  void init(const char *js) {
+    init_js = init_js + "(function(){" + js + "})();";
+  }
+  void eval(const char *js) {
+    m_webview.InvokeScriptAsync(
+        L"eval", single_threaded_vector<hstring>({winrt::to_hstring(js)}));
+  }
+
+private:
+  void resize() {
+    RECT r;
+    GetClientRect(m_window, &r);
+    Rect bounds(r.left, r.top, r.right - r.left, r.bottom - r.top);
+    m_webview.Bounds(bounds);
+  }
+  WebViewControlProcess m_process;
+  WebViewControl m_webview = nullptr;
+  std::string init_js = "";
+};
+} // namespace webview
+#endif
+
+#endif /* WEBVIEW_GTK, WEBVIEW_COCOA, WEBVIEW_MSHTML, WEBVIEW_MSHTML */
+
+namespace webview {
+
+class webview : public browser_engine {
+public:
+  webview(bool debug = false, void *wnd = nullptr)
+      : browser_engine(
+            std::bind(&webview::on_message, this, std::placeholders::_1), debug,
+            wnd) {}
+
+  void *window() { return (void *)m_window; }
+
+  void navigate(const char *url) {
+    std::string html = html_from_uri(url);
+    if (html != "") {
+      browser_engine::navigate(("data:text/html," + url_encode(html)).c_str());
+    } else {
+      browser_engine::navigate(url);
+    }
+  }
+
+  using binding_t = std::function<std::string(std::string)>;
+
+  void bind(const char *name, binding_t f) {
+    auto js = "(function() { var name = '" + std::string(name) + "';" + R"(
+      window[name] = function() {
+        var me = window[name];
+        var errors = me['errors'];
+        var callbacks = me['callbacks'];
+        if (!callbacks) {
+          callbacks = {};
+          me['callbacks'] = callbacks;
+        }
+        if (!errors) {
+          errors = {};
+          me['errors'] = errors;
+        }
+        var seq = (me['lastSeq'] || 0) + 1;
+        me['lastSeq'] = seq;
+        var promise = new Promise(function(resolve, reject) {
+          callbacks[seq] = resolve;
+          errors[seq] = reject;
+        });
+        window.external.invoke(JSON.stringify({
+          name: name,
+          seq:seq,
+          args: Array.prototype.slice.call(arguments),
+        }));
+        return promise;
+      }
+    })())";
+    init(js.c_str());
+    bindings[name] = new binding_t(f);
+  }
+
+private:
+  void on_message(const char *msg) {
+    auto seq = json_parse(msg, "seq", 0);
+    auto name = json_parse(msg, "name", 0);
+    auto args = json_parse(msg, "args", 0);
+    auto fn = bindings[name];
+    if (fn == nullptr) {
+      return;
+    }
+    std::async(std::launch::async, [=]() {
+      auto result = (*fn)(args);
+      dispatch([=]() {
+        eval(("var b = window['" + name + "'];b['callbacks'][" + seq + "](" +
+              result + ");b['callbacks'][" + seq +
+              "] = undefined;b['errors'][" + seq + "] = undefined;")
+                 .c_str());
+      });
+    });
+  }
+  std::map<std::string, binding_t *> bindings;
+};
+} // namespace webview
+
+WEBVIEW_API webview_t webview_create(int debug, void *wnd) {
+  return new webview::webview(debug, wnd);
+}
+
+WEBVIEW_API void webview_destroy(webview_t w) {
+  delete static_cast<webview::webview *>(w);
+}
+
+WEBVIEW_API void webview_run(webview_t w) {
+  static_cast<webview::webview *>(w)->run();
+}
+
+WEBVIEW_API void webview_terminate(webview_t w) {
+  static_cast<webview::webview *>(w)->terminate();
+}
+
+WEBVIEW_API void
+webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg) {
+  static_cast<webview::webview *>(w)->dispatch([=]() { fn(w, arg); });
+}
+
+WEBVIEW_API void *webview_get_window(webview_t w) {
+  return static_cast<webview::webview *>(w)->window();
+}
+
+WEBVIEW_API void webview_set_title(webview_t w, const char *title) {
+  static_cast<webview::webview *>(w)->set_title(title);
+}
+
+WEBVIEW_API void webview_set_bounds(webview_t w, int x, int y, int width,
+                                    int height, int flags) {
+  // TODO: x, y, flags
+  static_cast<webview::webview *>(w)->set_size(width, height, true);
+}
+
+WEBVIEW_API void webview_get_bounds(webview_t w, int *x, int *y, int *width,
+                                    int *height, int *flags) {
+  // TODO
+}
+
+WEBVIEW_API void webview_navigate(webview_t w, const char *url) {
+  static_cast<webview::webview *>(w)->navigate(url);
+}
+
+WEBVIEW_API void webview_init(webview_t w, const char *js) {
+  static_cast<webview::webview *>(w)->init(js);
+}
+
+WEBVIEW_API void webview_eval(webview_t w, const char *js) {
+  static_cast<webview::webview *>(w)->eval(js);
+}
+
+#endif /* WEBVIEW_HEADER */
+
+#endif /* WEBVIEW_H */

+ 38 - 0
attic/webview/webview_test.cc

@@ -0,0 +1,38 @@
+// +build ignore
+
+#include "webview.h"
+
+#include <cstring>
+#include <cassert>
+
+static void test_terminate() {
+  webview::webview w(false, nullptr);
+  w.dispatch([&]() { w.terminate(); });
+  w.run();
+}
+
+static void cb_assert_arg(webview_t w, void *arg) {
+  assert(w != NULL);
+  assert(memcmp(arg, "arg", 3) == 0);
+}
+static void cb_terminate(webview_t w, void *arg) {
+  assert(arg == NULL);
+  webview_terminate(w);
+}
+static void test_c_api() {
+  webview_t w;
+  w = webview_create(false, NULL);
+  webview_set_bounds(w, 100, 100, 480, 320, 0);
+  webview_set_title(w, "Test");
+  webview_navigate(w, "https://github.com/zserge/webview");
+  webview_dispatch(w, cb_assert_arg, (void *)"arg");
+  webview_dispatch(w, cb_terminate, NULL);
+  webview_run(w);
+  webview_destroy(w);
+}
+
+int main() {
+  test_terminate();
+  test_c_api();
+  return 0;
+}

+ 2 - 0
controller/EmbeddedNetworkController.cpp

@@ -479,6 +479,7 @@ EmbeddedNetworkController::~EmbeddedNetworkController()
 void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender)
 {
 	char tmp[64];
+
 	_signingId = signingId;
 	_sender = sender;
 	_signingIdAddressString = signingId.address().toString(tmp);
@@ -1445,6 +1446,7 @@ void EmbeddedNetworkController::_request(
 
 	const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false);
 
+	// Set IPv6 static IPs based on NDP emulated schemes if enabled.
 	if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) {
 		if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
 			nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());

+ 3 - 3
controller/LFDB.cpp

@@ -43,10 +43,10 @@ LFDB::LFDB(const Identity &myId,const char *path,const char *lfOwnerPrivate,cons
 
 		// LF record masking key is the first 32 bytes of SHA512(controller private key) in hex,
 		// hiding record values from anything but the controller or someone who has its key.
-		uint8_t sha512pk[64];
-		_myId.sha512PrivateKey(sha512pk);
+		uint8_t sha384pk[48];
+		_myId.hash(sha384pk,true);
 		char maskingKey [128];
-		Utils::hex(sha512pk,32,maskingKey);
+		Utils::hex(sha384pk,32,maskingKey);
 
 		httplib::Client htcli(_lfNodeHost.c_str(),_lfNodePort,600);
 		int64_t timeRangeStart = 0;

+ 0 - 47
go/cmd/zerotier/cli/addroot.go

@@ -13,53 +13,6 @@
 
 package cli
 
-import (
-	"encoding/base64"
-	"fmt"
-	"io/ioutil"
-	"net/url"
-	"os"
-	"strings"
-
-	"zerotier/pkg/zerotier"
-)
-
 // AddRoot CLI command
 func AddRoot(basePath, authToken string, args []string) {
-	if len(args) == 0 {
-		Help()
-		os.Exit(0)
-	}
-
-	locData, err := ioutil.ReadFile(args[0])
-	if err != nil {
-		locData, err2 := base64.StdEncoding.DecodeString(strings.TrimSpace(args[0]))
-		if err2 != nil || len(locData) == 0 {
-			fmt.Printf("ERROR: unable to read locator: %s\n", err.Error())
-			os.Exit(1)
-		}
-	}
-	loc, err := zerotier.NewLocatorFromBytes(locData)
-	if err != nil {
-		fmt.Printf("ERROR: invalid locator '%s' (tried as file and base64 literal): %s\n", args[0], err.Error())
-		os.Exit(1)
-	}
-
-	var name string
-	if len(args) > 1 {
-		if len(args) > 2 {
-			Help()
-			os.Exit(1)
-		}
-		name = strings.TrimSpace(args[1])
-	}
-
-	var result zerotier.Root
-	apiPost(basePath, authToken, "/root/"+url.PathEscape(name), &zerotier.Root{
-		Name:    name,
-		Locator: loc,
-	}, &result)
-
-	fmt.Println(jsonDump(&result))
-	os.Exit(0)
 }

+ 2 - 6
go/cmd/zerotier/cli/help.go

@@ -41,12 +41,8 @@ Commands:
   status                               Show ZeroTier service status and config
   peers                                Show VL1 peers
   roots                                Show configured VL1 root servers
-  addroot <locator> [name]             Add a VL1 root
-  removeroot <name>                    Remove a VL1 root
-  locator <command> [args]             Locator management commands
-    new <identity> <address> [...]     Create and sign locator for identity
-    newdnskey                          Create a secure DNS name and secret
-    getdns <dns key> <locator>         Create secure DNS TXT records
+  addroot <identity>                   Add VL1 root server
+  removeroot <identity|address>        Remove VL1 root server
   identity <command> [args]            Identity management commands
     new [c25519|p384]                  Create new identity (including secret)
     getpublic <identity>               Extract only public part of identity

+ 0 - 137
go/cmd/zerotier/cli/locator.go

@@ -1,137 +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.
- */
-/****/
-
-package cli
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"strings"
-
-	"zerotier/pkg/zerotier"
-)
-
-func locatorNew(args []string) {
-	if len(args) < 2 {
-		Help()
-		os.Exit(1)
-	}
-
-	identity := readIdentity(args[0])
-	if !identity.HasPrivate() {
-		fmt.Println("FATAL: identity does not contain a secret key (required to sign locator)")
-		os.Exit(1)
-	}
-
-	var virt []*zerotier.Identity
-	var phys []*zerotier.InetAddress
-	for i := 1; i < len(args); i++ {
-		if strings.Contains(args[i], "/") {
-			a := zerotier.NewInetAddressFromString(args[i])
-			if a == nil {
-				fmt.Printf("FATAL: IP/port address '%s' is not valid\n", args[i])
-				os.Exit(1)
-			}
-			phys = append(phys, a)
-		} else {
-			a, err := zerotier.NewIdentityFromString(args[i])
-			if err != nil {
-				fmt.Printf("FATAL: identity (virtual address) '%s' is not valid: %s\n", args[i], err.Error())
-				os.Exit(1)
-			}
-			virt = append(virt, a)
-		}
-	}
-
-	loc, err := zerotier.NewLocator(identity, virt, phys)
-	if err != nil {
-		fmt.Printf("FATAL: internal error creating locator: %s\n", err.Error())
-		os.Exit(1)
-	}
-	fmt.Println(jsonDump(loc))
-	os.Exit(0)
-}
-
-func locatorNewDNSKey(args []string) {
-	if len(args) != 0 {
-		Help()
-		os.Exit(0)
-	}
-
-	sk, err := zerotier.NewLocatorDNSSigningKey()
-	if err != nil {
-		fmt.Printf("FATAL: error creating secure DNS signing key: %s", err.Error())
-		os.Exit(1)
-	}
-	fmt.Println(jsonDump(sk))
-	os.Exit(0)
-}
-
-func locatorGetDNS(args []string) {
-	if len(args) < 2 {
-		Help()
-		os.Exit(1)
-	}
-
-	keyData, err := ioutil.ReadFile(args[0])
-	if err != nil {
-		fmt.Printf("FATAL: unable to read secure DNS key file: %s\n", err.Error())
-		os.Exit(1)
-	}
-	var sk zerotier.LocatorDNSSigningKey
-	err = json.Unmarshal(keyData, &sk)
-	if err != nil {
-		fmt.Printf("FATAL: DNS key file invalid: %s", err.Error())
-		os.Exit(1)
-	}
-
-	locData, err := ioutil.ReadFile(args[1])
-	if err != nil {
-		fmt.Printf("FATAL: unable to read locator: %s\n", err.Error())
-		os.Exit(1)
-	}
-	var loc zerotier.Locator
-	err = json.Unmarshal(locData, &loc)
-	if err != nil {
-		fmt.Printf("FATAL: locator invalid: %s", err.Error())
-		os.Exit(1)
-	}
-
-	txt, err := loc.MakeTXTRecords(&sk)
-	if err != nil {
-		fmt.Printf("FATAL: error creating TXT records: %s\n", err.Error())
-		os.Exit(1)
-	}
-	for _, t := range txt {
-		fmt.Println(t)
-	}
-	os.Exit(0)
-}
-
-// Locator CLI command
-func Locator(args []string) {
-	if len(args) > 0 {
-		switch args[0] {
-		case "new":
-			locatorNew(args[1:])
-		case "newdnskey":
-			locatorNewDNSKey(args[1:])
-		case "getdns":
-			locatorGetDNS(args[1:])
-		}
-	}
-	Help()
-	os.Exit(1)
-}

+ 0 - 45
go/cmd/zerotier/cli/roots.go

@@ -13,51 +13,6 @@
 
 package cli
 
-import (
-	"fmt"
-	"os"
-
-	"zerotier/pkg/zerotier"
-)
-
 // Roots CLI command
 func Roots(basePath, authToken string, args []string, jsonOutput bool) {
-	var roots []zerotier.Root
-	apiGet(basePath, authToken, "/root", &roots)
-
-	if jsonOutput {
-		fmt.Println(jsonDump(roots))
-	} else {
-		fmt.Printf("%32s <address>  <physical/virtual>\n", "<name>")
-		for _, r := range roots {
-			rn := r.Name
-			if len(rn) > 32 {
-				rn = rn[len(rn)-32:]
-			}
-			if r.Locator != nil {
-				if len(r.Locator.Physical) == 0 && len(r.Locator.Virtual) == 0 {
-					fmt.Printf("%32s %.10x -\n", rn, uint64(r.Locator.Identity.Address()))
-				} else {
-					fmt.Printf("%32s %.10x ", rn, uint64(r.Locator.Identity.Address()))
-					for i, a := range r.Locator.Physical {
-						if i > 0 {
-							fmt.Print(",")
-						}
-						fmt.Print(a.String())
-					}
-					for i, a := range r.Locator.Virtual {
-						if i > 0 || len(r.Locator.Physical) > 0 {
-							fmt.Print(",")
-						}
-						fmt.Print(a.String())
-					}
-					fmt.Printf("\n")
-				}
-			} else {
-				fmt.Printf("%32s -          -\n", rn)
-			}
-		}
-	}
-
-	os.Exit(0)
 }

+ 1 - 0
go/cmd/zerotier/cli/service.go

@@ -18,6 +18,7 @@ import (
 	"os"
 	"os/signal"
 	"syscall"
+
 	"zerotier/pkg/zerotier"
 )
 

+ 0 - 2
go/cmd/zerotier/zerotier.go

@@ -127,8 +127,6 @@ func main() {
 	case "removeroot":
 		authTokenRequired(authToken)
 		cli.RemoveRoot(basePath, authToken, cmdArgs)
-	case "locator":
-		cli.Locator(cmdArgs)
 	case "identity":
 		cli.Identity(cmdArgs)
 	case "networks", "listnetworks":

+ 1 - 0
go/go.mod

@@ -5,4 +5,5 @@ go 1.13
 require (
 	github.com/Microsoft/go-winio v0.4.14
 	github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
+	golang.org/x/sys v0.0.0-20200107162124-548cf772de50 // indirect
 )

+ 2 - 0
go/go.sum

@@ -13,3 +13,5 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU=
 golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA=
+golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

+ 7 - 162
go/native/GoGlue.cpp

@@ -13,35 +13,25 @@
 
 #include "GoGlue.h"
 
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+
 #include "../../node/Constants.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/Address.hpp"
-#include "../../node/Locator.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/EthernetTap.hpp"
-#include "../../osdep/ManagedRoute.hpp"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
 
 #ifndef __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>
 #ifdef __BSD__
 #include <net/if.h>
 #endif
@@ -56,7 +46,6 @@
 #include <mutex>
 #include <map>
 #include <vector>
-#include <array>
 #include <set>
 #include <memory>
 #include <atomic>
@@ -111,10 +100,9 @@ const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str();
 
 /* These functions are implemented in Go in pkg/ztnode/node-callbacks.go */
 extern "C" int goPathCheckFunc(void *,uint64_t,int,const void *,int);
-extern "C" int goPathLookupFunc(void *,uint64_t,int,int *,uint8_t [16],int *);
+extern "C" int goPathLookupFunc(void *,uint64_t,int,const ZT_Identity *,int *,uint8_t [16],int *);
 extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int);
 extern "C" int goStateObjectGetFunc(void *,int,const uint64_t [2],void *,unsigned int);
-extern "C" void goDNSResolverFunc(void *,const uint8_t *,int,const char *,uintptr_t);
 extern "C" void goVirtualNetworkConfigFunc(void *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *);
 extern "C" void goZtEvent(void *,int,const void *);
 extern "C" void goHandleTapAddedMulticastGroup(void *,ZT_GoTap *,uint64_t,uint64_t,uint32_t);
@@ -279,6 +267,7 @@ static int ZT_GoNode_PathLookupFunction(
 	void *uptr,
 	void *tptr,
 	uint64_t ztAddress,
+	const ZT_Identity *id,
 	int desiredAddressFamily,
 	struct sockaddr_storage *sa)
 {
@@ -289,6 +278,7 @@ static int ZT_GoNode_PathLookupFunction(
 		reinterpret_cast<ZT_GoNode *>(uptr)->goUserPtr,
 		ztAddress,
 		desiredAddressFamily,
+		id,
 		&family,
 		ip,
 		&port
@@ -310,20 +300,6 @@ static int ZT_GoNode_PathLookupFunction(
 	return 0;
 }
 
-static void ZT_GoNode_DNSResolver(
-	ZT_Node *node,
-	void *uptr,
-	void *tptr,
-	const enum ZT_DNSRecordType *types,
-	unsigned int numTypes,
-	const char *name,
-	uintptr_t requestId)
-{
-	uint8_t t[256];
-	for(unsigned int i=0;(i<numTypes)&&(i<256);++i) t[i] = (uint8_t)types[i];
-	goDNSResolverFunc(reinterpret_cast<ZT_GoNode *>(uptr)->goUserPtr,t,(int)numTypes,name,requestId);
-}
-
 /****************************************************************************/
 
 extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
@@ -336,7 +312,6 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
 		cb.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction;
 		cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction;
 		cb.eventCallback = &ZT_GoNode_EventCallback;
-		cb.dnsResolver = &ZT_GoNode_DNSResolver;
 		cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction;
 		cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction;
 
@@ -727,133 +702,3 @@ extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targe
 	}
 	return reinterpret_cast<EthernetTap *>(tap)->removeRoute(target,via,metric);
 }
-
-/****************************************************************************/
-
-extern "C" const char *ZT_GoIdentity_generate(int type)
-{
-	Identity id;
-	id.generate((Identity::Type)type);
-	char *tmp = (char *)malloc(ZT_IDENTITY_STRING_BUFFER_LENGTH);
-	if (tmp)
-		id.toString(true,tmp);
-	return tmp;
-}
-
-extern "C" int ZT_GoIdentity_validate(const char *idStr)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	if (!id.locallyValidate())
-		return 0;
-	return 1;
-}
-
-extern "C" int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	return (int)id.sign(data,len,sigbuf,sigbuflen);
-}
-
-extern "C" int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	return id.verify(data,len,sig,siglen) ? 1 : 0;
-}
-
-/****************************************************************************/
-
-extern "C" int ZT_GoLocator_makeSecureDNSName(char *name,unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize)
-{
-	if ((privateKeyBufSize < ZT_ECC384_PRIVATE_KEY_SIZE)||(nameBufSize < 256))
-		return -1;
-	uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
-	ECC384GenerateKey(pub,privateKey);
-	const Str n(Locator::makeSecureDnsName(pub));
-	if (n.length() >= nameBufSize)
-		return -1;
-	Utils::scopy(name,nameBufSize,n.c_str());
-	return ZT_ECC384_PRIVATE_KEY_SIZE;
-}
-
-extern "C" int ZT_GoLocator_makeLocator(
-	uint8_t *buf,
-	unsigned int bufSize,
-	int64_t ts,
-	const char *id,
-	const struct sockaddr_storage *physicalAddresses,
-	unsigned int physicalAddressCount,
-	const char **virtualAddresses,
-	unsigned int virtualAddressCount)
-{
-	Locator loc;
-	for(unsigned int i=0;i<physicalAddressCount;++i) {
-		loc.add(*reinterpret_cast<const InetAddress *>(physicalAddresses + i));
-	}
-	for(unsigned int i=0;i<virtualAddressCount;++i) {
-		Identity id;
-		if (!id.fromString(virtualAddresses[i]))
-			return -1;
-		loc.add(id);
-	}
-	Identity signingId;
-	if (!signingId.fromString(id))
-		return -1;
-	if (!signingId.hasPrivate())
-		return -1;
-	if (!loc.finish(signingId,ts))
-		return -1;
-	Buffer<65536> *tmp = new Buffer<65536>();
-	loc.serialize(*tmp);
-	if (tmp->size() > bufSize) {
-		delete tmp;
-		return -1;
-	}
-	memcpy(buf,tmp->data(),tmp->size());
-	int s = (int)tmp->size();
-	delete tmp;
-	return s;
-}
-
-extern "C" int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info)
-{
-	Locator loc;
-	if (!loc.deserialize(locatorBytes,locatorSize))
-		return -1;
-	if (!loc.verify())
-		return -2;
-	loc.id().toString(false,info->id);
-	info->phyCount = 0;
-	info->virtCount = 0;
-	for(auto p=loc.phy().begin();p!=loc.phy().end();++p)
-		memcpy(&(info->phy[info->phyCount++]),&(*p),sizeof(struct sockaddr_storage));
-	for(auto v=loc.virt().begin();v!=loc.virt().end();++v)
-		v->toString(false,info->virt[info->virtCount++]);
-	return 1;
-}
-
-int ZT_GoLocator_makeSignedTxtRecords(
-	const uint8_t *locator,
-	unsigned int locatorSize,
-	const char *name,
-	const uint8_t *privateKey,
-	unsigned int privateKeySize,
-	char results[256][256])
-{
-	if (privateKeySize != ZT_ECC384_PRIVATE_KEY_SIZE)
-		return -1;
-	Locator loc;
-	if (!loc.deserialize(locator,locatorSize))
-		return -1;
-	std::vector<Str> r(loc.makeTxtRecords(privateKey));
-	if (r.size() > 256)
-		return -1;
-	for(unsigned long i=0;i<r.size();++i)
-		Utils::scopy(results[i],256,r[i].c_str());
-	return (int)r.size();
-}

+ 9 - 74
go/native/GoGlue.h

@@ -14,14 +14,22 @@
 #ifndef ZT_GONODE_H
 #define ZT_GONODE_H
 
+#ifdef __cplusplus
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#else
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#endif
 
 #include "../../include/ZeroTierCore.h"
 #include "../../node/Constants.hpp"
 
-/****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 /* A pointer to an instance of EthernetTap */
 typedef void ZT_GoTap;
@@ -32,18 +40,8 @@ typedef void ZT_GoTap;
 struct ZT_GoNode_Impl;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 
-/****************************************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/****************************************************************************/
-
 extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH;
 
-/****************************************************************************/
-
 ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr);
 
 void ZT_GoNode_delete(ZT_GoNode *gn);
@@ -60,8 +58,6 @@ ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid);
 
 void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid);
 
-/****************************************************************************/
-
 void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled);
 
 int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
@@ -88,67 +84,6 @@ int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int target
 
 int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric);
 
-/****************************************************************************/
-
-const char *ZT_GoIdentity_generate(int type);
-
-int ZT_GoIdentity_validate(const char *idStr);
-
-int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen);
-
-int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen);
-
-/****************************************************************************/
-
-struct ZT_GoLocator_Info {
-	char id[1024];
-	unsigned int phyCount;
-	unsigned int virtCount;
-	struct sockaddr_storage phy[256];
-	char virt[256][1024];
-};
-
-/* Returns length of private key stored in private key buffer on success, -1 on fail */
-int ZT_GoLocator_makeSecureDNSName(char name[256],unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize);
-
-/*
- * The id is the full identity described by the locator. It must include
- * its secret key to permit the locator to be signed.
- *
- * Physical addresses must be IPv4 or IPv6 IP/port pairs. Virtual addresses
- * must be full ZeroTier identities in string format.
- *
- * On success this returns the actual number of bytes stored in the buffer.
- * On failure -1 is returned.
- */
-int ZT_GoLocator_makeLocator(
-	uint8_t *buf,
-	unsigned int bufSize,
-	int64_t ts,
-	const char *id,
-	const struct sockaddr_storage *physicalAddresses,
-	unsigned int physicalAddressCount,
-	const char **virtualAddresses,
-	unsigned int virtualAddressCount);
-
-/* Returns >0 on success, fills info structure */
-int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info);
-
-/*
- * The privateKey and privateKeySize are those created by makeSecureDNSName.
- * Results is filled and the number of lines of TXT are returned. The value
- * -1 is returned on error.
- */
-int ZT_GoLocator_makeSignedTxtRecords(
-	const uint8_t *locator,
-	unsigned int locatorSize,
-	const char *name,
-	const uint8_t *privateKey,
-	unsigned int privateKeySize,
-	char results[256][256]);
-
-/****************************************************************************/
-
 #ifdef __cplusplus
 }
 #endif

+ 4 - 47
go/pkg/zerotier/api.go

@@ -460,57 +460,14 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e
 		}
 		apiSetStandardHeaders(out)
 
-		var queriedName string
-		if len(req.URL.Path) > 6 {
-			queriedName = req.URL.Path[6:]
-		}
+		//var queriedName string
+		//if len(req.URL.Path) > 6 {
+		//	queriedName = req.URL.Path[6:]
+		//}
 
 		if req.Method == http.MethodDelete {
-			if len(queriedName) > 0 {
-				roots := node.Roots()
-				for _, r := range roots {
-					if r.Name == queriedName {
-						node.RemoveRoot(queriedName)
-						_ = apiSendObj(out, req, http.StatusOK, r)
-						return
-					}
-				}
-			}
-			_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"})
 		} else if req.Method == http.MethodPost || req.Method == http.MethodPut {
-			if len(queriedName) == 0 {
-				_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual roots can be added or modified with POST/PUT"})
-				return
-			}
-			var r Root
-			if apiReadObj(out, req, &r) == nil {
-				if r.Name != queriedName {
-					_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"root name does not match name in path"})
-					return
-				}
-				err := node.SetRoot(r.Name, r.Locator)
-				if err != nil {
-					_ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"set/update root failed: " + err.Error()})
-				} else {
-					roots := node.Roots()
-					for _, r := range roots {
-						if r.Name == queriedName {
-							_ = apiSendObj(out, req, http.StatusOK, r)
-							return
-						}
-					}
-					_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"set/update root failed: root set but not subsequently found in list"})
-				}
-			}
 		} else if req.Method == http.MethodGet || req.Method == http.MethodHead {
-			roots := node.Roots()
-			for _, r := range roots {
-				if r.Name == queriedName {
-					_ = apiSendObj(out, req, http.StatusOK, r)
-					return
-				}
-			}
-			_ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"})
 		} else {
 			out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
 			_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method})

+ 42 - 13
go/pkg/zerotier/identity.go

@@ -13,7 +13,6 @@
 
 package zerotier
 
-//#cgo CFLAGS: -O3
 //#include "../../native/GoGlue.h"
 import "C"
 
@@ -47,15 +46,26 @@ type Identity struct {
 	privateKey []byte
 }
 
+func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) {
+	if uintptr(cid) == 0 {
+		return nil, ErrInvalidParameter
+	}
+	var idStrBuf [4096]byte
+	idStr := C.ZT_Identity_toString(cid,(*C.char)(unsafe.Pointer(&idStrBuf[0])),4096,1)
+	if uintptr(unsafe.Pointer(idStr)) == 0 {
+		return nil, ErrInternal
+	}
+	return NewIdentityFromString(C.GoString(idStr))
+}
+
 // NewIdentity generates a new identity of the selected type
 func NewIdentity(identityType int) (*Identity, error) {
-	cIdStr := C.ZT_GoIdentity_generate(C.int(identityType))
-	if uintptr(unsafe.Pointer(cIdStr)) == 0 {
+	cid := C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType))
+	if uintptr(unsafe.Pointer(cid)) == 0 {
 		return nil, ErrInternal
 	}
-	id, err := NewIdentityFromString(C.GoString(cIdStr))
-	C.free(unsafe.Pointer(cIdStr))
-	return id, err
+	defer C.ZT_Identity_delete(cid)
+	return newIdentityFromCIdentity(cid)
 }
 
 // NewIdentityFromString generates a new identity from its string representation.
@@ -159,23 +169,35 @@ func (id *Identity) String() string {
 func (id *Identity) LocallyValidate() bool {
 	idCStr := C.CString(id.String())
 	defer C.free(unsafe.Pointer(idCStr))
-	return C.ZT_GoIdentity_validate(idCStr) != 0
+	cid := C.ZT_Identity_fromString(idCStr)
+	if uintptr(cid) == 0 {
+		return false
+	}
+	defer C.ZT_Identity_delete(cid)
+	return C.ZT_Identity_validate(cid) != 0
 }
 
 // Sign signs a message with this identity
 func (id *Identity) Sign(msg []byte) ([]byte, error) {
 	idCStr := C.CString(id.PrivateKeyString())
-	var sigbuf [96]byte
+	defer C.free(unsafe.Pointer(idCStr))
+	cid := C.ZT_Identity_fromString(idCStr)
+	if uintptr(cid) == 0 {
+		return nil, ErrInvalidKey
+	}
+	defer C.ZT_Identity_delete(cid)
+
 	var dataP unsafe.Pointer
 	if len(msg) > 0 {
 		dataP = unsafe.Pointer(&msg[0])
 	}
-	siglen := C.ZT_GoIdentity_sign(idCStr, dataP, C.uint(len(msg)), unsafe.Pointer(&sigbuf[0]), C.uint(len(sigbuf)))
-	C.free(unsafe.Pointer(idCStr))
-	if siglen <= 0 {
+	var sig [96]byte
+	sigLen := C.ZT_Identity_sign(cid,dataP,C.uint(len(msg)),unsafe.Pointer(&sig[0]),96)
+	if sigLen <= 0 {
 		return nil, ErrInvalidKey
 	}
-	return sigbuf[0:int(siglen)], nil
+
+	return sig[0:uint(sigLen)], nil
 }
 
 // Verify verifies a signature
@@ -183,13 +205,20 @@ func (id *Identity) Verify(msg, sig []byte) bool {
 	if len(sig) == 0 {
 		return false
 	}
+
 	idCStr := C.CString(id.String())
 	defer C.free(unsafe.Pointer(idCStr))
+	cid := C.ZT_Identity_fromString(idCStr)
+	if uintptr(cid) == 0 {
+		return false
+	}
+	defer C.ZT_Identity_delete(cid)
+
 	var dataP unsafe.Pointer
 	if len(msg) > 0 {
 		dataP = unsafe.Pointer(&msg[0])
 	}
-	return C.ZT_GoIdentity_verify(idCStr, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0
+	return C.ZT_Identity_verify(cid,dataP,C.uint(len(msg)),unsafe.Pointer(&sig[0]),C.uint(len(sig))) != 0
 }
 
 // MarshalJSON marshals this Identity in its string format (private key is never included)

+ 1 - 1
go/pkg/zerotier/localconfig.go

@@ -33,7 +33,7 @@ type LocalConfigPhysicalPathConfiguration struct {
 // LocalConfigVirtualAddressConfiguration contains settings for virtual addresses
 type LocalConfigVirtualAddressConfiguration struct {
 	// Try is a list of IPs/ports to try for this peer in addition to anything learned from roots or direct path push
-	Try []InetAddress `json:",omitempty"`
+	Try []InetAddress `json:"try,omitempty"`
 }
 
 // ExternalAddress is an externally visible address

+ 0 - 191
go/pkg/zerotier/locator.go

@@ -1,191 +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.
- */
-/****/
-
-package zerotier
-
-//#cgo CFLAGS: -O3
-//#include "../../native/GoGlue.h"
-import "C"
-
-import (
-	"encoding/json"
-	"unsafe"
-)
-
-// LocatorDNSSigningKey is the public (as a secure DNS name) and private keys for entering locators into DNS
-type LocatorDNSSigningKey struct {
-	SecureDNSName string `json:"secureDNSName"`
-	PrivateKey    []byte `json:"privateKey"`
-}
-
-// NewLocatorDNSSigningKey creates a new signing key and secure DNS name for storing locators in DNS
-func NewLocatorDNSSigningKey() (*LocatorDNSSigningKey, error) {
-	var nameBuf [256]C.char
-	var keyBuf [64]byte
-	keySize := int(C.ZT_GoLocator_makeSecureDNSName(&nameBuf[0], 256, (*C.uint8_t)(unsafe.Pointer(&keyBuf[0])), 128))
-	if keySize <= 0 {
-		return nil, ErrInternal
-	}
-	var sk LocatorDNSSigningKey
-	sk.SecureDNSName = C.GoString(&nameBuf[0])
-	sk.PrivateKey = keyBuf[0:keySize]
-	return &sk, nil
-}
-
-// Locator is a binary serialized record containing information about where a ZeroTier node is located on the network.
-// Note that for JSON objects only Bytes needs to be specified. When JSON is deserialized only this field is used
-// and the others are always reconstructed from it.
-type Locator struct {
-	// Identity is the full identity of the node being located
-	Identity *Identity `json:"identity"`
-
-	// Physical is a list of static physical network addresses for this node
-	Physical []*InetAddress `json:"physical,omitempty"`
-
-	// Virtual is a list of ZeroTier nodes that can relay to this node
-	Virtual []*Identity `json:"virtual,omitempty"`
-
-	// Bytes is the raw serialized Locator
-	Bytes []byte `json:"bytes,omitempty"`
-}
-
-// NewLocator creates a new locator with the given identity and addresses and the current time as timestamp.
-// The identity must include its secret key so that it can sign the final locator.
-func NewLocator(id *Identity, virtualAddresses []*Identity, physicalAddresses []*InetAddress) (*Locator, error) {
-	if !id.HasPrivate() {
-		return nil, ErrSecretKeyRequired
-	}
-
-	idstr := id.PrivateKeyString()
-	phy := make([]C.struct_sockaddr_storage, len(physicalAddresses))
-	virt := make([]*C.char, len(virtualAddresses))
-	idCstr := C.CString(idstr)
-
-	defer func() {
-		C.free(unsafe.Pointer(idCstr))
-		for _, v := range virt {
-			if uintptr(unsafe.Pointer(v)) != 0 {
-				C.free(unsafe.Pointer(v))
-			}
-		}
-	}()
-
-	for i := 0; i < len(physicalAddresses); i++ {
-		if !makeSockaddrStorage(physicalAddresses[i].IP, physicalAddresses[i].Port, &phy[i]) {
-			return nil, ErrInvalidParameter
-		}
-	}
-
-	for i := 0; i < len(virtualAddresses); i++ {
-		idstr := virtualAddresses[i].String()
-		virt[i] = C.CString(idstr)
-	}
-
-	var buf [65536]byte
-	var pPhy *C.struct_sockaddr_storage
-	var pVirt **C.char
-	if len(phy) > 0 {
-		pPhy = &phy[0]
-	}
-	if len(virt) > 0 {
-		pVirt = &virt[0]
-	}
-	locSize := C.ZT_GoLocator_makeLocator((*C.uint8_t)(unsafe.Pointer(&buf[0])), 65536, C.int64_t(TimeMs()), idCstr, pPhy, C.uint(len(phy)), pVirt, C.uint(len(virt)))
-	if locSize <= 0 {
-		return nil, ErrInvalidParameter
-	}
-
-	r := make([]byte, int(locSize))
-	copy(r[:], buf[0:int(locSize)])
-	return &Locator{
-		Identity: id,
-		Physical: physicalAddresses,
-		Virtual:  virtualAddresses,
-		Bytes:    r,
-	}, nil
-}
-
-// NewLocatorFromBytes decodes a locator from its serialized byte array form
-func NewLocatorFromBytes(b []byte) (*Locator, error) {
-	if len(b) == 0 {
-		return nil, ErrInvalidParameter
-	}
-	var info C.struct_ZT_GoLocator_Info
-	res := C.ZT_GoLocator_decodeLocator((*C.uint8_t)(unsafe.Pointer(&b[0])), C.uint(len(b)), &info)
-	if res == -2 {
-		return nil, ErrInvalidSignature
-	} else if res <= 0 {
-		return nil, ErrInvalidParameter
-	}
-
-	var loc Locator
-
-	var err error
-	loc.Identity, err = NewIdentityFromString(C.GoString(&info.id[0]))
-	if err != nil {
-		return nil, err
-	}
-	for i := 0; i < int(info.phyCount); i++ {
-		ua := sockaddrStorageToUDPAddr(&info.phy[i])
-		if ua != nil {
-			loc.Physical = append(loc.Physical, &InetAddress{IP: ua.IP, Port: ua.Port})
-		}
-	}
-	for i := 0; i < int(info.virtCount); i++ {
-		id, err := NewIdentityFromString(C.GoString(&info.virt[i][0]))
-		if err == nil {
-			loc.Virtual = append(loc.Virtual, id)
-		}
-	}
-	loc.Bytes = b
-
-	return &loc, nil
-}
-
-// MakeTXTRecords creates secure DNS TXT records for this locator
-func (l *Locator) MakeTXTRecords(key *LocatorDNSSigningKey) ([]string, error) {
-	if key == nil || len(l.Bytes) == 0 || len(key.PrivateKey) == 0 {
-		return nil, ErrInvalidParameter
-	}
-	var results [256][256]C.char
-	cName := C.CString(key.SecureDNSName)
-	defer C.free(unsafe.Pointer(cName))
-	count := int(C.ZT_GoLocator_makeSignedTxtRecords((*C.uint8_t)(&l.Bytes[0]), C.uint(len(l.Bytes)), cName, (*C.uint8_t)(&key.PrivateKey[0]), C.uint(len(key.PrivateKey)), &results[0]))
-	if count > 0 {
-		var t []string
-		for i := 0; i < int(count); i++ {
-			t = append(t, C.GoString(&results[i][0]))
-		}
-		return t, nil
-	}
-	return nil, ErrInternal
-}
-
-type locatorForUnmarshal struct {
-	Bytes []byte `json:"bytes,omitempty"`
-}
-
-// UnmarshalJSON unmarshals this Locator from a byte array in JSON.
-func (l *Locator) UnmarshalJSON(j []byte) error {
-	var bytes locatorForUnmarshal
-	err := json.Unmarshal(j, &bytes)
-	if err != nil {
-		return err
-	}
-	tmp, err := NewLocatorFromBytes(bytes.Bytes)
-	if err != nil {
-		return err
-	}
-	*l = *tmp
-	return nil
-}

+ 8 - 136
go/pkg/zerotier/node.go

@@ -595,62 +595,6 @@ func (n *Node) Networks() []*Network {
 	return nws
 }
 
-// Roots retrieves a list of root servers on this node and their preferred and online status.
-func (n *Node) Roots() []*Root {
-	var roots []*Root
-	rl := C.ZT_Node_listRoots(unsafe.Pointer(n.zn), C.int64_t(TimeMs()))
-	if rl != nil {
-		for i := 0; i < int(rl.count); i++ {
-			root := (*C.ZT_Root)(unsafe.Pointer(uintptr(unsafe.Pointer(rl)) + C.sizeof_ZT_RootList))
-			loc, _ := NewLocatorFromBytes(C.GoBytes(root.locator, C.int(root.locatorSize)))
-			if loc != nil {
-				roots = append(roots, &Root{
-					Name:    C.GoString(root.name),
-					Locator: loc,
-				})
-			}
-		}
-		C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(rl))
-	}
-	return roots
-}
-
-// SetRoot sets or updates a root.
-// Name can be a DNS name (preferably secure) for DNS fetched locators or can be
-// the empty string for static roots. If the name is empty then the locator must
-// be non-nil.
-func (n *Node) SetRoot(name string, locator *Locator) error {
-	if len(name) == 0 {
-		if locator == nil {
-			return ErrInvalidParameter
-		}
-		name = locator.Identity.address.String()
-	}
-	var lb []byte
-	if locator != nil {
-		lb = locator.Bytes
-	}
-	var lbp unsafe.Pointer
-	if len(lb) > 0 {
-		lbp = unsafe.Pointer(&lb[0])
-	}
-	cn := C.CString(name)
-	defer C.free(unsafe.Pointer(cn))
-	if C.ZT_Node_setRoot(unsafe.Pointer(n.zn), cn, lbp, C.uint(len(lb))) != 0 {
-		return ErrInternal
-	}
-	return nil
-}
-
-// RemoveRoot removes a root.
-// For static roots the name should be the ZeroTier address.
-func (n *Node) RemoveRoot(name string) {
-	cn := C.CString(name)
-	defer C.free(unsafe.Pointer(cn))
-	C.ZT_Node_removeRoot(unsafe.Pointer(n.zn), cn)
-	return
-}
-
 // Peers retrieves a list of current peers
 func (n *Node) Peers() []*Peer {
 	var peers []*Peer
@@ -665,59 +609,21 @@ func (n *Node) Peers() []*Peer {
 			p2.Role = int(p.role)
 
 			p2.Paths = make([]Path, 0, int(p.pathCount))
-			usingAllocation := false
 			for j := uintptr(0); j < uintptr(p.pathCount); j++ {
 				pt := &p.paths[j]
 				if pt.alive != 0 {
 					a := sockaddrStorageToUDPAddr(&pt.address)
 					if a != nil {
-						alloc := float32(pt.allocation)
-						if alloc > 0.0 {
-							usingAllocation = true
-						}
 						p2.Paths = append(p2.Paths, Path{
 							IP:                     a.IP,
 							Port:                   a.Port,
 							LastSend:               int64(pt.lastSend),
 							LastReceive:            int64(pt.lastReceive),
 							TrustedPathID:          uint64(pt.trustedPathId),
-							Latency:                float32(pt.latency),
-							PacketDelayVariance:    float32(pt.packetDelayVariance),
-							ThroughputDisturbCoeff: float32(pt.throughputDisturbCoeff),
-							PacketErrorRatio:       float32(pt.packetErrorRatio),
-							PacketLossRatio:        float32(pt.packetLossRatio),
-							Stability:              float32(pt.stability),
-							Throughput:             uint64(pt.throughput),
-							MaxThroughput:          uint64(pt.maxThroughput),
-							Allocation:             alloc,
 						})
 					}
 				}
 			}
-			if !usingAllocation { // if all allocations are zero fall back to single path mode that uses the preferred flag
-				for i, j := 0, uintptr(0); j < uintptr(p.pathCount); j++ {
-					pt := &p.paths[j]
-					if pt.alive != 0 {
-						if pt.preferred == 0 {
-							p2.Paths[i].Allocation = 0.0
-						} else {
-							p2.Paths[i].Allocation = 1.0
-						}
-						i++
-					}
-				}
-			}
-			sort.Slice(p2.Paths, func(a, b int) bool {
-				pa := &p2.Paths[a]
-				pb := &p2.Paths[b]
-				if pb.Allocation < pa.Allocation { // invert order, put highest allocation paths first
-					return true
-				}
-				if pa.Allocation == pb.Allocation {
-					return pa.LastReceive < pb.LastReceive // then sort by most recent activity
-				}
-				return false
-			})
 
 			p2.Clock = TimeMs()
 			peers = append(peers, p2)
@@ -754,10 +660,10 @@ func (n *Node) pathCheck(ztAddress Address, af int, ip net.IP, port int) bool {
 	return true
 }
 
-func (n *Node) pathLookup(ztAddress Address) (net.IP, int) {
+func (n *Node) pathLookup(id *Identity) (net.IP, int) {
 	n.localConfigLock.RLock()
 	defer n.localConfigLock.RUnlock()
-	virt := n.localConfig.Virtual[ztAddress]
+	virt := n.localConfig.Virtual[id.address]
 	if len(virt.Try) > 0 {
 		idx := rand.Int() % len(virt.Try)
 		return virt.Try[idx].IP, virt.Try[idx].Port
@@ -831,9 +737,6 @@ func (n *Node) handleTrace(traceMessage string) {
 func (n *Node) handleUserMessage(originAddress, messageTypeID uint64, data []byte) {
 }
 
-func (n *Node) handleRemoteTrace(originAddress uint64, dictData []byte) {
-}
-
 //////////////////////////////////////////////////////////////////////////////
 
 // These are callbacks called by the core and GoGlue stuff to talk to the
@@ -860,7 +763,7 @@ func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsaf
 }
 
 //export goPathLookupFunc
-func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, ipP, portP unsafe.Pointer) C.int {
+func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredFamily int, identity, familyP, ipP, portP unsafe.Pointer) C.int {
 	nodesByUserPtrLock.RLock()
 	node := nodesByUserPtr[uintptr(gn)]
 	nodesByUserPtrLock.RUnlock()
@@ -868,7 +771,11 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, i
 		return 0
 	}
 
-	ip, port := node.pathLookup(Address(ztAddress))
+	id, err := newIdentityFromCIdentity(identity)
+	if err != nil {
+		return 0
+	}
+	ip, port := node.pathLookup(id)
 	if len(ip) > 0 && port > 0 && port <= 65535 {
 		ip4 := ip.To4()
 		if len(ip4) == 4 {
@@ -922,38 +829,6 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Poin
 	return -1
 }
 
-//export goDNSResolverFunc
-func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) {
-	go func() {
-		nodesByUserPtrLock.RLock()
-		node := nodesByUserPtr[uintptr(gn)]
-		nodesByUserPtrLock.RUnlock()
-		if node == nil {
-			return
-		}
-
-		recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes)
-		recordName := C.GoString((*C.char)(name))
-
-		recordNameCStrCopy := C.CString(recordName)
-		for _, rt := range recordTypes {
-			switch rt {
-			case C.ZT_DNS_RECORD_TXT:
-				recs, _ := net.LookupTXT(recordName)
-				for _, rec := range recs {
-					if len(rec) > 0 {
-						rnCS := C.CString(rec)
-						C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0)
-						C.free(unsafe.Pointer(rnCS))
-					}
-				}
-			}
-		}
-		C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0)
-		C.free(unsafe.Pointer(recordNameCStrCopy))
-	}()
-}
-
 //export goVirtualNetworkConfigFunc
 func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
 	go func() {
@@ -1032,9 +907,6 @@ func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) {
 		case C.ZT_EVENT_USER_MESSAGE:
 			um := (*C.ZT_UserMessage)(data)
 			node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length)))
-		case C.ZT_EVENT_REMOTE_TRACE:
-			rt := (*C.ZT_RemoteTrace)(data)
-			node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len)))
 		}
 	}()
 }

+ 0 - 9
go/pkg/zerotier/path.go

@@ -22,13 +22,4 @@ type Path struct {
 	LastSend               int64   `json:"lastSend"`
 	LastReceive            int64   `json:"lastReceive"`
 	TrustedPathID          uint64  `json:"trustedPathID"`
-	Latency                float32 `json:"latency"`
-	PacketDelayVariance    float32 `json:"packetDelayVariance"`
-	ThroughputDisturbCoeff float32 `json:"throughputDisturbCoeff"`
-	PacketErrorRatio       float32 `json:"packetErrorRatio"`
-	PacketLossRatio        float32 `json:"packetLossRatio"`
-	Stability              float32 `json:"stability"`
-	Throughput             uint64  `json:"throughput"`
-	MaxThroughput          uint64  `json:"maxThroughput"`
-	Allocation             float32 `json:"allocation"`
 }

+ 4 - 16
go/pkg/zerotier/root.go

@@ -1,20 +1,8 @@
-/*
- * 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.
- */
-/****/
-
 package zerotier
 
-// Root describes a root server used to find and establish communication with other nodes.
+// Root is a root server with one or more permanent IPs.
 type Root struct {
-	Name    string   `json:"name"`
-	Locator *Locator `json:"locator,omitempty"`
+	Identity Identity
+	DNSName string
+	PhysicalAddresses []InetAddress
 }

+ 0 - 13
go/vendor/golang.org/x/sys/windows/asm_windows_386.s

@@ -1,13 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//
-// System calls for 386, Windows are implemented in runtime/syscall_windows.goc
-//
-
-TEXT ·getprocaddress(SB), 7, $0-16
-	JMP	syscall·getprocaddress(SB)
-
-TEXT ·loadlibrary(SB), 7, $0-12
-	JMP	syscall·loadlibrary(SB)

+ 0 - 13
go/vendor/golang.org/x/sys/windows/asm_windows_amd64.s

@@ -1,13 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//
-// System calls for amd64, Windows are implemented in runtime/syscall_windows.goc
-//
-
-TEXT ·getprocaddress(SB), 7, $0-32
-	JMP	syscall·getprocaddress(SB)
-
-TEXT ·loadlibrary(SB), 7, $0-24
-	JMP	syscall·loadlibrary(SB)

+ 0 - 11
go/vendor/golang.org/x/sys/windows/asm_windows_arm.s

@@ -1,11 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-#include "textflag.h"
-
-TEXT ·getprocaddress(SB),NOSPLIT,$0
-	B	syscall·getprocaddress(SB)
-
-TEXT ·loadlibrary(SB),NOSPLIT,$0
-	B	syscall·loadlibrary(SB)

+ 15 - 7
go/vendor/golang.org/x/sys/windows/dll_windows.go

@@ -11,6 +11,18 @@ import (
 	"unsafe"
 )
 
+// We need to use LoadLibrary and GetProcAddress from the Go runtime, because
+// the these symbols are loaded by the system linker and are required to
+// dynamically load additional symbols. Note that in the Go runtime, these
+// return syscall.Handle and syscall.Errno, but these are the same, in fact,
+// as windows.Handle and windows.Errno, and we intend to keep these the same.
+
+//go:linkname syscall_loadlibrary syscall.loadlibrary
+func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno)
+
+//go:linkname syscall_getprocaddress syscall.getprocaddress
+func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno)
+
 // DLLError describes reasons for DLL load failures.
 type DLLError struct {
 	Err     error
@@ -20,10 +32,6 @@ type DLLError struct {
 
 func (e *DLLError) Error() string { return e.Msg }
 
-// Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file.
-func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno)
-func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno)
-
 // A DLL implements access to a single DLL.
 type DLL struct {
 	Name   string
@@ -40,7 +48,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
 	if err != nil {
 		return nil, err
 	}
-	h, e := loadlibrary(namep)
+	h, e := syscall_loadlibrary(namep)
 	if e != 0 {
 		return nil, &DLLError{
 			Err:     e,
@@ -50,7 +58,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
 	}
 	d := &DLL{
 		Name:   name,
-		Handle: Handle(h),
+		Handle: h,
 	}
 	return d, nil
 }
@@ -71,7 +79,7 @@ func (d *DLL) FindProc(name string) (proc *Proc, err error) {
 	if err != nil {
 		return nil, err
 	}
-	a, e := getprocaddress(uintptr(d.Handle), namep)
+	a, e := syscall_getprocaddress(d.Handle, namep)
 	if e != 0 {
 		return nil, &DLLError{
 			Err:     e,

+ 3 - 4
go/vendor/golang.org/x/sys/windows/mkerrors.go → go/vendor/golang.org/x/sys/windows/empty.s

@@ -2,8 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build generate
+// +build !go1.12
 
-package windows
-
-//go:generate ./mkerrors.bash zerrors_windows.go
+// This file is here to allow bodyless functions with go:linkname for Go 1.11
+// and earlier (see https://golang.org/issue/23311).

+ 2 - 3
go/vendor/golang.org/x/sys/windows/mkerrors.bash

@@ -7,14 +7,13 @@
 set -e
 shopt -s nullglob
 
-[[ $# -eq 1 ]] || { echo "Usage: $0 OUTPUT_FILE.go" >&2; exit 1; }
 winerror="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/shared/winerror.h | sort -Vr | head -n 1)"
 [[ -n $winerror ]] || { echo "Unable to find winerror.h" >&2; exit 1; }
 
 declare -A errors
 
 {
-	echo "// Code generated by 'go generate'; DO NOT EDIT."
+	echo "// Code generated by 'mkerrors.bash'; DO NOT EDIT."
 	echo
 	echo "package windows"
 	echo "import \"syscall\""
@@ -61,4 +60,4 @@ declare -A errors
 	done < "$winerror"
 
 	echo ")"
-} | gofmt > "$1"
+} | gofmt > "zerrors_windows.go"

+ 27 - 0
go/vendor/golang.org/x/sys/windows/mkknownfolderids.bash

@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# Copyright 2019 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+set -e
+shopt -s nullglob
+
+knownfolders="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/um/KnownFolders.h | sort -Vr | head -n 1)"
+[[ -n $knownfolders ]] || { echo "Unable to find KnownFolders.h" >&2; exit 1; }
+
+{
+	echo "// Code generated by 'mkknownfolderids.bash'; DO NOT EDIT."
+	echo
+	echo "package windows"
+	echo "type KNOWNFOLDERID GUID"
+	echo "var ("
+	while read -r line; do
+		[[ $line =~ DEFINE_KNOWN_FOLDER\((FOLDERID_[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+),[\t\ ]*(0x[^,]+)\) ]] || continue
+		printf "%s = &KNOWNFOLDERID{0x%08x, 0x%04x, 0x%04x, [8]byte{0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x}}\n" \
+			"${BASH_REMATCH[1]}" $(( "${BASH_REMATCH[2]}" )) $(( "${BASH_REMATCH[3]}" )) $(( "${BASH_REMATCH[4]}" )) \
+			$(( "${BASH_REMATCH[5]}" )) $(( "${BASH_REMATCH[6]}" )) $(( "${BASH_REMATCH[7]}" )) $(( "${BASH_REMATCH[8]}" )) \
+			$(( "${BASH_REMATCH[9]}" )) $(( "${BASH_REMATCH[10]}" )) $(( "${BASH_REMATCH[11]}" )) $(( "${BASH_REMATCH[12]}" ))
+	done < "$knownfolders"
+	echo ")"
+} | gofmt > "zknownfolderids_windows.go"

+ 1 - 1
go/vendor/golang.org/x/sys/windows/mksyscall.go

@@ -6,4 +6,4 @@
 
 package windows
 
-//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go
+//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go

+ 583 - 31
go/vendor/golang.org/x/sys/windows/security_windows.go

@@ -9,14 +9,6 @@ import (
 	"unsafe"
 )
 
-const (
-	STANDARD_RIGHTS_REQUIRED = 0xf0000
-	STANDARD_RIGHTS_READ     = 0x20000
-	STANDARD_RIGHTS_WRITE    = 0x20000
-	STANDARD_RIGHTS_EXECUTE  = 0x20000
-	STANDARD_RIGHTS_ALL      = 0x1F0000
-)
-
 const (
 	NameUnknown          = 0
 	NameFullyQualifiedDN = 1
@@ -235,16 +227,15 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32,
 	}
 }
 
-// String converts SID to a string format
-// suitable for display, storage, or transmission.
-func (sid *SID) String() (string, error) {
+// String converts SID to a string format suitable for display, storage, or transmission.
+func (sid *SID) String() string {
 	var s *uint16
 	e := ConvertSidToStringSid(sid, &s)
 	if e != nil {
-		return "", e
+		return ""
 	}
 	defer LocalFree((Handle)(unsafe.Pointer(s)))
-	return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil
+	return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:])
 }
 
 // Len returns the length, in bytes, of a valid security identifier SID.
@@ -603,12 +594,22 @@ type Tokenprimarygroup struct {
 
 type Tokengroups struct {
 	GroupCount uint32
-	Groups     [1]SIDAndAttributes
+	Groups     [1]SIDAndAttributes // Use AllGroups() for iterating.
+}
+
+// AllGroups returns a slice that can be used to iterate over the groups in g.
+func (g *Tokengroups) AllGroups() []SIDAndAttributes {
+	return (*[(1 << 28) - 1]SIDAndAttributes)(unsafe.Pointer(&g.Groups[0]))[:g.GroupCount:g.GroupCount]
 }
 
 type Tokenprivileges struct {
 	PrivilegeCount uint32
-	Privileges     [1]LUIDAndAttributes
+	Privileges     [1]LUIDAndAttributes // Use AllPrivileges() for iterating.
+}
+
+// AllPrivileges returns a slice that can be used to iterate over the privileges in p.
+func (p *Tokenprivileges) AllPrivileges() []LUIDAndAttributes {
+	return (*[(1 << 27) - 1]LUIDAndAttributes)(unsafe.Pointer(&p.Privileges[0]))[:p.PrivilegeCount:p.PrivilegeCount]
 }
 
 type Tokenmandatorylabel struct {
@@ -634,6 +635,8 @@ func (tml *Tokenmandatorylabel) Size() uint32 {
 //sys	DuplicateTokenEx(existingToken Token, desiredAccess uint32, tokenAttributes *SecurityAttributes, impersonationLevel uint32, tokenType uint32, newToken *Token) (err error) = advapi32.DuplicateTokenEx
 //sys	GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
 //sys	getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW
+//sys	getWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetWindowsDirectoryW
+//sys	getSystemWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemWindowsDirectoryW
 
 // An access token contains the security information for a logon session.
 // The system creates an access token when a user logs on, and every
@@ -644,21 +647,16 @@ func (tml *Tokenmandatorylabel) Size() uint32 {
 // system-related operations on the local computer.
 type Token Handle
 
-// OpenCurrentProcessToken opens the access token
-// associated with current process. It is a real
-// token that needs to be closed, unlike
-// GetCurrentProcessToken.
+// OpenCurrentProcessToken opens an access token associated with current
+// process with TOKEN_QUERY access. It is a real token that needs to be closed.
+//
+// Deprecated: Explicitly call OpenProcessToken(CurrentProcess(), ...)
+// with the desired access instead, or use GetCurrentProcessToken for a
+// TOKEN_QUERY token.
 func OpenCurrentProcessToken() (Token, error) {
-	p, e := GetCurrentProcess()
-	if e != nil {
-		return 0, e
-	}
-	var t Token
-	e = OpenProcessToken(p, TOKEN_QUERY, &t)
-	if e != nil {
-		return 0, e
-	}
-	return t, nil
+	var token Token
+	err := OpenProcessToken(CurrentProcess(), TOKEN_QUERY, &token)
+	return token, err
 }
 
 // GetCurrentProcessToken returns the access token associated with
@@ -775,8 +773,8 @@ func (token Token) GetLinkedToken() (Token, error) {
 	return linkedToken, nil
 }
 
-// GetSystemDirectory retrieves path to current location of the system
-// directory, which is typically, though not always, C:\Windows\System32.
+// GetSystemDirectory retrieves the path to current location of the system
+// directory, which is typically, though not always, `C:\Windows\System32`.
 func GetSystemDirectory() (string, error) {
 	n := uint32(MAX_PATH)
 	for {
@@ -792,6 +790,42 @@ func GetSystemDirectory() (string, error) {
 	}
 }
 
+// GetWindowsDirectory retrieves the path to current location of the Windows
+// directory, which is typically, though not always, `C:\Windows`. This may
+// be a private user directory in the case that the application is running
+// under a terminal server.
+func GetWindowsDirectory() (string, error) {
+	n := uint32(MAX_PATH)
+	for {
+		b := make([]uint16, n)
+		l, e := getWindowsDirectory(&b[0], n)
+		if e != nil {
+			return "", e
+		}
+		if l <= n {
+			return UTF16ToString(b[:l]), nil
+		}
+		n = l
+	}
+}
+
+// GetSystemWindowsDirectory retrieves the path to current location of the
+// Windows directory, which is typically, though not always, `C:\Windows`.
+func GetSystemWindowsDirectory() (string, error) {
+	n := uint32(MAX_PATH)
+	for {
+		b := make([]uint16, n)
+		l, e := getSystemWindowsDirectory(&b[0], n)
+		if e != nil {
+			return "", e
+		}
+		if l <= n {
+			return UTF16ToString(b[:l]), nil
+		}
+		n = l
+	}
+}
+
 // IsMember reports whether the access token t is a member of the provided SID.
 func (t Token) IsMember(sid *SID) (bool, error) {
 	var b int32
@@ -842,3 +876,521 @@ type WTS_SESSION_INFO struct {
 //sys WTSQueryUserToken(session uint32, token *Token) (err error) = wtsapi32.WTSQueryUserToken
 //sys WTSEnumerateSessions(handle Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW
 //sys WTSFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory
+
+type ACL struct {
+	aclRevision byte
+	sbz1        byte
+	aclSize     uint16
+	aceCount    uint16
+	sbz2        uint16
+}
+
+type SECURITY_DESCRIPTOR struct {
+	revision byte
+	sbz1     byte
+	control  SECURITY_DESCRIPTOR_CONTROL
+	owner    *SID
+	group    *SID
+	sacl     *ACL
+	dacl     *ACL
+}
+
+type SecurityAttributes struct {
+	Length             uint32
+	SecurityDescriptor *SECURITY_DESCRIPTOR
+	InheritHandle      uint32
+}
+
+type SE_OBJECT_TYPE uint32
+
+// Constants for type SE_OBJECT_TYPE
+const (
+	SE_UNKNOWN_OBJECT_TYPE     = 0
+	SE_FILE_OBJECT             = 1
+	SE_SERVICE                 = 2
+	SE_PRINTER                 = 3
+	SE_REGISTRY_KEY            = 4
+	SE_LMSHARE                 = 5
+	SE_KERNEL_OBJECT           = 6
+	SE_WINDOW_OBJECT           = 7
+	SE_DS_OBJECT               = 8
+	SE_DS_OBJECT_ALL           = 9
+	SE_PROVIDER_DEFINED_OBJECT = 10
+	SE_WMIGUID_OBJECT          = 11
+	SE_REGISTRY_WOW64_32KEY    = 12
+	SE_REGISTRY_WOW64_64KEY    = 13
+)
+
+type SECURITY_INFORMATION uint32
+
+// Constants for type SECURITY_INFORMATION
+const (
+	OWNER_SECURITY_INFORMATION            = 0x00000001
+	GROUP_SECURITY_INFORMATION            = 0x00000002
+	DACL_SECURITY_INFORMATION             = 0x00000004
+	SACL_SECURITY_INFORMATION             = 0x00000008
+	LABEL_SECURITY_INFORMATION            = 0x00000010
+	ATTRIBUTE_SECURITY_INFORMATION        = 0x00000020
+	SCOPE_SECURITY_INFORMATION            = 0x00000040
+	BACKUP_SECURITY_INFORMATION           = 0x00010000
+	PROTECTED_DACL_SECURITY_INFORMATION   = 0x80000000
+	PROTECTED_SACL_SECURITY_INFORMATION   = 0x40000000
+	UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
+	UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
+)
+
+type SECURITY_DESCRIPTOR_CONTROL uint16
+
+// Constants for type SECURITY_DESCRIPTOR_CONTROL
+const (
+	SE_OWNER_DEFAULTED       = 0x0001
+	SE_GROUP_DEFAULTED       = 0x0002
+	SE_DACL_PRESENT          = 0x0004
+	SE_DACL_DEFAULTED        = 0x0008
+	SE_SACL_PRESENT          = 0x0010
+	SE_SACL_DEFAULTED        = 0x0020
+	SE_DACL_AUTO_INHERIT_REQ = 0x0100
+	SE_SACL_AUTO_INHERIT_REQ = 0x0200
+	SE_DACL_AUTO_INHERITED   = 0x0400
+	SE_SACL_AUTO_INHERITED   = 0x0800
+	SE_DACL_PROTECTED        = 0x1000
+	SE_SACL_PROTECTED        = 0x2000
+	SE_RM_CONTROL_VALID      = 0x4000
+	SE_SELF_RELATIVE         = 0x8000
+)
+
+type ACCESS_MASK uint32
+
+// Constants for type ACCESS_MASK
+const (
+	DELETE                   = 0x00010000
+	READ_CONTROL             = 0x00020000
+	WRITE_DAC                = 0x00040000
+	WRITE_OWNER              = 0x00080000
+	SYNCHRONIZE              = 0x00100000
+	STANDARD_RIGHTS_REQUIRED = 0x000F0000
+	STANDARD_RIGHTS_READ     = READ_CONTROL
+	STANDARD_RIGHTS_WRITE    = READ_CONTROL
+	STANDARD_RIGHTS_EXECUTE  = READ_CONTROL
+	STANDARD_RIGHTS_ALL      = 0x001F0000
+	SPECIFIC_RIGHTS_ALL      = 0x0000FFFF
+	ACCESS_SYSTEM_SECURITY   = 0x01000000
+	MAXIMUM_ALLOWED          = 0x02000000
+	GENERIC_READ             = 0x80000000
+	GENERIC_WRITE            = 0x40000000
+	GENERIC_EXECUTE          = 0x20000000
+	GENERIC_ALL              = 0x10000000
+)
+
+type ACCESS_MODE uint32
+
+// Constants for type ACCESS_MODE
+const (
+	NOT_USED_ACCESS   = 0
+	GRANT_ACCESS      = 1
+	SET_ACCESS        = 2
+	DENY_ACCESS       = 3
+	REVOKE_ACCESS     = 4
+	SET_AUDIT_SUCCESS = 5
+	SET_AUDIT_FAILURE = 6
+)
+
+// Constants for AceFlags and Inheritance fields
+const (
+	NO_INHERITANCE                     = 0x0
+	SUB_OBJECTS_ONLY_INHERIT           = 0x1
+	SUB_CONTAINERS_ONLY_INHERIT        = 0x2
+	SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3
+	INHERIT_NO_PROPAGATE               = 0x4
+	INHERIT_ONLY                       = 0x8
+	INHERITED_ACCESS_ENTRY             = 0x10
+	INHERITED_PARENT                   = 0x10000000
+	INHERITED_GRANDPARENT              = 0x20000000
+	OBJECT_INHERIT_ACE                 = 0x1
+	CONTAINER_INHERIT_ACE              = 0x2
+	NO_PROPAGATE_INHERIT_ACE           = 0x4
+	INHERIT_ONLY_ACE                   = 0x8
+	INHERITED_ACE                      = 0x10
+	VALID_INHERIT_FLAGS                = 0x1F
+)
+
+type MULTIPLE_TRUSTEE_OPERATION uint32
+
+// Constants for MULTIPLE_TRUSTEE_OPERATION
+const (
+	NO_MULTIPLE_TRUSTEE    = 0
+	TRUSTEE_IS_IMPERSONATE = 1
+)
+
+type TRUSTEE_FORM uint32
+
+// Constants for TRUSTEE_FORM
+const (
+	TRUSTEE_IS_SID              = 0
+	TRUSTEE_IS_NAME             = 1
+	TRUSTEE_BAD_FORM            = 2
+	TRUSTEE_IS_OBJECTS_AND_SID  = 3
+	TRUSTEE_IS_OBJECTS_AND_NAME = 4
+)
+
+type TRUSTEE_TYPE uint32
+
+// Constants for TRUSTEE_TYPE
+const (
+	TRUSTEE_IS_UNKNOWN          = 0
+	TRUSTEE_IS_USER             = 1
+	TRUSTEE_IS_GROUP            = 2
+	TRUSTEE_IS_DOMAIN           = 3
+	TRUSTEE_IS_ALIAS            = 4
+	TRUSTEE_IS_WELL_KNOWN_GROUP = 5
+	TRUSTEE_IS_DELETED          = 6
+	TRUSTEE_IS_INVALID          = 7
+	TRUSTEE_IS_COMPUTER         = 8
+)
+
+// Constants for ObjectsPresent field
+const (
+	ACE_OBJECT_TYPE_PRESENT           = 0x1
+	ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x2
+)
+
+type EXPLICIT_ACCESS struct {
+	AccessPermissions ACCESS_MASK
+	AccessMode        ACCESS_MODE
+	Inheritance       uint32
+	Trustee           TRUSTEE
+}
+
+// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
+type TrusteeValue uintptr
+
+func TrusteeValueFromString(str string) TrusteeValue {
+	return TrusteeValue(unsafe.Pointer(StringToUTF16Ptr(str)))
+}
+func TrusteeValueFromSID(sid *SID) TrusteeValue {
+	return TrusteeValue(unsafe.Pointer(sid))
+}
+func TrusteeValueFromObjectsAndSid(objectsAndSid *OBJECTS_AND_SID) TrusteeValue {
+	return TrusteeValue(unsafe.Pointer(objectsAndSid))
+}
+func TrusteeValueFromObjectsAndName(objectsAndName *OBJECTS_AND_NAME) TrusteeValue {
+	return TrusteeValue(unsafe.Pointer(objectsAndName))
+}
+
+type TRUSTEE struct {
+	MultipleTrustee          *TRUSTEE
+	MultipleTrusteeOperation MULTIPLE_TRUSTEE_OPERATION
+	TrusteeForm              TRUSTEE_FORM
+	TrusteeType              TRUSTEE_TYPE
+	TrusteeValue             TrusteeValue
+}
+
+type OBJECTS_AND_SID struct {
+	ObjectsPresent          uint32
+	ObjectTypeGuid          GUID
+	InheritedObjectTypeGuid GUID
+	Sid                     *SID
+}
+
+type OBJECTS_AND_NAME struct {
+	ObjectsPresent          uint32
+	ObjectType              SE_OBJECT_TYPE
+	ObjectTypeName          *uint16
+	InheritedObjectTypeName *uint16
+	Name                    *uint16
+}
+
+//sys	getSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) = advapi32.GetSecurityInfo
+//sys	SetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) = advapi32.SetSecurityInfo
+//sys	getNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) = advapi32.GetNamedSecurityInfoW
+//sys	SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) = advapi32.SetNamedSecurityInfoW
+
+//sys	buildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, countAccessEntries uint32, accessEntries *EXPLICIT_ACCESS, countAuditEntries uint32, auditEntries *EXPLICIT_ACCESS, oldSecurityDescriptor *SECURITY_DESCRIPTOR, sizeNewSecurityDescriptor *uint32, newSecurityDescriptor **SECURITY_DESCRIPTOR) (ret error) = advapi32.BuildSecurityDescriptorW
+//sys	initializeSecurityDescriptor(absoluteSD *SECURITY_DESCRIPTOR, revision uint32) (err error) = advapi32.InitializeSecurityDescriptor
+
+//sys	getSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, control *SECURITY_DESCRIPTOR_CONTROL, revision *uint32) (err error) = advapi32.GetSecurityDescriptorControl
+//sys	getSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent *bool, dacl **ACL, daclDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorDacl
+//sys	getSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent *bool, sacl **ACL, saclDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorSacl
+//sys	getSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner **SID, ownerDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorOwner
+//sys	getSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group **SID, groupDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorGroup
+//sys	getSecurityDescriptorLength(sd *SECURITY_DESCRIPTOR) (len uint32) = advapi32.GetSecurityDescriptorLength
+//sys	getSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) (ret error) [failretval!=0] = advapi32.GetSecurityDescriptorRMControl
+//sys	isValidSecurityDescriptor(sd *SECURITY_DESCRIPTOR) (isValid bool) = advapi32.IsValidSecurityDescriptor
+
+//sys	setSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) (err error) = advapi32.SetSecurityDescriptorControl
+//sys	setSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent bool, dacl *ACL, daclDefaulted bool) (err error) = advapi32.SetSecurityDescriptorDacl
+//sys	setSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent bool, sacl *ACL, saclDefaulted bool) (err error) = advapi32.SetSecurityDescriptorSacl
+//sys	setSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner *SID, ownerDefaulted bool) (err error) = advapi32.SetSecurityDescriptorOwner
+//sys	setSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group *SID, groupDefaulted bool) (err error) = advapi32.SetSecurityDescriptorGroup
+//sys	setSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) = advapi32.SetSecurityDescriptorRMControl
+
+//sys	convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd **SECURITY_DESCRIPTOR, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW
+//sys	convertSecurityDescriptorToStringSecurityDescriptor(sd *SECURITY_DESCRIPTOR, revision uint32, securityInformation SECURITY_INFORMATION, str **uint16, strLen *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW
+
+//sys	makeAbsoluteSD(selfRelativeSD *SECURITY_DESCRIPTOR, absoluteSD *SECURITY_DESCRIPTOR, absoluteSDSize *uint32, dacl *ACL, daclSize *uint32, sacl *ACL, saclSize *uint32, owner *SID, ownerSize *uint32, group *SID, groupSize *uint32) (err error) = advapi32.MakeAbsoluteSD
+//sys	makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD
+
+//sys	setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW
+
+// Control returns the security descriptor control bits.
+func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) {
+	err = getSecurityDescriptorControl(sd, &control, &revision)
+	return
+}
+
+// SetControl sets the security descriptor control bits.
+func (sd *SECURITY_DESCRIPTOR) SetControl(controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) error {
+	return setSecurityDescriptorControl(sd, controlBitsOfInterest, controlBitsToSet)
+}
+
+// RMControl returns the security descriptor resource manager control bits.
+func (sd *SECURITY_DESCRIPTOR) RMControl() (control uint8, err error) {
+	err = getSecurityDescriptorRMControl(sd, &control)
+	return
+}
+
+// SetRMControl sets the security descriptor resource manager control bits.
+func (sd *SECURITY_DESCRIPTOR) SetRMControl(rmControl uint8) {
+	setSecurityDescriptorRMControl(sd, &rmControl)
+}
+
+// DACL returns the security descriptor DACL and whether it was defaulted. The dacl return value may be nil
+// if a DACL exists but is an "empty DACL", meaning fully permissive. If the DACL does not exist, err returns
+// ERROR_OBJECT_NOT_FOUND.
+func (sd *SECURITY_DESCRIPTOR) DACL() (dacl *ACL, defaulted bool, err error) {
+	var present bool
+	err = getSecurityDescriptorDacl(sd, &present, &dacl, &defaulted)
+	if !present {
+		err = ERROR_OBJECT_NOT_FOUND
+	}
+	return
+}
+
+// SetDACL sets the absolute security descriptor DACL.
+func (absoluteSD *SECURITY_DESCRIPTOR) SetDACL(dacl *ACL, present, defaulted bool) error {
+	return setSecurityDescriptorDacl(absoluteSD, present, dacl, defaulted)
+}
+
+// SACL returns the security descriptor SACL and whether it was defaulted. The sacl return value may be nil
+// if a SACL exists but is an "empty SACL", meaning fully permissive. If the SACL does not exist, err returns
+// ERROR_OBJECT_NOT_FOUND.
+func (sd *SECURITY_DESCRIPTOR) SACL() (sacl *ACL, defaulted bool, err error) {
+	var present bool
+	err = getSecurityDescriptorSacl(sd, &present, &sacl, &defaulted)
+	if !present {
+		err = ERROR_OBJECT_NOT_FOUND
+	}
+	return
+}
+
+// SetSACL sets the absolute security descriptor SACL.
+func (absoluteSD *SECURITY_DESCRIPTOR) SetSACL(sacl *ACL, present, defaulted bool) error {
+	return setSecurityDescriptorSacl(absoluteSD, present, sacl, defaulted)
+}
+
+// Owner returns the security descriptor owner and whether it was defaulted.
+func (sd *SECURITY_DESCRIPTOR) Owner() (owner *SID, defaulted bool, err error) {
+	err = getSecurityDescriptorOwner(sd, &owner, &defaulted)
+	return
+}
+
+// SetOwner sets the absolute security descriptor owner.
+func (absoluteSD *SECURITY_DESCRIPTOR) SetOwner(owner *SID, defaulted bool) error {
+	return setSecurityDescriptorOwner(absoluteSD, owner, defaulted)
+}
+
+// Group returns the security descriptor group and whether it was defaulted.
+func (sd *SECURITY_DESCRIPTOR) Group() (group *SID, defaulted bool, err error) {
+	err = getSecurityDescriptorGroup(sd, &group, &defaulted)
+	return
+}
+
+// SetGroup sets the absolute security descriptor owner.
+func (absoluteSD *SECURITY_DESCRIPTOR) SetGroup(group *SID, defaulted bool) error {
+	return setSecurityDescriptorGroup(absoluteSD, group, defaulted)
+}
+
+// Length returns the length of the security descriptor.
+func (sd *SECURITY_DESCRIPTOR) Length() uint32 {
+	return getSecurityDescriptorLength(sd)
+}
+
+// IsValid returns whether the security descriptor is valid.
+func (sd *SECURITY_DESCRIPTOR) IsValid() bool {
+	return isValidSecurityDescriptor(sd)
+}
+
+// String returns the SDDL form of the security descriptor, with a function signature that can be
+// used with %v formatting directives.
+func (sd *SECURITY_DESCRIPTOR) String() string {
+	var sddl *uint16
+	err := convertSecurityDescriptorToStringSecurityDescriptor(sd, 1, 0xff, &sddl, nil)
+	if err != nil {
+		return ""
+	}
+	defer LocalFree(Handle(unsafe.Pointer(sddl)))
+	return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(sddl))[:])
+}
+
+// ToAbsolute converts a self-relative security descriptor into an absolute one.
+func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DESCRIPTOR, err error) {
+	control, _, err := selfRelativeSD.Control()
+	if err != nil {
+		return
+	}
+	if control&SE_SELF_RELATIVE == 0 {
+		err = ERROR_INVALID_PARAMETER
+		return
+	}
+	var absoluteSDSize, daclSize, saclSize, ownerSize, groupSize uint32
+	err = makeAbsoluteSD(selfRelativeSD, nil, &absoluteSDSize,
+		nil, &daclSize, nil, &saclSize, nil, &ownerSize, nil, &groupSize)
+	switch err {
+	case ERROR_INSUFFICIENT_BUFFER:
+	case nil:
+		// makeAbsoluteSD is expected to fail, but it succeeds.
+		return nil, ERROR_INTERNAL_ERROR
+	default:
+		return nil, err
+	}
+	if absoluteSDSize > 0 {
+		absoluteSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, absoluteSDSize)[0]))
+	}
+	var (
+		dacl  *ACL
+		sacl  *ACL
+		owner *SID
+		group *SID
+	)
+	if daclSize > 0 {
+		dacl = (*ACL)(unsafe.Pointer(&make([]byte, daclSize)[0]))
+	}
+	if saclSize > 0 {
+		sacl = (*ACL)(unsafe.Pointer(&make([]byte, saclSize)[0]))
+	}
+	if ownerSize > 0 {
+		owner = (*SID)(unsafe.Pointer(&make([]byte, ownerSize)[0]))
+	}
+	if groupSize > 0 {
+		group = (*SID)(unsafe.Pointer(&make([]byte, groupSize)[0]))
+	}
+	err = makeAbsoluteSD(selfRelativeSD, absoluteSD, &absoluteSDSize,
+		dacl, &daclSize, sacl, &saclSize, owner, &ownerSize, group, &groupSize)
+	return
+}
+
+// ToSelfRelative converts an absolute security descriptor into a self-relative one.
+func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURITY_DESCRIPTOR, err error) {
+	control, _, err := absoluteSD.Control()
+	if err != nil {
+		return
+	}
+	if control&SE_SELF_RELATIVE != 0 {
+		err = ERROR_INVALID_PARAMETER
+		return
+	}
+	var selfRelativeSDSize uint32
+	err = makeSelfRelativeSD(absoluteSD, nil, &selfRelativeSDSize)
+	switch err {
+	case ERROR_INSUFFICIENT_BUFFER:
+	case nil:
+		// makeSelfRelativeSD is expected to fail, but it succeeds.
+		return nil, ERROR_INTERNAL_ERROR
+	default:
+		return nil, err
+	}
+	if selfRelativeSDSize > 0 {
+		selfRelativeSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, selfRelativeSDSize)[0]))
+	}
+	err = makeSelfRelativeSD(absoluteSD, selfRelativeSD, &selfRelativeSDSize)
+	return
+}
+
+func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR {
+	sdBytes := make([]byte, selfRelativeSD.Length())
+	copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)])
+	return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0]))
+}
+
+// SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a
+// self-relative security descriptor object allocated on the Go heap.
+func SecurityDescriptorFromString(sddl string) (sd *SECURITY_DESCRIPTOR, err error) {
+	var winHeapSD *SECURITY_DESCRIPTOR
+	err = convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &winHeapSD, nil)
+	if err != nil {
+		return
+	}
+	defer LocalFree(Handle(unsafe.Pointer(winHeapSD)))
+	return winHeapSD.copySelfRelativeSecurityDescriptor(), nil
+}
+
+// GetSecurityInfo queries the security information for a given handle and returns the self-relative security
+// descriptor result on the Go heap.
+func GetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION) (sd *SECURITY_DESCRIPTOR, err error) {
+	var winHeapSD *SECURITY_DESCRIPTOR
+	err = getSecurityInfo(handle, objectType, securityInformation, nil, nil, nil, nil, &winHeapSD)
+	if err != nil {
+		return
+	}
+	defer LocalFree(Handle(unsafe.Pointer(winHeapSD)))
+	return winHeapSD.copySelfRelativeSecurityDescriptor(), nil
+}
+
+// GetNamedSecurityInfo queries the security information for a given named object and returns the self-relative security
+// descriptor result on the Go heap.
+func GetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION) (sd *SECURITY_DESCRIPTOR, err error) {
+	var winHeapSD *SECURITY_DESCRIPTOR
+	err = getNamedSecurityInfo(objectName, objectType, securityInformation, nil, nil, nil, nil, &winHeapSD)
+	if err != nil {
+		return
+	}
+	defer LocalFree(Handle(unsafe.Pointer(winHeapSD)))
+	return winHeapSD.copySelfRelativeSecurityDescriptor(), nil
+}
+
+// BuildSecurityDescriptor makes a new security descriptor using the input trustees, explicit access lists, and
+// prior security descriptor to be merged, any of which can be nil, returning the self-relative security descriptor
+// result on the Go heap.
+func BuildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, accessEntries []EXPLICIT_ACCESS, auditEntries []EXPLICIT_ACCESS, mergedSecurityDescriptor *SECURITY_DESCRIPTOR) (sd *SECURITY_DESCRIPTOR, err error) {
+	var winHeapSD *SECURITY_DESCRIPTOR
+	var winHeapSDSize uint32
+	var firstAccessEntry *EXPLICIT_ACCESS
+	if len(accessEntries) > 0 {
+		firstAccessEntry = &accessEntries[0]
+	}
+	var firstAuditEntry *EXPLICIT_ACCESS
+	if len(auditEntries) > 0 {
+		firstAuditEntry = &auditEntries[0]
+	}
+	err = buildSecurityDescriptor(owner, group, uint32(len(accessEntries)), firstAccessEntry, uint32(len(auditEntries)), firstAuditEntry, mergedSecurityDescriptor, &winHeapSDSize, &winHeapSD)
+	if err != nil {
+		return
+	}
+	defer LocalFree(Handle(unsafe.Pointer(winHeapSD)))
+	return winHeapSD.copySelfRelativeSecurityDescriptor(), nil
+}
+
+// NewSecurityDescriptor creates and initializes a new absolute security descriptor.
+func NewSecurityDescriptor() (absoluteSD *SECURITY_DESCRIPTOR, err error) {
+	absoluteSD = &SECURITY_DESCRIPTOR{}
+	err = initializeSecurityDescriptor(absoluteSD, 1)
+	return
+}
+
+// ACLFromEntries returns a new ACL on the Go heap containing a list of explicit entries as well as those of another ACL.
+// Both explicitEntries and mergedACL are optional and can be nil.
+func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL, err error) {
+	var firstExplicitEntry *EXPLICIT_ACCESS
+	if len(explicitEntries) > 0 {
+		firstExplicitEntry = &explicitEntries[0]
+	}
+	var winHeapACL *ACL
+	err = setEntriesInAcl(uint32(len(explicitEntries)), firstExplicitEntry, mergedACL, &winHeapACL)
+	if err != nil {
+		return
+	}
+	defer LocalFree(Handle(unsafe.Pointer(winHeapACL)))
+	aclBytes := make([]byte, winHeapACL.aclSize)
+	copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes)])
+	return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil
+}

+ 11 - 0
go/vendor/golang.org/x/sys/windows/service.go

@@ -159,6 +159,10 @@ type SERVICE_DESCRIPTION struct {
 	Description *uint16
 }
 
+type SERVICE_DELAYED_AUTO_START_INFO struct {
+	IsDelayedAutoStartUp uint32
+}
+
 type SERVICE_STATUS_PROCESS struct {
 	ServiceType             uint32
 	CurrentState            uint32
@@ -200,12 +204,19 @@ type SC_ACTION struct {
 	Delay uint32
 }
 
+type QUERY_SERVICE_LOCK_STATUS struct {
+	IsLocked     uint32
+	LockOwner    *uint16
+	LockDuration uint32
+}
+
 //sys	CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
 //sys	CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW
 //sys	OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
 //sys	DeleteService(service Handle) (err error) = advapi32.DeleteService
 //sys	StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
 //sys	QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus
+//sys	QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceLockStatusW
 //sys	ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService
 //sys	StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
 //sys	SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus

+ 228 - 10
go/vendor/golang.org/x/sys/windows/syscall_windows.go

@@ -10,6 +10,7 @@ import (
 	errorspkg "errors"
 	"sync"
 	"syscall"
+	"time"
 	"unicode/utf16"
 	"unsafe"
 )
@@ -56,6 +57,10 @@ const (
 	FILE_VOLUME_IS_COMPRESSED         = 0x00008000
 	FILE_VOLUME_QUOTAS                = 0x00000020
 
+	// Flags for LockFileEx.
+	LOCKFILE_FAIL_IMMEDIATELY = 0x00000001
+	LOCKFILE_EXCLUSIVE_LOCK   = 0x00000002
+
 	// Return values of SleepEx and other APC functions
 	STATUS_USER_APC    = 0x000000C0
 	WAIT_IO_COMPLETION = STATUS_USER_APC
@@ -135,6 +140,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, err error) = LoadLibraryExW
 //sys	FreeLibrary(handle Handle) (err error)
 //sys	GetProcAddress(module Handle, procname string) (proc uintptr, err error)
+//sys	GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW
+//sys	GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW
 //sys	GetVersion() (ver uint32, err error)
 //sys	FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
 //sys	ExitProcess(exitcode uint32)
@@ -159,6 +166,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	DeleteFile(path *uint16) (err error) = DeleteFileW
 //sys	MoveFile(from *uint16, to *uint16) (err error) = MoveFileW
 //sys	MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
+//sys	LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error)
+//sys	UnlockFileEx(file Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error)
 //sys	GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
 //sys	GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
 //sys	SetEndOfFile(handle Handle) (err error)
@@ -171,13 +180,12 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	CancelIo(s Handle) (err error)
 //sys	CancelIoEx(s Handle, o *Overlapped) (err error)
 //sys	CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW
-//sys	OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error)
-//sys	ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) = shell32.ShellExecuteW
+//sys	OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error)
+//sys	ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW
+//sys	shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) = shell32.SHGetKnownFolderPath
 //sys	TerminateProcess(handle Handle, exitcode uint32) (err error)
 //sys	GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)
 //sys	GetStartupInfo(startupInfo *StartupInfo) (err error) = GetStartupInfoW
-//sys	GetCurrentProcess() (pseudoHandle Handle, err error)
-//sys	GetCurrentThread() (pseudoHandle Handle, err error)
 //sys	GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error)
 //sys	DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error)
 //sys	WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) [failretval==0xffffffff]
@@ -194,6 +202,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	SetEnvironmentVariable(name *uint16, value *uint16) (err error) = kernel32.SetEnvironmentVariableW
 //sys	CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
 //sys	DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
+//sys	getTickCount64() (ms uint64) = kernel32.GetTickCount64
 //sys	SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error)
 //sys	GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
 //sys	SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW
@@ -232,7 +241,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW
 //sys	RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW
 //sys	RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW
-//sys	getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
+//sys	GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId
 //sys	GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
 //sys	SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
 //sys	GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
@@ -241,6 +250,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot
 //sys	Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32FirstW
 //sys	Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32NextW
+//sys	Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error)
+//sys	Thread32Next(snapshot Handle, threadEntry *ThreadEntry32) (err error)
 //sys	DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBufferSize uint32, outBuffer *byte, outBufferSize uint32, bytesReturned *uint32, overlapped *Overlapped) (err error)
 // This function returns 1 byte BOOLEAN rather than the 4 byte BOOL.
 //sys	CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) [failretval&0xff==0] = CreateSymbolicLinkW
@@ -252,6 +263,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	SetEvent(event Handle) (err error) = kernel32.SetEvent
 //sys	ResetEvent(event Handle) (err error) = kernel32.ResetEvent
 //sys	PulseEvent(event Handle) (err error) = kernel32.PulseEvent
+//sys	CreateMutex(mutexAttrs *SecurityAttributes, initialOwner bool, name *uint16) (handle Handle, err error) = kernel32.CreateMutexW
+//sys	CreateMutexEx(mutexAttrs *SecurityAttributes, name *uint16, flags uint32, desiredAccess uint32) (handle Handle, err error) = kernel32.CreateMutexExW
+//sys	OpenMutex(desiredAccess uint32, inheritHandle bool, name *uint16) (handle Handle, err error) = kernel32.OpenMutexW
+//sys	ReleaseMutex(mutex Handle) (err error) = kernel32.ReleaseMutex
 //sys	SleepEx(milliseconds uint32, alertable bool) (ret uint32) = kernel32.SleepEx
 //sys	CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, err error) = kernel32.CreateJobObjectW
 //sys	AssignProcessToJobObject(job Handle, process Handle) (err error) = kernel32.AssignProcessToJobObject
@@ -261,6 +276,10 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	SetPriorityClass(process Handle, priorityClass uint32) (err error) = kernel32.SetPriorityClass
 //sys	GetPriorityClass(process Handle) (ret uint32, err error) = kernel32.GetPriorityClass
 //sys	SetInformationJobObject(job Handle, JobObjectInformationClass uint32, JobObjectInformation uintptr, JobObjectInformationLength uint32) (ret int, err error)
+//sys	GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error)
+//sys	GetProcessId(process Handle) (id uint32, err error)
+//sys	OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (handle Handle, err error)
+//sys	SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost
 
 // Volume Management Functions
 //sys	DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW
@@ -271,6 +290,7 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	FindNextVolumeMountPoint(findVolumeMountPoint Handle, volumeMountPoint *uint16, bufferLength uint32) (err error) = FindNextVolumeMountPointW
 //sys	FindVolumeClose(findVolume Handle) (err error)
 //sys	FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error)
+//sys	GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) = GetDiskFreeSpaceExW
 //sys	GetDriveType(rootPathName *uint16) (driveType uint32) = GetDriveTypeW
 //sys	GetLogicalDrives() (drivesBitMask uint32, err error) [failretval==0]
 //sys	GetLogicalDriveStrings(bufferLength uint32, buffer *uint16) (n uint32, err error) [failretval==0] = GetLogicalDriveStringsW
@@ -283,9 +303,54 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) = SetVolumeLabelW
 //sys	SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) = SetVolumeMountPointW
 //sys	MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW
+//sys	ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx
+//sys	InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint32, forceAppsClosed bool, rebootAfterShutdown bool, reason uint32) (err error) = advapi32.InitiateSystemShutdownExW
+//sys	SetProcessShutdownParameters(level uint32, flags uint32) (err error) = kernel32.SetProcessShutdownParameters
+//sys	GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) = kernel32.GetProcessShutdownParameters
+//sys	clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) = ole32.CLSIDFromString
+//sys	stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2
+//sys	coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid
+//sys	CoTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree
+//sys	rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion
+//sys	rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers
+//sys	getProcessPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetProcessPreferredUILanguages
+//sys	getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetThreadPreferredUILanguages
+//sys	getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetUserPreferredUILanguages
+//sys	getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetSystemPreferredUILanguages
+
+// Process Status API (PSAPI)
+//sys	EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses
 
 // syscall interface implementation for other packages
 
+// GetCurrentProcess returns the handle for the current process.
+// It is a pseudo handle that does not need to be closed.
+// The returned error is always nil.
+//
+// Deprecated: use CurrentProcess for the same Handle without the nil
+// error.
+func GetCurrentProcess() (Handle, error) {
+	return CurrentProcess(), nil
+}
+
+// CurrentProcess returns the handle for the current process.
+// It is a pseudo handle that does not need to be closed.
+func CurrentProcess() Handle { return Handle(^uintptr(1 - 1)) }
+
+// GetCurrentThread returns the handle for the current thread.
+// It is a pseudo handle that does not need to be closed.
+// The returned error is always nil.
+//
+// Deprecated: use CurrentThread for the same Handle without the nil
+// error.
+func GetCurrentThread() (Handle, error) {
+	return CurrentThread(), nil
+}
+
+// CurrentThread returns the handle for the current thread.
+// It is a pseudo handle that does not need to be closed.
+func CurrentThread() Handle { return Handle(^uintptr(2 - 1)) }
+
 // GetProcAddressByOrdinal retrieves the address of the exported
 // function from module by ordinal.
 func GetProcAddressByOrdinal(module Handle, ordinal uintptr) (proc uintptr, err error) {
@@ -352,7 +417,11 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) {
 	default:
 		createmode = OPEN_EXISTING
 	}
-	h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0)
+	var attrs uint32 = FILE_ATTRIBUTE_NORMAL
+	if perm&S_IWRITE == 0 {
+		attrs = FILE_ATTRIBUTE_READONLY
+	}
+	h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
 	return h, e
 }
 
@@ -497,6 +566,10 @@ func ComputerName() (name string, err error) {
 	return string(utf16.Decode(b[0:n])), nil
 }
 
+func DurationSinceBoot() time.Duration {
+	return time.Duration(getTickCount64()) * time.Millisecond
+}
+
 func Ftruncate(fd Handle, length int64) (err error) {
 	curoffset, e := Seek(fd, 0, 1)
 	if e != nil {
@@ -628,6 +701,8 @@ const socket_error = uintptr(^uint32(0))
 //sys	WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup
 //sys	WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSAIoctl
 //sys	socket(af int32, typ int32, protocol int32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.socket
+//sys	sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) (err error) [failretval==socket_error] = ws2_32.sendto
+//sys	recvfrom(s Handle, buf []byte, flags int32, from *RawSockaddrAny, fromlen *int32) (n int32, err error) [failretval==-1] = ws2_32.recvfrom
 //sys	Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==socket_error] = ws2_32.setsockopt
 //sys	Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockopt
 //sys	bind(s Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socket_error] = ws2_32.bind
@@ -795,7 +870,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) {
 		for n < len(pp.Path) && pp.Path[n] != 0 {
 			n++
 		}
-		bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
+		bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n]
 		sa.Name = string(bytes)
 		return sa, nil
 
@@ -1056,10 +1131,27 @@ func NsecToTimespec(nsec int64) (ts Timespec) {
 // TODO(brainman): fix all needed for net
 
 func Accept(fd Handle) (nfd Handle, sa Sockaddr, err error) { return 0, nil, syscall.EWINDOWS }
+
 func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, err error) {
-	return 0, nil, syscall.EWINDOWS
+	var rsa RawSockaddrAny
+	l := int32(unsafe.Sizeof(rsa))
+	n32, err := recvfrom(fd, p, int32(flags), &rsa, &l)
+	n = int(n32)
+	if err != nil {
+		return
+	}
+	from, err = rsa.Sockaddr()
+	return
+}
+
+func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error) {
+	ptr, l, err := to.sockaddr()
+	if err != nil {
+		return err
+	}
+	return sendto(fd, p, int32(flags), ptr, l)
 }
-func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error)       { return syscall.EWINDOWS }
+
 func SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (err error) { return syscall.EWINDOWS }
 
 // The Linger struct is wrong but we only noticed after Go 1.
@@ -1106,7 +1198,7 @@ func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) {
 	return syscall.EWINDOWS
 }
 
-func Getpid() (pid int) { return int(getCurrentProcessId()) }
+func Getpid() (pid int) { return int(GetCurrentProcessId()) }
 
 func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
 	// NOTE(rsc): The Win32finddata struct is wrong for the system call:
@@ -1234,3 +1326,129 @@ func Readlink(path string, buf []byte) (n int, err error) {
 
 	return n, nil
 }
+
+// GUIDFromString parses a string in the form of
+// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" into a GUID.
+func GUIDFromString(str string) (GUID, error) {
+	guid := GUID{}
+	str16, err := syscall.UTF16PtrFromString(str)
+	if err != nil {
+		return guid, err
+	}
+	err = clsidFromString(str16, &guid)
+	if err != nil {
+		return guid, err
+	}
+	return guid, nil
+}
+
+// GenerateGUID creates a new random GUID.
+func GenerateGUID() (GUID, error) {
+	guid := GUID{}
+	err := coCreateGuid(&guid)
+	if err != nil {
+		return guid, err
+	}
+	return guid, nil
+}
+
+// String returns the canonical string form of the GUID,
+// in the form of "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
+func (guid GUID) String() string {
+	var str [100]uint16
+	chars := stringFromGUID2(&guid, &str[0], int32(len(str)))
+	if chars <= 1 {
+		return ""
+	}
+	return string(utf16.Decode(str[:chars-1]))
+}
+
+// KnownFolderPath returns a well-known folder path for the current user, specified by one of
+// the FOLDERID_ constants, and chosen and optionally created based on a KF_ flag.
+func KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, error) {
+	return Token(0).KnownFolderPath(folderID, flags)
+}
+
+// KnownFolderPath returns a well-known folder path for the user token, specified by one of
+// the FOLDERID_ constants, and chosen and optionally created based on a KF_ flag.
+func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, error) {
+	var p *uint16
+	err := shGetKnownFolderPath(folderID, flags, t, &p)
+	if err != nil {
+		return "", err
+	}
+	defer CoTaskMemFree(unsafe.Pointer(p))
+	return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil
+}
+
+// RtlGetVersion returns the version of the underlying operating system, ignoring
+// manifest semantics but is affected by the application compatibility layer.
+func RtlGetVersion() *OsVersionInfoEx {
+	info := &OsVersionInfoEx{}
+	info.osVersionInfoSize = uint32(unsafe.Sizeof(*info))
+	// According to documentation, this function always succeeds.
+	// The function doesn't even check the validity of the
+	// osVersionInfoSize member. Disassembling ntdll.dll indicates
+	// that the documentation is indeed correct about that.
+	_ = rtlGetVersion(info)
+	return info
+}
+
+// RtlGetNtVersionNumbers returns the version of the underlying operating system,
+// ignoring manifest semantics and the application compatibility layer.
+func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) {
+	rtlGetNtVersionNumbers(&majorVersion, &minorVersion, &buildNumber)
+	buildNumber &= 0xffff
+	return
+}
+
+// GetProcessPreferredUILanguages retrieves the process preferred UI languages.
+func GetProcessPreferredUILanguages(flags uint32) ([]string, error) {
+	return getUILanguages(flags, getProcessPreferredUILanguages)
+}
+
+// GetThreadPreferredUILanguages retrieves the thread preferred UI languages for the current thread.
+func GetThreadPreferredUILanguages(flags uint32) ([]string, error) {
+	return getUILanguages(flags, getThreadPreferredUILanguages)
+}
+
+// GetUserPreferredUILanguages retrieves information about the user preferred UI languages.
+func GetUserPreferredUILanguages(flags uint32) ([]string, error) {
+	return getUILanguages(flags, getUserPreferredUILanguages)
+}
+
+// GetSystemPreferredUILanguages retrieves the system preferred UI languages.
+func GetSystemPreferredUILanguages(flags uint32) ([]string, error) {
+	return getUILanguages(flags, getSystemPreferredUILanguages)
+}
+
+func getUILanguages(flags uint32, f func(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) error) ([]string, error) {
+	size := uint32(128)
+	for {
+		var numLanguages uint32
+		buf := make([]uint16, size)
+		err := f(flags, &numLanguages, &buf[0], &size)
+		if err == ERROR_INSUFFICIENT_BUFFER {
+			continue
+		}
+		if err != nil {
+			return nil, err
+		}
+		buf = buf[:size]
+		if numLanguages == 0 || len(buf) == 0 { // GetProcessPreferredUILanguages may return numLanguages==0 with "\0\0"
+			return []string{}, nil
+		}
+		if buf[len(buf)-1] == 0 {
+			buf = buf[:len(buf)-1] // remove terminating null
+		}
+		languages := make([]string, 0, numLanguages)
+		from := 0
+		for i, c := range buf {
+			if c == 0 {
+				languages = append(languages, string(utf16.Decode(buf[from:i])))
+				from = i + 1
+			}
+		}
+		return languages, nil
+	}
+}

+ 203 - 16
go/vendor/golang.org/x/sys/windows/types_windows.go

@@ -62,11 +62,6 @@ var signals = [...]string{
 }
 
 const (
-	GENERIC_READ    = 0x80000000
-	GENERIC_WRITE   = 0x40000000
-	GENERIC_EXECUTE = 0x20000000
-	GENERIC_ALL     = 0x10000000
-
 	FILE_LIST_DIRECTORY   = 0x00000001
 	FILE_APPEND_DATA      = 0x00000004
 	FILE_WRITE_ATTRIBUTES = 0x00000100
@@ -158,17 +153,43 @@ const (
 	WAIT_OBJECT_0  = 0x00000000
 	WAIT_FAILED    = 0xFFFFFFFF
 
-	PROCESS_TERMINATE         = 1
-	PROCESS_QUERY_INFORMATION = 0x00000400
-	SYNCHRONIZE               = 0x00100000
+	// Access rights for process.
+	PROCESS_CREATE_PROCESS            = 0x0080
+	PROCESS_CREATE_THREAD             = 0x0002
+	PROCESS_DUP_HANDLE                = 0x0040
+	PROCESS_QUERY_INFORMATION         = 0x0400
+	PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
+	PROCESS_SET_INFORMATION           = 0x0200
+	PROCESS_SET_QUOTA                 = 0x0100
+	PROCESS_SUSPEND_RESUME            = 0x0800
+	PROCESS_TERMINATE                 = 0x0001
+	PROCESS_VM_OPERATION              = 0x0008
+	PROCESS_VM_READ                   = 0x0010
+	PROCESS_VM_WRITE                  = 0x0020
+
+	// Access rights for thread.
+	THREAD_DIRECT_IMPERSONATION      = 0x0200
+	THREAD_GET_CONTEXT               = 0x0008
+	THREAD_IMPERSONATE               = 0x0100
+	THREAD_QUERY_INFORMATION         = 0x0040
+	THREAD_QUERY_LIMITED_INFORMATION = 0x0800
+	THREAD_SET_CONTEXT               = 0x0010
+	THREAD_SET_INFORMATION           = 0x0020
+	THREAD_SET_LIMITED_INFORMATION   = 0x0400
+	THREAD_SET_THREAD_TOKEN          = 0x0080
+	THREAD_SUSPEND_RESUME            = 0x0002
+	THREAD_TERMINATE                 = 0x0001
 
 	FILE_MAP_COPY    = 0x01
 	FILE_MAP_WRITE   = 0x02
 	FILE_MAP_READ    = 0x04
 	FILE_MAP_EXECUTE = 0x20
 
-	CTRL_C_EVENT     = 0
-	CTRL_BREAK_EVENT = 1
+	CTRL_C_EVENT        = 0
+	CTRL_BREAK_EVENT    = 1
+	CTRL_CLOSE_EVENT    = 2
+	CTRL_LOGOFF_EVENT   = 5
+	CTRL_SHUTDOWN_EVENT = 6
 
 	// Windows reserves errors >= 1<<29 for application use.
 	APPLICATION_ERROR = 1 << 29
@@ -450,12 +471,6 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
 	return
 }
 
-type SecurityAttributes struct {
-	Length             uint32
-	SecurityDescriptor uintptr
-	InheritHandle      uint32
-}
-
 type Overlapped struct {
 	Internal     uintptr
 	InternalHigh uintptr
@@ -629,6 +644,16 @@ type ProcessEntry32 struct {
 	ExeFile         [MAX_PATH]uint16
 }
 
+type ThreadEntry32 struct {
+	Size           uint32
+	Usage          uint32
+	ThreadID       uint32
+	OwnerProcessID uint32
+	BasePri        int32
+	DeltaPri       int32
+	Flags          uint32
+}
+
 type Systemtime struct {
 	Year         uint16
 	Month        uint16
@@ -665,6 +690,7 @@ const (
 	SOCK_SEQPACKET = 5
 
 	IPPROTO_IP   = 0
+	IPPROTO_ICMP = 1
 	IPPROTO_IPV6 = 0x29
 	IPPROTO_TCP  = 6
 	IPPROTO_UDP  = 17
@@ -676,6 +702,7 @@ const (
 	SO_BROADCAST              = 32
 	SO_LINGER                 = 128
 	SO_RCVBUF                 = 0x1002
+	SO_RCVTIMEO               = 0x1006
 	SO_SNDBUF                 = 0x1001
 	SO_UPDATE_ACCEPT_CONTEXT  = 0x700b
 	SO_UPDATE_CONNECT_CONTEXT = 0x7010
@@ -1147,6 +1174,28 @@ const (
 	REG_QWORD = REG_QWORD_LITTLE_ENDIAN
 )
 
+const (
+	EVENT_MODIFY_STATE = 0x0002
+	EVENT_ALL_ACCESS   = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3
+
+	MUTANT_QUERY_STATE = 0x0001
+	MUTANT_ALL_ACCESS  = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | MUTANT_QUERY_STATE
+
+	SEMAPHORE_MODIFY_STATE = 0x0002
+	SEMAPHORE_ALL_ACCESS   = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3
+
+	TIMER_QUERY_STATE  = 0x0001
+	TIMER_MODIFY_STATE = 0x0002
+	TIMER_ALL_ACCESS   = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE | TIMER_MODIFY_STATE
+
+	MUTEX_MODIFY_STATE = MUTANT_QUERY_STATE
+	MUTEX_ALL_ACCESS   = MUTANT_ALL_ACCESS
+
+	CREATE_EVENT_MANUAL_RESET  = 0x1
+	CREATE_EVENT_INITIAL_SET   = 0x2
+	CREATE_MUTEX_INITIAL_OWNER = 0x1
+)
+
 type AddrinfoW struct {
 	Flags     int32
 	Family    int32
@@ -1590,3 +1639,141 @@ const (
 	JobObjectNotificationLimitInformation2      = 34
 	JobObjectSecurityLimitInformation           = 5
 )
+
+const (
+	KF_FLAG_DEFAULT                          = 0x00000000
+	KF_FLAG_FORCE_APP_DATA_REDIRECTION       = 0x00080000
+	KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET = 0x00040000
+	KF_FLAG_FORCE_PACKAGE_REDIRECTION        = 0x00020000
+	KF_FLAG_NO_PACKAGE_REDIRECTION           = 0x00010000
+	KF_FLAG_FORCE_APPCONTAINER_REDIRECTION   = 0x00020000
+	KF_FLAG_NO_APPCONTAINER_REDIRECTION      = 0x00010000
+	KF_FLAG_CREATE                           = 0x00008000
+	KF_FLAG_DONT_VERIFY                      = 0x00004000
+	KF_FLAG_DONT_UNEXPAND                    = 0x00002000
+	KF_FLAG_NO_ALIAS                         = 0x00001000
+	KF_FLAG_INIT                             = 0x00000800
+	KF_FLAG_DEFAULT_PATH                     = 0x00000400
+	KF_FLAG_NOT_PARENT_RELATIVE              = 0x00000200
+	KF_FLAG_SIMPLE_IDLIST                    = 0x00000100
+	KF_FLAG_ALIAS_ONLY                       = 0x80000000
+)
+
+type OsVersionInfoEx struct {
+	osVersionInfoSize uint32
+	MajorVersion      uint32
+	MinorVersion      uint32
+	BuildNumber       uint32
+	PlatformId        uint32
+	CsdVersion        [128]uint16
+	ServicePackMajor  uint16
+	ServicePackMinor  uint16
+	SuiteMask         uint16
+	ProductType       byte
+	_                 byte
+}
+
+const (
+	EWX_LOGOFF          = 0x00000000
+	EWX_SHUTDOWN        = 0x00000001
+	EWX_REBOOT          = 0x00000002
+	EWX_FORCE           = 0x00000004
+	EWX_POWEROFF        = 0x00000008
+	EWX_FORCEIFHUNG     = 0x00000010
+	EWX_QUICKRESOLVE    = 0x00000020
+	EWX_RESTARTAPPS     = 0x00000040
+	EWX_HYBRID_SHUTDOWN = 0x00400000
+	EWX_BOOTOPTIONS     = 0x01000000
+
+	SHTDN_REASON_FLAG_COMMENT_REQUIRED          = 0x01000000
+	SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED = 0x02000000
+	SHTDN_REASON_FLAG_CLEAN_UI                  = 0x04000000
+	SHTDN_REASON_FLAG_DIRTY_UI                  = 0x08000000
+	SHTDN_REASON_FLAG_USER_DEFINED              = 0x40000000
+	SHTDN_REASON_FLAG_PLANNED                   = 0x80000000
+	SHTDN_REASON_MAJOR_OTHER                    = 0x00000000
+	SHTDN_REASON_MAJOR_NONE                     = 0x00000000
+	SHTDN_REASON_MAJOR_HARDWARE                 = 0x00010000
+	SHTDN_REASON_MAJOR_OPERATINGSYSTEM          = 0x00020000
+	SHTDN_REASON_MAJOR_SOFTWARE                 = 0x00030000
+	SHTDN_REASON_MAJOR_APPLICATION              = 0x00040000
+	SHTDN_REASON_MAJOR_SYSTEM                   = 0x00050000
+	SHTDN_REASON_MAJOR_POWER                    = 0x00060000
+	SHTDN_REASON_MAJOR_LEGACY_API               = 0x00070000
+	SHTDN_REASON_MINOR_OTHER                    = 0x00000000
+	SHTDN_REASON_MINOR_NONE                     = 0x000000ff
+	SHTDN_REASON_MINOR_MAINTENANCE              = 0x00000001
+	SHTDN_REASON_MINOR_INSTALLATION             = 0x00000002
+	SHTDN_REASON_MINOR_UPGRADE                  = 0x00000003
+	SHTDN_REASON_MINOR_RECONFIG                 = 0x00000004
+	SHTDN_REASON_MINOR_HUNG                     = 0x00000005
+	SHTDN_REASON_MINOR_UNSTABLE                 = 0x00000006
+	SHTDN_REASON_MINOR_DISK                     = 0x00000007
+	SHTDN_REASON_MINOR_PROCESSOR                = 0x00000008
+	SHTDN_REASON_MINOR_NETWORKCARD              = 0x00000009
+	SHTDN_REASON_MINOR_POWER_SUPPLY             = 0x0000000a
+	SHTDN_REASON_MINOR_CORDUNPLUGGED            = 0x0000000b
+	SHTDN_REASON_MINOR_ENVIRONMENT              = 0x0000000c
+	SHTDN_REASON_MINOR_HARDWARE_DRIVER          = 0x0000000d
+	SHTDN_REASON_MINOR_OTHERDRIVER              = 0x0000000e
+	SHTDN_REASON_MINOR_BLUESCREEN               = 0x0000000F
+	SHTDN_REASON_MINOR_SERVICEPACK              = 0x00000010
+	SHTDN_REASON_MINOR_HOTFIX                   = 0x00000011
+	SHTDN_REASON_MINOR_SECURITYFIX              = 0x00000012
+	SHTDN_REASON_MINOR_SECURITY                 = 0x00000013
+	SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY     = 0x00000014
+	SHTDN_REASON_MINOR_WMI                      = 0x00000015
+	SHTDN_REASON_MINOR_SERVICEPACK_UNINSTALL    = 0x00000016
+	SHTDN_REASON_MINOR_HOTFIX_UNINSTALL         = 0x00000017
+	SHTDN_REASON_MINOR_SECURITYFIX_UNINSTALL    = 0x00000018
+	SHTDN_REASON_MINOR_MMC                      = 0x00000019
+	SHTDN_REASON_MINOR_SYSTEMRESTORE            = 0x0000001a
+	SHTDN_REASON_MINOR_TERMSRV                  = 0x00000020
+	SHTDN_REASON_MINOR_DC_PROMOTION             = 0x00000021
+	SHTDN_REASON_MINOR_DC_DEMOTION              = 0x00000022
+	SHTDN_REASON_UNKNOWN                        = SHTDN_REASON_MINOR_NONE
+	SHTDN_REASON_LEGACY_API                     = SHTDN_REASON_MAJOR_LEGACY_API | SHTDN_REASON_FLAG_PLANNED
+	SHTDN_REASON_VALID_BIT_MASK                 = 0xc0ffffff
+
+	SHUTDOWN_NORETRY = 0x1
+)
+
+// Flags used for GetModuleHandleEx
+const (
+	GET_MODULE_HANDLE_EX_FLAG_PIN                = 1
+	GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2
+	GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS       = 4
+)
+
+// MUI function flag values
+const (
+	MUI_LANGUAGE_ID                    = 0x4
+	MUI_LANGUAGE_NAME                  = 0x8
+	MUI_MERGE_SYSTEM_FALLBACK          = 0x10
+	MUI_MERGE_USER_FALLBACK            = 0x20
+	MUI_UI_FALLBACK                    = MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK
+	MUI_THREAD_LANGUAGES               = 0x40
+	MUI_CONSOLE_FILTER                 = 0x100
+	MUI_COMPLEX_SCRIPT_FILTER          = 0x200
+	MUI_RESET_FILTERS                  = 0x001
+	MUI_USER_PREFERRED_UI_LANGUAGES    = 0x10
+	MUI_USE_INSTALLED_LANGUAGES        = 0x20
+	MUI_USE_SEARCH_ALL_LANGUAGES       = 0x40
+	MUI_LANG_NEUTRAL_PE_FILE           = 0x100
+	MUI_NON_LANG_NEUTRAL_FILE          = 0x200
+	MUI_MACHINE_LANGUAGE_SETTINGS      = 0x400
+	MUI_FILETYPE_NOT_LANGUAGE_NEUTRAL  = 0x001
+	MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN = 0x002
+	MUI_FILETYPE_LANGUAGE_NEUTRAL_MUI  = 0x004
+	MUI_QUERY_TYPE                     = 0x001
+	MUI_QUERY_CHECKSUM                 = 0x002
+	MUI_QUERY_LANGUAGE_NAME            = 0x004
+	MUI_QUERY_RESOURCE_TYPES           = 0x008
+	MUI_FILEINFO_VERSION               = 0x001
+
+	MUI_FULL_LANGUAGE      = 0x01
+	MUI_PARTIAL_LANGUAGE   = 0x02
+	MUI_LIP_LANGUAGE       = 0x04
+	MUI_LANGUAGE_INSTALLED = 0x20
+	MUI_LANGUAGE_LICENSED  = 0x40
+)

+ 1 - 1
go/vendor/golang.org/x/sys/windows/zerrors_windows.go

@@ -1,4 +1,4 @@
-// Code generated by 'go generate'; DO NOT EDIT.
+// Code generated by 'mkerrors.bash'; DO NOT EDIT.
 
 package windows
 

+ 149 - 0
go/vendor/golang.org/x/sys/windows/zknownfolderids_windows.go

@@ -0,0 +1,149 @@
+// Code generated by 'mkknownfolderids.bash'; DO NOT EDIT.
+
+package windows
+
+type KNOWNFOLDERID GUID
+
+var (
+	FOLDERID_NetworkFolder          = &KNOWNFOLDERID{0xd20beec4, 0x5ca8, 0x4905, [8]byte{0xae, 0x3b, 0xbf, 0x25, 0x1e, 0xa0, 0x9b, 0x53}}
+	FOLDERID_ComputerFolder         = &KNOWNFOLDERID{0x0ac0837c, 0xbbf8, 0x452a, [8]byte{0x85, 0x0d, 0x79, 0xd0, 0x8e, 0x66, 0x7c, 0xa7}}
+	FOLDERID_InternetFolder         = &KNOWNFOLDERID{0x4d9f7874, 0x4e0c, 0x4904, [8]byte{0x96, 0x7b, 0x40, 0xb0, 0xd2, 0x0c, 0x3e, 0x4b}}
+	FOLDERID_ControlPanelFolder     = &KNOWNFOLDERID{0x82a74aeb, 0xaeb4, 0x465c, [8]byte{0xa0, 0x14, 0xd0, 0x97, 0xee, 0x34, 0x6d, 0x63}}
+	FOLDERID_PrintersFolder         = &KNOWNFOLDERID{0x76fc4e2d, 0xd6ad, 0x4519, [8]byte{0xa6, 0x63, 0x37, 0xbd, 0x56, 0x06, 0x81, 0x85}}
+	FOLDERID_SyncManagerFolder      = &KNOWNFOLDERID{0x43668bf8, 0xc14e, 0x49b2, [8]byte{0x97, 0xc9, 0x74, 0x77, 0x84, 0xd7, 0x84, 0xb7}}
+	FOLDERID_SyncSetupFolder        = &KNOWNFOLDERID{0x0f214138, 0xb1d3, 0x4a90, [8]byte{0xbb, 0xa9, 0x27, 0xcb, 0xc0, 0xc5, 0x38, 0x9a}}
+	FOLDERID_ConflictFolder         = &KNOWNFOLDERID{0x4bfefb45, 0x347d, 0x4006, [8]byte{0xa5, 0xbe, 0xac, 0x0c, 0xb0, 0x56, 0x71, 0x92}}
+	FOLDERID_SyncResultsFolder      = &KNOWNFOLDERID{0x289a9a43, 0xbe44, 0x4057, [8]byte{0xa4, 0x1b, 0x58, 0x7a, 0x76, 0xd7, 0xe7, 0xf9}}
+	FOLDERID_RecycleBinFolder       = &KNOWNFOLDERID{0xb7534046, 0x3ecb, 0x4c18, [8]byte{0xbe, 0x4e, 0x64, 0xcd, 0x4c, 0xb7, 0xd6, 0xac}}
+	FOLDERID_ConnectionsFolder      = &KNOWNFOLDERID{0x6f0cd92b, 0x2e97, 0x45d1, [8]byte{0x88, 0xff, 0xb0, 0xd1, 0x86, 0xb8, 0xde, 0xdd}}
+	FOLDERID_Fonts                  = &KNOWNFOLDERID{0xfd228cb7, 0xae11, 0x4ae3, [8]byte{0x86, 0x4c, 0x16, 0xf3, 0x91, 0x0a, 0xb8, 0xfe}}
+	FOLDERID_Desktop                = &KNOWNFOLDERID{0xb4bfcc3a, 0xdb2c, 0x424c, [8]byte{0xb0, 0x29, 0x7f, 0xe9, 0x9a, 0x87, 0xc6, 0x41}}
+	FOLDERID_Startup                = &KNOWNFOLDERID{0xb97d20bb, 0xf46a, 0x4c97, [8]byte{0xba, 0x10, 0x5e, 0x36, 0x08, 0x43, 0x08, 0x54}}
+	FOLDERID_Programs               = &KNOWNFOLDERID{0xa77f5d77, 0x2e2b, 0x44c3, [8]byte{0xa6, 0xa2, 0xab, 0xa6, 0x01, 0x05, 0x4a, 0x51}}
+	FOLDERID_StartMenu              = &KNOWNFOLDERID{0x625b53c3, 0xab48, 0x4ec1, [8]byte{0xba, 0x1f, 0xa1, 0xef, 0x41, 0x46, 0xfc, 0x19}}
+	FOLDERID_Recent                 = &KNOWNFOLDERID{0xae50c081, 0xebd2, 0x438a, [8]byte{0x86, 0x55, 0x8a, 0x09, 0x2e, 0x34, 0x98, 0x7a}}
+	FOLDERID_SendTo                 = &KNOWNFOLDERID{0x8983036c, 0x27c0, 0x404b, [8]byte{0x8f, 0x08, 0x10, 0x2d, 0x10, 0xdc, 0xfd, 0x74}}
+	FOLDERID_Documents              = &KNOWNFOLDERID{0xfdd39ad0, 0x238f, 0x46af, [8]byte{0xad, 0xb4, 0x6c, 0x85, 0x48, 0x03, 0x69, 0xc7}}
+	FOLDERID_Favorites              = &KNOWNFOLDERID{0x1777f761, 0x68ad, 0x4d8a, [8]byte{0x87, 0xbd, 0x30, 0xb7, 0x59, 0xfa, 0x33, 0xdd}}
+	FOLDERID_NetHood                = &KNOWNFOLDERID{0xc5abbf53, 0xe17f, 0x4121, [8]byte{0x89, 0x00, 0x86, 0x62, 0x6f, 0xc2, 0xc9, 0x73}}
+	FOLDERID_PrintHood              = &KNOWNFOLDERID{0x9274bd8d, 0xcfd1, 0x41c3, [8]byte{0xb3, 0x5e, 0xb1, 0x3f, 0x55, 0xa7, 0x58, 0xf4}}
+	FOLDERID_Templates              = &KNOWNFOLDERID{0xa63293e8, 0x664e, 0x48db, [8]byte{0xa0, 0x79, 0xdf, 0x75, 0x9e, 0x05, 0x09, 0xf7}}
+	FOLDERID_CommonStartup          = &KNOWNFOLDERID{0x82a5ea35, 0xd9cd, 0x47c5, [8]byte{0x96, 0x29, 0xe1, 0x5d, 0x2f, 0x71, 0x4e, 0x6e}}
+	FOLDERID_CommonPrograms         = &KNOWNFOLDERID{0x0139d44e, 0x6afe, 0x49f2, [8]byte{0x86, 0x90, 0x3d, 0xaf, 0xca, 0xe6, 0xff, 0xb8}}
+	FOLDERID_CommonStartMenu        = &KNOWNFOLDERID{0xa4115719, 0xd62e, 0x491d, [8]byte{0xaa, 0x7c, 0xe7, 0x4b, 0x8b, 0xe3, 0xb0, 0x67}}
+	FOLDERID_PublicDesktop          = &KNOWNFOLDERID{0xc4aa340d, 0xf20f, 0x4863, [8]byte{0xaf, 0xef, 0xf8, 0x7e, 0xf2, 0xe6, 0xba, 0x25}}
+	FOLDERID_ProgramData            = &KNOWNFOLDERID{0x62ab5d82, 0xfdc1, 0x4dc3, [8]byte{0xa9, 0xdd, 0x07, 0x0d, 0x1d, 0x49, 0x5d, 0x97}}
+	FOLDERID_CommonTemplates        = &KNOWNFOLDERID{0xb94237e7, 0x57ac, 0x4347, [8]byte{0x91, 0x51, 0xb0, 0x8c, 0x6c, 0x32, 0xd1, 0xf7}}
+	FOLDERID_PublicDocuments        = &KNOWNFOLDERID{0xed4824af, 0xdce4, 0x45a8, [8]byte{0x81, 0xe2, 0xfc, 0x79, 0x65, 0x08, 0x36, 0x34}}
+	FOLDERID_RoamingAppData         = &KNOWNFOLDERID{0x3eb685db, 0x65f9, 0x4cf6, [8]byte{0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}}
+	FOLDERID_LocalAppData           = &KNOWNFOLDERID{0xf1b32785, 0x6fba, 0x4fcf, [8]byte{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91}}
+	FOLDERID_LocalAppDataLow        = &KNOWNFOLDERID{0xa520a1a4, 0x1780, 0x4ff6, [8]byte{0xbd, 0x18, 0x16, 0x73, 0x43, 0xc5, 0xaf, 0x16}}
+	FOLDERID_InternetCache          = &KNOWNFOLDERID{0x352481e8, 0x33be, 0x4251, [8]byte{0xba, 0x85, 0x60, 0x07, 0xca, 0xed, 0xcf, 0x9d}}
+	FOLDERID_Cookies                = &KNOWNFOLDERID{0x2b0f765d, 0xc0e9, 0x4171, [8]byte{0x90, 0x8e, 0x08, 0xa6, 0x11, 0xb8, 0x4f, 0xf6}}
+	FOLDERID_History                = &KNOWNFOLDERID{0xd9dc8a3b, 0xb784, 0x432e, [8]byte{0xa7, 0x81, 0x5a, 0x11, 0x30, 0xa7, 0x59, 0x63}}
+	FOLDERID_System                 = &KNOWNFOLDERID{0x1ac14e77, 0x02e7, 0x4e5d, [8]byte{0xb7, 0x44, 0x2e, 0xb1, 0xae, 0x51, 0x98, 0xb7}}
+	FOLDERID_SystemX86              = &KNOWNFOLDERID{0xd65231b0, 0xb2f1, 0x4857, [8]byte{0xa4, 0xce, 0xa8, 0xe7, 0xc6, 0xea, 0x7d, 0x27}}
+	FOLDERID_Windows                = &KNOWNFOLDERID{0xf38bf404, 0x1d43, 0x42f2, [8]byte{0x93, 0x05, 0x67, 0xde, 0x0b, 0x28, 0xfc, 0x23}}
+	FOLDERID_Profile                = &KNOWNFOLDERID{0x5e6c858f, 0x0e22, 0x4760, [8]byte{0x9a, 0xfe, 0xea, 0x33, 0x17, 0xb6, 0x71, 0x73}}
+	FOLDERID_Pictures               = &KNOWNFOLDERID{0x33e28130, 0x4e1e, 0x4676, [8]byte{0x83, 0x5a, 0x98, 0x39, 0x5c, 0x3b, 0xc3, 0xbb}}
+	FOLDERID_ProgramFilesX86        = &KNOWNFOLDERID{0x7c5a40ef, 0xa0fb, 0x4bfc, [8]byte{0x87, 0x4a, 0xc0, 0xf2, 0xe0, 0xb9, 0xfa, 0x8e}}
+	FOLDERID_ProgramFilesCommonX86  = &KNOWNFOLDERID{0xde974d24, 0xd9c6, 0x4d3e, [8]byte{0xbf, 0x91, 0xf4, 0x45, 0x51, 0x20, 0xb9, 0x17}}
+	FOLDERID_ProgramFilesX64        = &KNOWNFOLDERID{0x6d809377, 0x6af0, 0x444b, [8]byte{0x89, 0x57, 0xa3, 0x77, 0x3f, 0x02, 0x20, 0x0e}}
+	FOLDERID_ProgramFilesCommonX64  = &KNOWNFOLDERID{0x6365d5a7, 0x0f0d, 0x45e5, [8]byte{0x87, 0xf6, 0x0d, 0xa5, 0x6b, 0x6a, 0x4f, 0x7d}}
+	FOLDERID_ProgramFiles           = &KNOWNFOLDERID{0x905e63b6, 0xc1bf, 0x494e, [8]byte{0xb2, 0x9c, 0x65, 0xb7, 0x32, 0xd3, 0xd2, 0x1a}}
+	FOLDERID_ProgramFilesCommon     = &KNOWNFOLDERID{0xf7f1ed05, 0x9f6d, 0x47a2, [8]byte{0xaa, 0xae, 0x29, 0xd3, 0x17, 0xc6, 0xf0, 0x66}}
+	FOLDERID_UserProgramFiles       = &KNOWNFOLDERID{0x5cd7aee2, 0x2219, 0x4a67, [8]byte{0xb8, 0x5d, 0x6c, 0x9c, 0xe1, 0x56, 0x60, 0xcb}}
+	FOLDERID_UserProgramFilesCommon = &KNOWNFOLDERID{0xbcbd3057, 0xca5c, 0x4622, [8]byte{0xb4, 0x2d, 0xbc, 0x56, 0xdb, 0x0a, 0xe5, 0x16}}
+	FOLDERID_AdminTools             = &KNOWNFOLDERID{0x724ef170, 0xa42d, 0x4fef, [8]byte{0x9f, 0x26, 0xb6, 0x0e, 0x84, 0x6f, 0xba, 0x4f}}
+	FOLDERID_CommonAdminTools       = &KNOWNFOLDERID{0xd0384e7d, 0xbac3, 0x4797, [8]byte{0x8f, 0x14, 0xcb, 0xa2, 0x29, 0xb3, 0x92, 0xb5}}
+	FOLDERID_Music                  = &KNOWNFOLDERID{0x4bd8d571, 0x6d19, 0x48d3, [8]byte{0xbe, 0x97, 0x42, 0x22, 0x20, 0x08, 0x0e, 0x43}}
+	FOLDERID_Videos                 = &KNOWNFOLDERID{0x18989b1d, 0x99b5, 0x455b, [8]byte{0x84, 0x1c, 0xab, 0x7c, 0x74, 0xe4, 0xdd, 0xfc}}
+	FOLDERID_Ringtones              = &KNOWNFOLDERID{0xc870044b, 0xf49e, 0x4126, [8]byte{0xa9, 0xc3, 0xb5, 0x2a, 0x1f, 0xf4, 0x11, 0xe8}}
+	FOLDERID_PublicPictures         = &KNOWNFOLDERID{0xb6ebfb86, 0x6907, 0x413c, [8]byte{0x9a, 0xf7, 0x4f, 0xc2, 0xab, 0xf0, 0x7c, 0xc5}}
+	FOLDERID_PublicMusic            = &KNOWNFOLDERID{0x3214fab5, 0x9757, 0x4298, [8]byte{0xbb, 0x61, 0x92, 0xa9, 0xde, 0xaa, 0x44, 0xff}}
+	FOLDERID_PublicVideos           = &KNOWNFOLDERID{0x2400183a, 0x6185, 0x49fb, [8]byte{0xa2, 0xd8, 0x4a, 0x39, 0x2a, 0x60, 0x2b, 0xa3}}
+	FOLDERID_PublicRingtones        = &KNOWNFOLDERID{0xe555ab60, 0x153b, 0x4d17, [8]byte{0x9f, 0x04, 0xa5, 0xfe, 0x99, 0xfc, 0x15, 0xec}}
+	FOLDERID_ResourceDir            = &KNOWNFOLDERID{0x8ad10c31, 0x2adb, 0x4296, [8]byte{0xa8, 0xf7, 0xe4, 0x70, 0x12, 0x32, 0xc9, 0x72}}
+	FOLDERID_LocalizedResourcesDir  = &KNOWNFOLDERID{0x2a00375e, 0x224c, 0x49de, [8]byte{0xb8, 0xd1, 0x44, 0x0d, 0xf7, 0xef, 0x3d, 0xdc}}
+	FOLDERID_CommonOEMLinks         = &KNOWNFOLDERID{0xc1bae2d0, 0x10df, 0x4334, [8]byte{0xbe, 0xdd, 0x7a, 0xa2, 0x0b, 0x22, 0x7a, 0x9d}}
+	FOLDERID_CDBurning              = &KNOWNFOLDERID{0x9e52ab10, 0xf80d, 0x49df, [8]byte{0xac, 0xb8, 0x43, 0x30, 0xf5, 0x68, 0x78, 0x55}}
+	FOLDERID_UserProfiles           = &KNOWNFOLDERID{0x0762d272, 0xc50a, 0x4bb0, [8]byte{0xa3, 0x82, 0x69, 0x7d, 0xcd, 0x72, 0x9b, 0x80}}
+	FOLDERID_Playlists              = &KNOWNFOLDERID{0xde92c1c7, 0x837f, 0x4f69, [8]byte{0xa3, 0xbb, 0x86, 0xe6, 0x31, 0x20, 0x4a, 0x23}}
+	FOLDERID_SamplePlaylists        = &KNOWNFOLDERID{0x15ca69b3, 0x30ee, 0x49c1, [8]byte{0xac, 0xe1, 0x6b, 0x5e, 0xc3, 0x72, 0xaf, 0xb5}}
+	FOLDERID_SampleMusic            = &KNOWNFOLDERID{0xb250c668, 0xf57d, 0x4ee1, [8]byte{0xa6, 0x3c, 0x29, 0x0e, 0xe7, 0xd1, 0xaa, 0x1f}}
+	FOLDERID_SamplePictures         = &KNOWNFOLDERID{0xc4900540, 0x2379, 0x4c75, [8]byte{0x84, 0x4b, 0x64, 0xe6, 0xfa, 0xf8, 0x71, 0x6b}}
+	FOLDERID_SampleVideos           = &KNOWNFOLDERID{0x859ead94, 0x2e85, 0x48ad, [8]byte{0xa7, 0x1a, 0x09, 0x69, 0xcb, 0x56, 0xa6, 0xcd}}
+	FOLDERID_PhotoAlbums            = &KNOWNFOLDERID{0x69d2cf90, 0xfc33, 0x4fb7, [8]byte{0x9a, 0x0c, 0xeb, 0xb0, 0xf0, 0xfc, 0xb4, 0x3c}}
+	FOLDERID_Public                 = &KNOWNFOLDERID{0xdfdf76a2, 0xc82a, 0x4d63, [8]byte{0x90, 0x6a, 0x56, 0x44, 0xac, 0x45, 0x73, 0x85}}
+	FOLDERID_ChangeRemovePrograms   = &KNOWNFOLDERID{0xdf7266ac, 0x9274, 0x4867, [8]byte{0x8d, 0x55, 0x3b, 0xd6, 0x61, 0xde, 0x87, 0x2d}}
+	FOLDERID_AppUpdates             = &KNOWNFOLDERID{0xa305ce99, 0xf527, 0x492b, [8]byte{0x8b, 0x1a, 0x7e, 0x76, 0xfa, 0x98, 0xd6, 0xe4}}
+	FOLDERID_AddNewPrograms         = &KNOWNFOLDERID{0xde61d971, 0x5ebc, 0x4f02, [8]byte{0xa3, 0xa9, 0x6c, 0x82, 0x89, 0x5e, 0x5c, 0x04}}
+	FOLDERID_Downloads              = &KNOWNFOLDERID{0x374de290, 0x123f, 0x4565, [8]byte{0x91, 0x64, 0x39, 0xc4, 0x92, 0x5e, 0x46, 0x7b}}
+	FOLDERID_PublicDownloads        = &KNOWNFOLDERID{0x3d644c9b, 0x1fb8, 0x4f30, [8]byte{0x9b, 0x45, 0xf6, 0x70, 0x23, 0x5f, 0x79, 0xc0}}
+	FOLDERID_SavedSearches          = &KNOWNFOLDERID{0x7d1d3a04, 0xdebb, 0x4115, [8]byte{0x95, 0xcf, 0x2f, 0x29, 0xda, 0x29, 0x20, 0xda}}
+	FOLDERID_QuickLaunch            = &KNOWNFOLDERID{0x52a4f021, 0x7b75, 0x48a9, [8]byte{0x9f, 0x6b, 0x4b, 0x87, 0xa2, 0x10, 0xbc, 0x8f}}
+	FOLDERID_Contacts               = &KNOWNFOLDERID{0x56784854, 0xc6cb, 0x462b, [8]byte{0x81, 0x69, 0x88, 0xe3, 0x50, 0xac, 0xb8, 0x82}}
+	FOLDERID_SidebarParts           = &KNOWNFOLDERID{0xa75d362e, 0x50fc, 0x4fb7, [8]byte{0xac, 0x2c, 0xa8, 0xbe, 0xaa, 0x31, 0x44, 0x93}}
+	FOLDERID_SidebarDefaultParts    = &KNOWNFOLDERID{0x7b396e54, 0x9ec5, 0x4300, [8]byte{0xbe, 0x0a, 0x24, 0x82, 0xeb, 0xae, 0x1a, 0x26}}
+	FOLDERID_PublicGameTasks        = &KNOWNFOLDERID{0xdebf2536, 0xe1a8, 0x4c59, [8]byte{0xb6, 0xa2, 0x41, 0x45, 0x86, 0x47, 0x6a, 0xea}}
+	FOLDERID_GameTasks              = &KNOWNFOLDERID{0x054fae61, 0x4dd8, 0x4787, [8]byte{0x80, 0xb6, 0x09, 0x02, 0x20, 0xc4, 0xb7, 0x00}}
+	FOLDERID_SavedGames             = &KNOWNFOLDERID{0x4c5c32ff, 0xbb9d, 0x43b0, [8]byte{0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4}}
+	FOLDERID_Games                  = &KNOWNFOLDERID{0xcac52c1a, 0xb53d, 0x4edc, [8]byte{0x92, 0xd7, 0x6b, 0x2e, 0x8a, 0xc1, 0x94, 0x34}}
+	FOLDERID_SEARCH_MAPI            = &KNOWNFOLDERID{0x98ec0e18, 0x2098, 0x4d44, [8]byte{0x86, 0x44, 0x66, 0x97, 0x93, 0x15, 0xa2, 0x81}}
+	FOLDERID_SEARCH_CSC             = &KNOWNFOLDERID{0xee32e446, 0x31ca, 0x4aba, [8]byte{0x81, 0x4f, 0xa5, 0xeb, 0xd2, 0xfd, 0x6d, 0x5e}}
+	FOLDERID_Links                  = &KNOWNFOLDERID{0xbfb9d5e0, 0xc6a9, 0x404c, [8]byte{0xb2, 0xb2, 0xae, 0x6d, 0xb6, 0xaf, 0x49, 0x68}}
+	FOLDERID_UsersFiles             = &KNOWNFOLDERID{0xf3ce0f7c, 0x4901, 0x4acc, [8]byte{0x86, 0x48, 0xd5, 0xd4, 0x4b, 0x04, 0xef, 0x8f}}
+	FOLDERID_UsersLibraries         = &KNOWNFOLDERID{0xa302545d, 0xdeff, 0x464b, [8]byte{0xab, 0xe8, 0x61, 0xc8, 0x64, 0x8d, 0x93, 0x9b}}
+	FOLDERID_SearchHome             = &KNOWNFOLDERID{0x190337d1, 0xb8ca, 0x4121, [8]byte{0xa6, 0x39, 0x6d, 0x47, 0x2d, 0x16, 0x97, 0x2a}}
+	FOLDERID_OriginalImages         = &KNOWNFOLDERID{0x2c36c0aa, 0x5812, 0x4b87, [8]byte{0xbf, 0xd0, 0x4c, 0xd0, 0xdf, 0xb1, 0x9b, 0x39}}
+	FOLDERID_DocumentsLibrary       = &KNOWNFOLDERID{0x7b0db17d, 0x9cd2, 0x4a93, [8]byte{0x97, 0x33, 0x46, 0xcc, 0x89, 0x02, 0x2e, 0x7c}}
+	FOLDERID_MusicLibrary           = &KNOWNFOLDERID{0x2112ab0a, 0xc86a, 0x4ffe, [8]byte{0xa3, 0x68, 0x0d, 0xe9, 0x6e, 0x47, 0x01, 0x2e}}
+	FOLDERID_PicturesLibrary        = &KNOWNFOLDERID{0xa990ae9f, 0xa03b, 0x4e80, [8]byte{0x94, 0xbc, 0x99, 0x12, 0xd7, 0x50, 0x41, 0x04}}
+	FOLDERID_VideosLibrary          = &KNOWNFOLDERID{0x491e922f, 0x5643, 0x4af4, [8]byte{0xa7, 0xeb, 0x4e, 0x7a, 0x13, 0x8d, 0x81, 0x74}}
+	FOLDERID_RecordedTVLibrary      = &KNOWNFOLDERID{0x1a6fdba2, 0xf42d, 0x4358, [8]byte{0xa7, 0x98, 0xb7, 0x4d, 0x74, 0x59, 0x26, 0xc5}}
+	FOLDERID_HomeGroup              = &KNOWNFOLDERID{0x52528a6b, 0xb9e3, 0x4add, [8]byte{0xb6, 0x0d, 0x58, 0x8c, 0x2d, 0xba, 0x84, 0x2d}}
+	FOLDERID_HomeGroupCurrentUser   = &KNOWNFOLDERID{0x9b74b6a3, 0x0dfd, 0x4f11, [8]byte{0x9e, 0x78, 0x5f, 0x78, 0x00, 0xf2, 0xe7, 0x72}}
+	FOLDERID_DeviceMetadataStore    = &KNOWNFOLDERID{0x5ce4a5e9, 0xe4eb, 0x479d, [8]byte{0xb8, 0x9f, 0x13, 0x0c, 0x02, 0x88, 0x61, 0x55}}
+	FOLDERID_Libraries              = &KNOWNFOLDERID{0x1b3ea5dc, 0xb587, 0x4786, [8]byte{0xb4, 0xef, 0xbd, 0x1d, 0xc3, 0x32, 0xae, 0xae}}
+	FOLDERID_PublicLibraries        = &KNOWNFOLDERID{0x48daf80b, 0xe6cf, 0x4f4e, [8]byte{0xb8, 0x00, 0x0e, 0x69, 0xd8, 0x4e, 0xe3, 0x84}}
+	FOLDERID_UserPinned             = &KNOWNFOLDERID{0x9e3995ab, 0x1f9c, 0x4f13, [8]byte{0xb8, 0x27, 0x48, 0xb2, 0x4b, 0x6c, 0x71, 0x74}}
+	FOLDERID_ImplicitAppShortcuts   = &KNOWNFOLDERID{0xbcb5256f, 0x79f6, 0x4cee, [8]byte{0xb7, 0x25, 0xdc, 0x34, 0xe4, 0x02, 0xfd, 0x46}}
+	FOLDERID_AccountPictures        = &KNOWNFOLDERID{0x008ca0b1, 0x55b4, 0x4c56, [8]byte{0xb8, 0xa8, 0x4d, 0xe4, 0xb2, 0x99, 0xd3, 0xbe}}
+	FOLDERID_PublicUserTiles        = &KNOWNFOLDERID{0x0482af6c, 0x08f1, 0x4c34, [8]byte{0x8c, 0x90, 0xe1, 0x7e, 0xc9, 0x8b, 0x1e, 0x17}}
+	FOLDERID_AppsFolder             = &KNOWNFOLDERID{0x1e87508d, 0x89c2, 0x42f0, [8]byte{0x8a, 0x7e, 0x64, 0x5a, 0x0f, 0x50, 0xca, 0x58}}
+	FOLDERID_StartMenuAllPrograms   = &KNOWNFOLDERID{0xf26305ef, 0x6948, 0x40b9, [8]byte{0xb2, 0x55, 0x81, 0x45, 0x3d, 0x09, 0xc7, 0x85}}
+	FOLDERID_CommonStartMenuPlaces  = &KNOWNFOLDERID{0xa440879f, 0x87a0, 0x4f7d, [8]byte{0xb7, 0x00, 0x02, 0x07, 0xb9, 0x66, 0x19, 0x4a}}
+	FOLDERID_ApplicationShortcuts   = &KNOWNFOLDERID{0xa3918781, 0xe5f2, 0x4890, [8]byte{0xb3, 0xd9, 0xa7, 0xe5, 0x43, 0x32, 0x32, 0x8c}}
+	FOLDERID_RoamingTiles           = &KNOWNFOLDERID{0x00bcfc5a, 0xed94, 0x4e48, [8]byte{0x96, 0xa1, 0x3f, 0x62, 0x17, 0xf2, 0x19, 0x90}}
+	FOLDERID_RoamedTileImages       = &KNOWNFOLDERID{0xaaa8d5a5, 0xf1d6, 0x4259, [8]byte{0xba, 0xa8, 0x78, 0xe7, 0xef, 0x60, 0x83, 0x5e}}
+	FOLDERID_Screenshots            = &KNOWNFOLDERID{0xb7bede81, 0xdf94, 0x4682, [8]byte{0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f}}
+	FOLDERID_CameraRoll             = &KNOWNFOLDERID{0xab5fb87b, 0x7ce2, 0x4f83, [8]byte{0x91, 0x5d, 0x55, 0x08, 0x46, 0xc9, 0x53, 0x7b}}
+	FOLDERID_SkyDrive               = &KNOWNFOLDERID{0xa52bba46, 0xe9e1, 0x435f, [8]byte{0xb3, 0xd9, 0x28, 0xda, 0xa6, 0x48, 0xc0, 0xf6}}
+	FOLDERID_OneDrive               = &KNOWNFOLDERID{0xa52bba46, 0xe9e1, 0x435f, [8]byte{0xb3, 0xd9, 0x28, 0xda, 0xa6, 0x48, 0xc0, 0xf6}}
+	FOLDERID_SkyDriveDocuments      = &KNOWNFOLDERID{0x24d89e24, 0x2f19, 0x4534, [8]byte{0x9d, 0xde, 0x6a, 0x66, 0x71, 0xfb, 0xb8, 0xfe}}
+	FOLDERID_SkyDrivePictures       = &KNOWNFOLDERID{0x339719b5, 0x8c47, 0x4894, [8]byte{0x94, 0xc2, 0xd8, 0xf7, 0x7a, 0xdd, 0x44, 0xa6}}
+	FOLDERID_SkyDriveMusic          = &KNOWNFOLDERID{0xc3f2459e, 0x80d6, 0x45dc, [8]byte{0xbf, 0xef, 0x1f, 0x76, 0x9f, 0x2b, 0xe7, 0x30}}
+	FOLDERID_SkyDriveCameraRoll     = &KNOWNFOLDERID{0x767e6811, 0x49cb, 0x4273, [8]byte{0x87, 0xc2, 0x20, 0xf3, 0x55, 0xe1, 0x08, 0x5b}}
+	FOLDERID_SearchHistory          = &KNOWNFOLDERID{0x0d4c3db6, 0x03a3, 0x462f, [8]byte{0xa0, 0xe6, 0x08, 0x92, 0x4c, 0x41, 0xb5, 0xd4}}
+	FOLDERID_SearchTemplates        = &KNOWNFOLDERID{0x7e636bfe, 0xdfa9, 0x4d5e, [8]byte{0xb4, 0x56, 0xd7, 0xb3, 0x98, 0x51, 0xd8, 0xa9}}
+	FOLDERID_CameraRollLibrary      = &KNOWNFOLDERID{0x2b20df75, 0x1eda, 0x4039, [8]byte{0x80, 0x97, 0x38, 0x79, 0x82, 0x27, 0xd5, 0xb7}}
+	FOLDERID_SavedPictures          = &KNOWNFOLDERID{0x3b193882, 0xd3ad, 0x4eab, [8]byte{0x96, 0x5a, 0x69, 0x82, 0x9d, 0x1f, 0xb5, 0x9f}}
+	FOLDERID_SavedPicturesLibrary   = &KNOWNFOLDERID{0xe25b5812, 0xbe88, 0x4bd9, [8]byte{0x94, 0xb0, 0x29, 0x23, 0x34, 0x77, 0xb6, 0xc3}}
+	FOLDERID_RetailDemo             = &KNOWNFOLDERID{0x12d4c69e, 0x24ad, 0x4923, [8]byte{0xbe, 0x19, 0x31, 0x32, 0x1c, 0x43, 0xa7, 0x67}}
+	FOLDERID_Device                 = &KNOWNFOLDERID{0x1c2ac1dc, 0x4358, 0x4b6c, [8]byte{0x97, 0x33, 0xaf, 0x21, 0x15, 0x65, 0x76, 0xf0}}
+	FOLDERID_DevelopmentFiles       = &KNOWNFOLDERID{0xdbe8e08e, 0x3053, 0x4bbc, [8]byte{0xb1, 0x83, 0x2a, 0x7b, 0x2b, 0x19, 0x1e, 0x59}}
+	FOLDERID_Objects3D              = &KNOWNFOLDERID{0x31c0dd25, 0x9439, 0x4f12, [8]byte{0xbf, 0x41, 0x7f, 0xf4, 0xed, 0xa3, 0x87, 0x22}}
+	FOLDERID_AppCaptures            = &KNOWNFOLDERID{0xedc0fe71, 0x98d8, 0x4f4a, [8]byte{0xb9, 0x20, 0xc8, 0xdc, 0x13, 0x3c, 0xb1, 0x65}}
+	FOLDERID_LocalDocuments         = &KNOWNFOLDERID{0xf42ee2d3, 0x909f, 0x4907, [8]byte{0x88, 0x71, 0x4c, 0x22, 0xfc, 0x0b, 0xf7, 0x56}}
+	FOLDERID_LocalPictures          = &KNOWNFOLDERID{0x0ddd015d, 0xb06c, 0x45d5, [8]byte{0x8c, 0x4c, 0xf5, 0x97, 0x13, 0x85, 0x46, 0x39}}
+	FOLDERID_LocalVideos            = &KNOWNFOLDERID{0x35286a68, 0x3c57, 0x41a1, [8]byte{0xbb, 0xb1, 0x0e, 0xae, 0x73, 0xd7, 0x6c, 0x95}}
+	FOLDERID_LocalMusic             = &KNOWNFOLDERID{0xa0c69a99, 0x21c8, 0x4671, [8]byte{0x87, 0x03, 0x79, 0x34, 0x16, 0x2f, 0xcf, 0x1d}}
+	FOLDERID_LocalDownloads         = &KNOWNFOLDERID{0x7d83ee9b, 0x2244, 0x4e70, [8]byte{0xb1, 0xf5, 0x53, 0x93, 0x04, 0x2a, 0xf1, 0xe4}}
+	FOLDERID_RecordedCalls          = &KNOWNFOLDERID{0x2f8b40c2, 0x83ed, 0x48ee, [8]byte{0xb3, 0x83, 0xa1, 0xf1, 0x57, 0xec, 0x6f, 0x9a}}
+	FOLDERID_AllAppMods             = &KNOWNFOLDERID{0x7ad67899, 0x66af, 0x43ba, [8]byte{0x91, 0x56, 0x6a, 0xad, 0x42, 0xe6, 0xc5, 0x96}}
+	FOLDERID_CurrentAppMods         = &KNOWNFOLDERID{0x3db40b20, 0x2a30, 0x4dbe, [8]byte{0x91, 0x7e, 0x77, 0x1d, 0xd2, 0x1d, 0xd0, 0x99}}
+	FOLDERID_AppDataDesktop         = &KNOWNFOLDERID{0xb2c5e279, 0x7add, 0x439f, [8]byte{0xb2, 0x8c, 0xc4, 0x1f, 0xe1, 0xbb, 0xf6, 0x72}}
+	FOLDERID_AppDataDocuments       = &KNOWNFOLDERID{0x7be16610, 0x1f7f, 0x44ac, [8]byte{0xbf, 0xf0, 0x83, 0xe1, 0x5f, 0x2f, 0xfc, 0xa1}}
+	FOLDERID_AppDataFavorites       = &KNOWNFOLDERID{0x7cfbefbc, 0xde1f, 0x45aa, [8]byte{0xb8, 0x43, 0xa5, 0x42, 0xac, 0x53, 0x6c, 0xc9}}
+	FOLDERID_AppDataProgramData     = &KNOWNFOLDERID{0x559d40a3, 0xa036, 0x40fa, [8]byte{0xaf, 0x61, 0x84, 0xcb, 0x43, 0x0a, 0x4d, 0x34}}
+)

Dosya farkı çok büyük olduğundan ihmal edildi
+ 574 - 291
go/vendor/golang.org/x/sys/windows/zsyscall_windows.go


+ 1 - 1
go/vendor/modules.txt

@@ -4,5 +4,5 @@ github.com/Microsoft/go-winio/pkg/guid
 # github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
 github.com/hectane/go-acl
 github.com/hectane/go-acl/api
-# golang.org/x/sys v0.0.0-20190529164535-6a60838ec259
+# golang.org/x/sys v0.0.0-20200107162124-548cf772de50
 golang.org/x/sys/windows

+ 159 - 400
include/ZeroTierCore.h

@@ -19,7 +19,12 @@
 #ifndef ZT_ZEROTIER_API_H
 #define ZT_ZEROTIER_API_H
 
+#ifdef __cplusplus
+#include <cstdint>
+extern "C" {
+#else
 #include <stdint.h>
+#endif
 
 /* For struct sockaddr_storage, which is referenced here. */
 #if defined(_WIN32) || defined(_WIN64)
@@ -39,10 +44,6 @@
 #define ZT_SDK_API
 #endif
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /****************************************************************************/
 /* Core constants                                                           */
 /****************************************************************************/
@@ -91,11 +92,6 @@ extern "C" {
  */
 #define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM)
 
-/**
- * Maximum size of a remote trace message's serialized Dictionary
- */
-#define ZT_MAX_REMOTE_TRACE_SIZE 10000
-
 /**
  * Maximum length of network short name
  */
@@ -258,78 +254,6 @@ extern "C" {
  */
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 
-/****************************************************************************/
-
-// Fields in remote trace dictionaries
-#define ZT_REMOTE_TRACE_FIELD__EVENT "event"
-#define ZT_REMOTE_TRACE_FIELD__NODE_ID "nodeId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_ID "packetId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_VERB "packetVerb"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_ID "packetTrustedPathId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_APPROVED "packetTrustedPathApproved"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_HOPS "packetHops"
-#define ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR "remoteZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR "remotePhyAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_ZTADDR "localZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR "localPhyAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET "localSocket"
-#define ZT_REMOTE_TRACE_FIELD__IP_SCOPE "phyAddrIpScope"
-#define ZT_REMOTE_TRACE_FIELD__NETWORK_ID "networkId"
-#define ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR "sourceZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR "destZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__SOURCE_MAC "sourceMac"
-#define ZT_REMOTE_TRACE_FIELD__DEST_MAC "destMac"
-#define ZT_REMOTE_TRACE_FIELD__ETHERTYPE "etherType"
-#define ZT_REMOTE_TRACE_FIELD__VLAN_ID "vlanId"
-#define ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH "frameLength"
-#define ZT_REMOTE_TRACE_FIELD__FRAME_DATA "frameData"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE "filterNoTee"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND "filterInbound"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_RESULT "filterResult"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG "filterBaseRuleLog"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG "filterCapRuleLog"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID "filterMatchingCapId"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE "credType"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID "credId"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP "credTs"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO "credInfo"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO "credIssuedTo"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET "credRevocationTarget"
-#define ZT_REMOTE_TRACE_FIELD__REASON "reason"
-#define ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID "networkControllerId"
-
-// Event types in remote traces
-#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE 0x1000
-#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH 0x1001
-#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH 0x1002
-#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED 0x1003
-#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE 0x1004
-#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID 0x1005
-#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO 0x1006
-#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED 0x2000
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED 0x2001
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED 0x2002
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED 0x2003
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED 0x2004
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT 0x2005
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE 0x2006
-
-// Event types in remote traces in hex string form
-#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S "1000"
-#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S "1001"
-#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S "1002"
-#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S "1003"
-#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S "1004"
-#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S "1005"
-#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO_S "1006"
-#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S "2000"
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S "2001"
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S "2002"
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S "2003"
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED_S "2004"
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S "2005"
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S "2006"
-
 /****************************************************************************/
 /* Structures and other types                                               */
 /****************************************************************************/
@@ -397,35 +321,6 @@ enum ZT_ResultCode
  */
 #define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
 
-/**
- * The multipath algorithm in use by this node.
- */
-enum ZT_MultipathMode
-{
-	/**
-	 * No active multipath.
-	 *
-	 * Traffic is merely sent over the strongest path. That being
-	 * said, this mode will automatically failover in the event that a link goes down.
-	 */
-	ZT_MULTIPATH_NONE = 0,
-
-	/**
-	 * Traffic is randomly distributed among all active paths.
-	 *
-	 * Will cease sending traffic over links that appear to be stale.
-	 */
-	ZT_MULTIPATH_RANDOM = 1,
-
-	/**
-	 * Traffic is allocated across all active paths in proportion to their strength and
-	 * reliability.
-	 *
-	 * Will cease sending traffic over links that appear to be stale.
-	 */
-	ZT_MULTIPATH_PROPORTIONALLY_BALANCED = 2,
-};
-
 /**
  * Status codes sent to status update callback when things happen
  */
@@ -499,87 +394,23 @@ enum ZT_Event
 	 *
 	 * Meta-data: ZT_UserMessage structure
 	 */
-	ZT_EVENT_USER_MESSAGE = 6,
-
-	/**
-	 * Remote trace received
-	 *
-	 * NOTE: any node can fling a VERB_REMOTE_TRACE at you. It's up to you
-	 * to determine if you want to do anything with it or just silently
-	 * drop it on the floor. It's also up to you to handle these securely!
-	 *
-	 * Meta-data: ZT_RemoteTrace structure
-	 */
-	ZT_EVENT_REMOTE_TRACE = 7
+	ZT_EVENT_USER_MESSAGE = 6
 };
 
 /**
- * A root server
- */
-typedef struct {
-	/**
-	 * Name of root
-	 *
-	 * This will be a DNS name for dynamic roots. For static roots
-	 * it will be the ZeroTier address. The presence or absence
-	 * of a dot is used internally as a distinguisher.
-	 */
-	const char *name;
-
-	/**
-	 * Serialized locator
-	 */
-	const void *locator;
-
-	/**
-	 * The size of locator in bytes
-	 */
-	unsigned int locatorSize;
-} ZT_Root;
-
-/**
- * List of root servers
+ * Identity type codes
  */
-typedef struct {
-	/**
-	 * Number of root servers
-	 */
-	unsigned int count;
-
-	/**
-	 * Array of root servers
-	 */
-	ZT_Root roots[];
-} ZT_RootList;
+enum ZT_Identity_Type
+{
+	/* These values must be the same as in Identity.hpp in the core. */
+	ZT_IDENTITY_TYPE_C25519 = 0,
+	ZT_IDENTITY_TYPE_P384 = 1
+};
 
 /**
- * Payload of REMOTE_TRACE event
+ * A ZeroTier identity (opaque)
  */
-typedef struct
-{
-	/**
-	 * ZeroTier address of sender (in least significant 40 bits only)
-	 */
-	uint64_t origin;
-
-	/**
-	 * Null-terminated Dictionary containing key/value pairs sent by origin
-	 *
-	 * This *should* be a dictionary, but the implementation only checks
-	 * that it is a valid non-empty C-style null-terminated string. Be very
-	 * careful to use a well-tested parser to parse this as it represents
-	 * data received from a potentially un-trusted peer on the network.
-	 * Invalid payloads should be dropped.
-	 *
-	 * The contents of data[] may be modified.
-	 */
-	const char *data;
-
-	/**
-	 * Length of dict[] in bytes, INCLUDING terminating null
-	 */
-	unsigned int len;
-} ZT_RemoteTrace;
+typedef void ZT_Identity;
 
 /**
  * User message used with ZT_EVENT_USER_MESSAGE
@@ -624,6 +455,11 @@ typedef struct
 	 */
 	uint64_t address;
 
+	/**
+	 * Actual identity object for this node
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	 * Public identity in string-serialized form (safe to send to others)
 	 *
@@ -1000,39 +836,6 @@ enum ZT_VirtualNetworkConfigOperation
 	ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
 };
 
-/**
- * What trust hierarchy role does this peer have?
- */
-enum ZT_PeerRole
-{
-	ZT_PEER_ROLE_LEAF = 0,       // ordinary node
-	ZT_PEER_ROLE_MOON = 1,       // moon root
-	ZT_PEER_ROLE_PLANET = 2      // planetary root
-};
-
-/**
- * DNS record types for reporting DNS results
- *
- * These integer IDs (other than end of results) are the same as the DNS protocol's
- * internal IDs. Not all of these are used by ZeroTier, and not all DNS record types
- * are listed here. These are just common ones that are used now or may be used in
- * the future for some purpose.
- */
-enum ZT_DNSRecordType
-{
-	ZT_DNS_RECORD__END_OF_RESULTS = 0,
-	ZT_DNS_RECORD_A = 1,
-	ZT_DNS_RECORD_NS = 2,
-	ZT_DNS_RECORD_CNAME = 5,
-	ZT_DNS_RECORD_PTR = 12,
-	ZT_DNS_RECORD_MX = 15,
-	ZT_DNS_RECORD_TXT = 16,
-	ZT_DNS_RECORD_AAAA = 28,
-	ZT_DNS_RECORD_LOC = 29,
-	ZT_DNS_RECORD_SRV = 33,
-	ZT_DNS_RECORD_DNAME = 39
-};
-
 /**
  * Virtual network configuration
  */
@@ -1180,56 +983,6 @@ typedef struct
 	 */
 	uint64_t trustedPathId;
 
-	/**
-	 * One-way latency
-	 */
-	float latency;
-
-	/**
-	 * How much latency varies over time
-	 */
-	float packetDelayVariance;
-
-	/**
-	 * How much observed throughput varies over time
-	 */
-	float throughputDisturbCoeff;
-
-	/**
-	 * Packet Error Ratio (PER)
-	 */
-	float packetErrorRatio;
-
-	/**
-	 * Packet Loss Ratio (PLR)
-	 */
-	float packetLossRatio;
-
-	/**
-	 * Stability of the path
-	 */
-	float stability;
-
-	/**
-	 * Current throughput (moving average)
-	 */
-	uint64_t throughput;
-
-	/**
-	 * Maximum observed throughput for this path
-	 */
-	uint64_t maxThroughput;
-
-	/**
-	 * Percentage of traffic allocated to this path
-	 */
-	float allocation;
-
-	/**
-	 * Name of physical interface (for monitoring)
-	 */
-	char *ifname;
-
 	/**
 	 * Is path alive?
 	 */
@@ -1241,6 +994,15 @@ typedef struct
 	int preferred;
 } ZT_PeerPhysicalPath;
 
+/**
+ * What trust hierarchy role does this peer have?
+ */
+enum ZT_PeerRole
+{
+	ZT_PEER_ROLE_LEAF = 0,       // ordinary node
+	ZT_PEER_ROLE_ROOT = 1        // root server
+};
+
 /**
  * Peer status result buffer
  */
@@ -1251,6 +1013,11 @@ typedef struct
 	 */
 	uint64_t address;
 
+	/**
+	 * Peer identity
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	 * Remote major version or -1 if not known
 	 */
@@ -1281,11 +1048,6 @@ typedef struct
 	 */
 	unsigned int pathCount;
 
-	/**
-	 * Whether this peer was ever reachable via an aggregate link
-	 */
-	int hadAggregateLink;
-
 	/**
 	 * Known network paths to peer
 	 */
@@ -1541,8 +1303,9 @@ typedef int (*ZT_PathCheckFunction)(
  *  (1) Node
  *  (2) User pointer
  *  (3) ZeroTier address (least significant 40 bits)
- *  (4) Desired address family or -1 for any
- *  (5) Buffer to fill with result
+ *  (4) Identity in string form
+ *  (5) Desired address family or -1 for any
+ *  (6) Buffer to fill with result
  *
  * If provided this function will be occasionally called to get physical
  * addresses that might be tried to reach a ZeroTier address. It must
@@ -1554,53 +1317,10 @@ typedef int (*ZT_PathLookupFunction)(
 	void *,                           /* User ptr */
 	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address (40 bits) */
+	const ZT_Identity *,              /* Full identity of node */
 	int,                              /* Desired ss_family or -1 for any */
 	struct sockaddr_storage *);       /* Result buffer */
 
-/**
- * Function to request an asynchronous DNS TXT lookup
- *
- * Parameters:
- *  (1) Node
- *  (2) User pointer
- *  (3) Thread pointer
- *  (4) Array of DNS record types we want
- *  (5) Number of DNS record types in array
- *  (6) DNS name to fetch
- *  (7) DNS request ID to supply to ZT_Node_processDNSResult()
- *
- * DNS is not handled in the core because every platform and runtime
- * typically has its own DNS functions or libraries and these may need
- * to interface with OS or network services in your local environment.
- * Instead this function and its result submission counterpart are
- * provided so you can provide a DNS implementation.
- *
- * If this callback is set in your callback struct to a NULL value,
- * DNS will not be available. The ZeroTier protocol is designed to
- * work in the absence of DNS but you may not get optimal results. For
- * example you may default to root servers that are not geographically
- * optimal or your node may cease to function if a root server's IP
- * changes and there's no way to signal this.
- *
- * This function requests resolution of a DNS record. The result
- * submission method ZT_Node_processDNSResult() must be called at
- * least once in response. See its documentation.
- *
- * Right now ZeroTier only requests resolution of TXT records, but
- * it's possible that this will change in the future.
- *
- * It's safe to call processDNSResult() from within your handler
- * for this function.
- */
-typedef void (*ZT_DNSResolver)(
-	ZT_Node *,                        /* Node */
-	void *,                           /* User ptr */
-	void *,                           /* Thread ptr */
-	const enum ZT_DNSRecordType *,    /* DNS record type(s) to fetch */
-	unsigned int,                     /* Number of DNS record type(s) */
-	const char *,                     /* DNS name to fetch */
-	uintptr_t);                       /* Request ID for returning results */
-
 /****************************************************************************/
 /* C Node API                                                               */
 /****************************************************************************/
@@ -1641,17 +1361,12 @@ struct ZT_Node_Callbacks
 	ZT_EventCallback eventCallback;
 
 	/**
-	 * STRONGLY RECOMMENDED: Function to request a DNS lookup
-	 */
-	ZT_DNSResolver dnsResolver;
-
-	/**
-	 * OPTIONAL: Function to check whether a given physical path should be used
+	 * OPTIONAL: Function to check whether a given physical path should be used for ZeroTier traffic
 	 */
 	ZT_PathCheckFunction pathCheckFunction;
 
 	/**
-	 * OPTIONAL: Function to get hints to physical paths to ZeroTier addresses
+	 * RECOMMENDED: Function to look up paths to ZeroTier nodes
 	 */
 	ZT_PathLookupFunction pathLookupFunction;
 };
@@ -1750,60 +1465,6 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks(
 	int64_t now,
 	volatile int64_t *nextBackgroundTaskDeadline);
 
-/**
- * Submit the result(s) of a requested DNS query
- *
- * This MUST be called at least once after the node requsts DNS resolution.
- * If there are no results or DNS is not implemented or available, just
- * send one ZT_DNS_RECORD__END_OF_RESULTS to signal that no results were
- * obtained.
- *
- * If result is non-NULL but resultLength is zero then result is assumed to
- * be a C string terminated by a zero. Passing an unterminated string with a
- * zero resultLength will result in a crash.
- *
- * The results of A and AAAA records can be returned as either strings or
- * binary IP address bytes (network byte order). If the result is a string,
- * resultLength must be 0 to signal that result is a C string. Otherwise for
- * A resultLength must be 4 and for AAAA it must be 16 if the result is
- * in binary format.
- *
- * The Node implementation makes an effort to ignore obviously invalid
- * submissions like an AAAA record in bianry form with length 25, but this
- * is not guaranteed. It's possible to crash your program by calling this
- * with garbage inputs.
- *
- * Results may be submitted in any order and order should not be assumed
- * to have any meaning.
- *
- * The ZT_DNS_RECORD__END_OF_RESULTS pseudo-response must be sent after all
- * results have been submitted. The result and resultLength paramters are
- * ignored for this type ID.
- *
- * It is safe to call this function from inside the DNS request callback,
- * such as to return a locally cached result or a result from some kind
- * of local database. It's also safe to call this function from threads
- * other than the one that received the DNS request.
- *
- * @param node Node instance that requested DNS resolution
- * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
- * @param dnsRequestID Request ID supplied to DNS request callback
- * @param name DNS name
- * @param recordType Record type of this result
- * @param result Result (content depends on record type)
- * @param resultLength Length of result
- * @param resultIsString If non-zero, IP results for A and AAAA records are being given as C strings not binary IPs
- */
-ZT_SDK_API void ZT_Node_processDNSResult(
-	ZT_Node *node,
-	void *tptr,
-	uintptr_t dnsRequestID,
-	const char *name,
-	enum ZT_DNSRecordType recordType,
-	const void *result,
-	unsigned int resultLength,
-	int resultIsString);
-
 /**
  * Join a network
  *
@@ -1884,36 +1545,25 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tpt
 ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
 
 /**
- * List roots for this node
+ * Add a root server (has no effect if already added)
  *
  * @param node Node instance
- * @param now Current time
- * @return List of roots, use ZT_Node_freeQueryResult to free this when done
+ * @param identity Identity of this root server in string format
+ * @return OK (0) or error code if a fatal error condition has occurred
  */
-ZT_SDK_API ZT_RootList *ZT_Node_listRoots(ZT_Node *node,int64_t now);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,const char *identity);
 
 /**
- * Add or update a root
+ * Remove a root server
  *
- * The node will begin trying to resolve the DNS TXT record for
- * this root and possibly obtain it from other peers.
+ * This removes this node's root designation but does not prevent this node
+ * from communicating with it or close active paths to it.
  *
  * @param node Node instance
- * @param name DNS name or simply the address in hex form for static roots
- * @param locator Binary-serialized locator of NULL if none
- * @param locatorSize Size of locator or 0 if none
- * @return OK (0) or error code
- */
-ZT_SDK_API enum ZT_ResultCode ZT_Node_setRoot(ZT_Node *node,const char *name,const void *locator,unsigned int locatorSize);
-
-/**
- * Remove a dynamic root
- *
- * @param node Node instance
- * @param name DNS name of this dynamic root or the address in hex form for static roots
- * @return OK (0) or error code
+ * @param identity Identity in string format
+ * @return OK (0) or error code if a fatal error condition has occurred
  */
-ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *name);
+ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *identity);
 
 /**
  * Get this node's 40-bit ZeroTier address
@@ -2035,6 +1685,115 @@ ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterIns
  */
 ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 
+/**
+ * Generate a new identity
+ *
+ * Due to a small amount of proof of work this can be a time consuming and CPU
+ * intensive operation. It takes less than a second on most desktop-class systems
+ * but can take longer on e.g. phones.
+ *
+ * @param type Type of identity to generate
+ * @return New identity or NULL on error
+ */
+ZT_SDK_API ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type);
+
+/**
+ * Create a new identity object from a string-serialized identity
+ *
+ * @param idStr Identity in string format
+ * @return Identity object or NULL if the supplied identity string was not valid
+ */
+ZT_SDK_API ZT_Identity *ZT_Identity_fromString(const char *idStr);
+
+/**
+ * Validate this identity
+ *
+ * This can be slightly time consuming due to address derivation (work) checking.
+ *
+ * @return Non-zero if identity is valid
+ */
+ZT_SDK_API int ZT_Identity_validate(const ZT_Identity *id);
+
+/**
+ * Sign a data object with this identity
+ *
+ * The identity must have a private key or this will fail.
+ *
+ * @param id Identity to use to sign
+ * @param data Data to sign
+ * @param len Length of data
+ * @param signature Buffer to store signature
+ * @param signatureBufferLength Length of buffer (must be at least 96 bytes)
+ * @return Length of signature in bytes or 0 on failure.
+ */
+ZT_SDK_API unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength);
+
+/**
+ * Verify a signature
+ *
+ * @param id Identity to use to verify
+ * @param data Data to verify
+ * @param len Length of data
+ * @param signature Signature to check
+ * @param sigLen Length of signature in bytes
+ * @return Non-zero if signature is valid
+ */
+ZT_SDK_API int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen);
+
+/**
+ * Get identity type
+ *
+ * @param id Identity to query
+ * @return Identity type code
+ */
+ZT_SDK_API enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id);
+
+/**
+ * Convert an identity to its string representation
+ *
+ * @param id Identity to convert
+ * @param buf Buffer to store identity (should be at least about 1024 bytes in length)
+ * @param capacity Capacity of buffer
+ * @param includePrivate If true include the private key if present
+ * @return Pointer to buf or NULL on overflow or other error
+ */
+ZT_SDK_API char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate);
+
+/**
+ * Check whether this identity object also holds a private key
+ *
+ * @param id Identity to query
+ * @return Non-zero if a private key is held
+ */
+ZT_SDK_API int ZT_Identity_hasPrivate(const ZT_Identity *id);
+
+/**
+ * Get the ZeroTier address associated with this identity
+ *
+ * @param id Identity to query
+ * @return ZeroTier address (only least significant 40 bits are meaningful, rest will be 0)
+ */
+ZT_SDK_API uint64_t ZT_Identity_address(const ZT_Identity *id);
+
+/**
+ * Compute a hash of this identity's public keys (or both public and private if includePrivate is true)
+ *
+ * @param id Identity to query
+ * @param h Buffer for 384-bit hash
+ * @param includePrivate If true include private keys if any
+ */
+ZT_SDK_API void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate);
+
+/**
+ * Delete an identity and free associated memory
+ *
+ * This should only be used with identities created via Identity_new
+ * and Identity_fromString().
+ *
+ * @param id Identity to delete
+ */
+ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id);
+
 /**
  * Get ZeroTier One version
  *

+ 0 - 168
node/AES-aesni.c

@@ -1,168 +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.
- */
-/****/
-
-/* This is done in plain C because the compiler (at least GCC and CLANG) seem
- * to do a *slightly* better job optimizing this intrinsic code when compiling
- * plain C. C also gives us the register hint keyword, which seems to actually
- * make a small difference. */
-
-#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
-
-#include <stdint.h>
-#include <wmmintrin.h>
-#include <emmintrin.h>
-#include <smmintrin.h>
-
-#define ZT_AES_CTR_AESNI_ROUND(kk) c0 = _mm_aesenc_si128(c0,kk); c1 = _mm_aesenc_si128(c1,kk); c2 = _mm_aesenc_si128(c2,kk); c3 = _mm_aesenc_si128(c3,kk);
-
-void zt_crypt_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out)
-{
-	/* Because our CTR supports full 128-bit nonces, we must do a full 128-bit (big-endian)
-	 * increment to be compatible with canonical NIST-certified CTR implementations. That's
-	 * because it's possible to have a lot of bit saturation in the least significant 64
-	 * bits, which could on rare occasions actually cause a 64-bit wrap. If this happened
-	 * without carry it would result in incompatibility and quietly dropped packets. The
-	 * probability is low, so this would be a one in billions packet loss bug that would
-	 * probably never be found.
-	 *
-	 * This crazy code does a branch-free 128-bit increment by adding a one or a zero to
-	 * the most significant 64 bits of the 128-bit vector based on whether the add we want
-	 * to do to the least significant 64 bits would overflow. This can be computed by
-	 * NOTing those bits and comparing with what we want to add, since NOT is the same
-	 * as subtracting from uint64_max. This generates branch-free ASM on x64 with most
-	 * good compilers. */
-	register __m128i swap128 = _mm_set_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
-	register __m128i ctr0 = _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)iv),swap128);
-	register uint64_t notctr0msq = ~((uint64_t)_mm_extract_epi64(ctr0,0));
-	register __m128i ctr1 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 1ULL),1LL)),swap128);
-	register __m128i ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 2ULL),2LL)),swap128);
-	register __m128i ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 3ULL),3LL)),swap128);
-	ctr0 = _mm_shuffle_epi8(ctr0,swap128);
-
-	register __m128i k0 = key[0];
-	register __m128i k1 = key[1];
-
-	while (len >= 64) {
-		register __m128i ka = key[2];
-		register __m128i c0 = _mm_xor_si128(ctr0,k0);
-		register __m128i c1 = _mm_xor_si128(ctr1,k0);
-		register __m128i c2 = _mm_xor_si128(ctr2,k0);
-		register __m128i c3 = _mm_xor_si128(ctr3,k0);
-		ctr0 = _mm_shuffle_epi8(ctr0,swap128);
-		notctr0msq = ~((uint64_t)_mm_extract_epi64(ctr0,0));
-		ctr1 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 5ULL),5LL)),swap128);
-		ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 6ULL),6LL)),swap128);
-		ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 7ULL),7LL)),swap128);
-		ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 4ULL),4LL)),swap128);
-		register __m128i kb = key[3];
-		ZT_AES_CTR_AESNI_ROUND(k1);
-		register __m128i kc = key[4];
-		ZT_AES_CTR_AESNI_ROUND(ka);
-		register __m128i kd = key[5];
-		ZT_AES_CTR_AESNI_ROUND(kb);
-		ka = key[6];
-		ZT_AES_CTR_AESNI_ROUND(kc);
-		kb = key[7];
-		ZT_AES_CTR_AESNI_ROUND(kd);
-		kc = key[8];
-		ZT_AES_CTR_AESNI_ROUND(ka);
-		kd = key[9];
-		ZT_AES_CTR_AESNI_ROUND(kb);
-		ka = key[10];
-		ZT_AES_CTR_AESNI_ROUND(kc);
-		kb = key[11];
-		ZT_AES_CTR_AESNI_ROUND(kd);
-		kc = key[12];
-		ZT_AES_CTR_AESNI_ROUND(ka);
-		kd = key[13];
-		ZT_AES_CTR_AESNI_ROUND(kb);
-		ka = key[14];
-		ZT_AES_CTR_AESNI_ROUND(kc);
-		ZT_AES_CTR_AESNI_ROUND(kd);
-		_mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka)));
-		_mm_storeu_si128((__m128i *)(out + 16),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 16)),_mm_aesenclast_si128(c1,ka)));
-		_mm_storeu_si128((__m128i *)(out + 32),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 32)),_mm_aesenclast_si128(c2,ka)));
-		_mm_storeu_si128((__m128i *)(out + 48),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 48)),_mm_aesenclast_si128(c3,ka)));
-		in += 64;
-		out += 64;
-		len -= 64;
-	}
-
-	register __m128i k2 = key[2];
-	register __m128i k3 = key[3];
-	register __m128i k4 = key[4];
-	register __m128i k5 = key[5];
-	register __m128i k6 = key[6];
-	register __m128i k7 = key[7];
-
-	while (len >= 16) {
-		register __m128i c0 = _mm_xor_si128(ctr0,k0);
-		ctr0 = _mm_shuffle_epi8(ctr0,swap128);
-		ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)((~((uint64_t)_mm_extract_epi64(ctr0,0))) < 1ULL),1LL)),swap128);
-		c0 = _mm_aesenc_si128(c0,k1);
-		c0 = _mm_aesenc_si128(c0,k2);
-		c0 = _mm_aesenc_si128(c0,k3);
-		c0 = _mm_aesenc_si128(c0,k4);
-		c0 = _mm_aesenc_si128(c0,k5);
-		c0 = _mm_aesenc_si128(c0,k6);
-		register __m128i ka = key[8];
-		c0 = _mm_aesenc_si128(c0,k7);
-		register __m128i kb = key[9];
-		c0 = _mm_aesenc_si128(c0,ka);
-		ka = key[10];
-		c0 = _mm_aesenc_si128(c0,kb);
-		kb = key[11];
-		c0 = _mm_aesenc_si128(c0,ka);
-		ka = key[12];
-		c0 = _mm_aesenc_si128(c0,kb);
-		kb = key[13];
-		c0 = _mm_aesenc_si128(c0,ka);
-		ka = key[14];
-		c0 = _mm_aesenc_si128(c0,kb);
-		_mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka)));
-		in += 16;
-		out += 16;
-		len -= 16;
-	}
-
-	if (len) {
-		register __m128i c0 = _mm_xor_si128(ctr0,k0);
-		k0 = key[8];
-		c0 = _mm_aesenc_si128(c0,k1);
-		c0 = _mm_aesenc_si128(c0,k2);
-		k1 = key[9];
-		c0 = _mm_aesenc_si128(c0,k3);
-		c0 = _mm_aesenc_si128(c0,k4);
-		k2 = key[10];
-		c0 = _mm_aesenc_si128(c0,k5);
-		c0 = _mm_aesenc_si128(c0,k6);
-		k3 = key[11];
-		c0 = _mm_aesenc_si128(c0,k7);
-		c0 = _mm_aesenc_si128(c0,k0);
-		k0 = key[12];
-		c0 = _mm_aesenc_si128(c0,k1);
-		c0 = _mm_aesenc_si128(c0,k2);
-		k1 = key[13];
-		c0 = _mm_aesenc_si128(c0,k3);
-		c0 = _mm_aesenc_si128(c0,k0);
-		k2 = key[14];
-		c0 = _mm_aesenc_si128(c0,k1);
-		c0 = _mm_aesenclast_si128(c0,k2);
-		uint8_t tmp[16];
-		_mm_storeu_si128((__m128i *)tmp,c0);
-		for(unsigned int i=0;i<len;++i)
-			out[i] = in[i] ^ tmp[i];
-	}
-}
-
-#endif

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 29
node/AES.cpp


+ 159 - 59
node/AES.hpp

@@ -18,35 +18,24 @@
 #include "Utils.hpp"
 #include "SHA512.hpp"
 
-#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#include <cstdint>
 
+#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64))
+#include <xmmintrin.h>
 #include <wmmintrin.h>
 #include <emmintrin.h>
 #include <smmintrin.h>
-
 #define ZT_AES_AESNI 1
-
-// AES-aesni.c
-extern "C" void zt_crypt_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out);
-
-#endif // x64
-
-#define ZT_AES_KEY_SIZE 32
-#define ZT_AES_BLOCK_SIZE 16
+#endif
 
 namespace ZeroTier {
 
 /**
- * AES-256 and pals
+ * AES-256 and pals including GMAC, CTR, etc.
  */
 class AES
 {
 public:
-	/**
-	 * This will be true if your platform's type of AES acceleration is supported on this machine
-	 */
-	static const bool HW_ACCEL;
-
 	ZT_ALWAYS_INLINE AES() {}
 	ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); }
 	ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); }
@@ -57,12 +46,11 @@ public:
 	ZT_ALWAYS_INLINE void init(const uint8_t key[32])
 	{
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_init_aesni(key);
 			return;
 		}
 #endif
-
 		_initSW(key);
 	}
 
@@ -75,12 +63,11 @@ public:
 	ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const
 	{
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_encrypt_aesni(in,out);
 			return;
 		}
 #endif
-
 		_encryptSW(in,out);
 	}
 
@@ -95,12 +82,11 @@ public:
 	ZT_ALWAYS_INLINE void gmac(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16]) const
 	{
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_gmac_aesni(iv,(const uint8_t *)in,len,out);
 			return;
 		}
 #endif
-
 		_gmacSW(iv,(const uint8_t *)in,len,out);
 	}
 
@@ -119,41 +105,12 @@ public:
 	ZT_ALWAYS_INLINE void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const
 	{
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
-			zt_crypt_ctr_aesni(_k.ni.k,iv,(const uint8_t *)in,len,(uint8_t *)out);
+		if (likely(Utils::CPUID.aes)) {
+			_ctr_aesni(_k.ni.k,iv,(const uint8_t *)in,len,(uint8_t *)out);
 			return;
 		}
 #endif
-
-		uint64_t ctr[2],cenc[2];
-		memcpy(ctr,iv,16);
-		uint64_t bctr = Utils::ntoh(ctr[1]);
-
-		const uint8_t *i = (const uint8_t *)in;
-		uint8_t *o = (uint8_t *)out;
-
-		while (len >= 16) {
-			_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
-			ctr[1] = Utils::hton(++bctr);
-#ifdef ZT_NO_TYPE_PUNNING
-			for(unsigned int k=0;k<16;++k)
-				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
-#else
-			*((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[0];
-			o += 8;
-			i += 8;
-			*((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[1];
-			o += 8;
-			i += 8;
-#endif
-			len -= 16;
-		}
-
-		if (len) {
-			_encryptSW((const uint8_t *)ctr,(uint8_t *)cenc);
-			for(unsigned int k=0;k<len;++k)
-				*(o++) = *(i++) ^ ((uint8_t *)cenc)[k];
-		}
+		_ctrSW(iv,in,len,out);
 	}
 
 	/**
@@ -187,7 +144,7 @@ public:
 	 * @param out Output buffer to receive ciphertext
 	 * @param tag Output buffer to receive 64-bit authentication tag
 	 */
-	static ZT_ALWAYS_INLINE void gmacSivEncrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,uint8_t tag[8])
+	static inline void gmacSivEncrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,uint8_t tag[8])
 	{
 #ifdef __GNUC__
 		uint8_t __attribute__ ((aligned (16))) miv[12];
@@ -246,7 +203,7 @@ public:
 	 * @param tag Authentication tag supplied with message
 	 * @return True if authentication tags match and message appears authentic
 	 */
-	static ZT_ALWAYS_INLINE bool gmacSivDecrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,const uint8_t tag[8])
+	static inline bool gmacSivDecrypt(const AES &k1,const AES &k2,const AES &k3,const AES &k4,const uint8_t iv[8],const uint8_t pc,const void *in,const unsigned int len,void *out,const uint8_t tag[8])
 	{
 #ifdef __GNUC__
 		uint8_t __attribute__ ((aligned (16))) miv[12];
@@ -307,7 +264,7 @@ public:
 	 * @param k3 CTR IV keyed hash key
 	 * @param k4 AES-CTR key
 	 */
-	static ZT_ALWAYS_INLINE void initGmacCtrKeys(const uint8_t masterKey[32],AES &k1,AES &k2,AES &k3,AES &k4)
+	static inline void initGmacCtrKeys(const uint8_t masterKey[32],AES &k1,AES &k2,AES &k3,AES &k4)
 	{
 		uint8_t k[32];
 		KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K1,0,0,k);
@@ -329,6 +286,7 @@ private:
 
 	void _initSW(const uint8_t key[32]);
 	void _encryptSW(const uint8_t in[16],uint8_t out[16]) const;
+	void _ctrSW(const uint8_t iv[16],const void *in,unsigned int len,void *out) const;
 	void _gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_t out[16]) const;
 
 	/**************************************************************************/
@@ -529,7 +487,7 @@ private:
 		_mm_storeu_si128((__m128i *)out,_mm_aesenclast_si128(tmp,_k.ni.k[14]));
 	}
 
-	static ZT_ALWAYS_INLINE __m128i _mult_block_aesni(__m128i shuf,__m128i h,__m128i y)
+	static ZT_ALWAYS_INLINE inline __m128i _mult_block_aesni(__m128i shuf,__m128i h,__m128i y)
 	{
 		y = _mm_shuffle_epi8(y,shuf);
 		__m128i t1 = _mm_clmulepi64_si128(h,y,0x00);
@@ -569,7 +527,7 @@ private:
 		t4 = _mm_xor_si128(t4,t5);
 		return _mm_shuffle_epi8(t4,shuf);
 	}
-	static ZT_ALWAYS_INLINE __m128i _ghash_aesni(__m128i shuf,__m128i h,__m128i y,__m128i x) { return _mult_block_aesni(shuf,h,_mm_xor_si128(y,x)); }
+	static inline __m128i _ghash_aesni(__m128i shuf,__m128i h,__m128i y,__m128i x) { return _mult_block_aesni(shuf,h,_mm_xor_si128(y,x)); }
 
 	ZT_ALWAYS_INLINE void _gmac_aesni(const uint8_t iv[12],const uint8_t *in,const unsigned int len,uint8_t out[16]) const
 	{
@@ -687,6 +645,148 @@ private:
 		t = _mm_aesenclast_si128(t,_k.ni.k[14]);
 		_mm_storeu_si128((__m128i *)out,_mm_xor_si128(y,t));
 	}
+
+#define ZT_AES_CTR_AESNI_ROUND(kk) c0 = _mm_aesenc_si128(c0,kk); c1 = _mm_aesenc_si128(c1,kk); c2 = _mm_aesenc_si128(c2,kk); c3 = _mm_aesenc_si128(c3,kk);
+
+	static ZT_ALWAYS_INLINE void _ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out)
+	{
+		/* Because our CTR supports full 128-bit nonces, we must do a full 128-bit (big-endian)
+		 * increment to be compatible with canonical NIST-certified CTR implementations. That's
+		 * because it's possible to have a lot of bit saturation in the least significant 64
+		 * bits, which could on rare occasions actually cause a 64-bit wrap. If this happened
+		 * without carry it would result in incompatibility and quietly dropped packets. The
+		 * probability is low, so this would be a one in billions packet loss bug that would
+		 * probably never be found.
+		 *
+		 * This crazy code does a branch-free 128-bit increment by adding a one or a zero to
+		 * the most significant 64 bits of the 128-bit vector based on whether the add we want
+		 * to do to the least significant 64 bits would overflow. This can be computed by
+		 * NOTing those bits and comparing with what we want to add, since NOT is the same
+		 * as subtracting from uint64_max. This generates branch-free ASM on x64 with most
+		 * good compilers. */
+		__m128i swap128 = _mm_set_epi8(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+		__m128i ctr0 = _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)iv),swap128);
+		uint64_t notctr0msq = ~((uint64_t)_mm_extract_epi64(ctr0,0));
+		__m128i ctr1 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 1ULL),1LL)),swap128);
+		__m128i ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 2ULL),2LL)),swap128);
+		__m128i ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 3ULL),3LL)),swap128);
+		ctr0 = _mm_shuffle_epi8(ctr0,swap128);
+
+		__m128i k0 = key[0];
+		__m128i k1 = key[1];
+
+		while (len >= 64) {
+			__m128i ka = key[2];
+			__m128i c0 = _mm_xor_si128(ctr0,k0);
+			__m128i c1 = _mm_xor_si128(ctr1,k0);
+			__m128i c2 = _mm_xor_si128(ctr2,k0);
+			__m128i c3 = _mm_xor_si128(ctr3,k0);
+			ctr0 = _mm_shuffle_epi8(ctr0,swap128);
+			notctr0msq = ~((uint64_t)_mm_extract_epi64(ctr0,0));
+			ctr1 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 5ULL),5LL)),swap128);
+			ctr2 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 6ULL),6LL)),swap128);
+			ctr3 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 7ULL),7LL)),swap128);
+			ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)(notctr0msq < 4ULL),4LL)),swap128);
+			__m128i kb = key[3];
+			ZT_AES_CTR_AESNI_ROUND(k1);
+			__m128i kc = key[4];
+			ZT_AES_CTR_AESNI_ROUND(ka);
+			__m128i kd = key[5];
+			ZT_AES_CTR_AESNI_ROUND(kb);
+			ka = key[6];
+			ZT_AES_CTR_AESNI_ROUND(kc);
+			kb = key[7];
+			ZT_AES_CTR_AESNI_ROUND(kd);
+			kc = key[8];
+			ZT_AES_CTR_AESNI_ROUND(ka);
+			kd = key[9];
+			ZT_AES_CTR_AESNI_ROUND(kb);
+			ka = key[10];
+			ZT_AES_CTR_AESNI_ROUND(kc);
+			kb = key[11];
+			ZT_AES_CTR_AESNI_ROUND(kd);
+			kc = key[12];
+			ZT_AES_CTR_AESNI_ROUND(ka);
+			kd = key[13];
+			ZT_AES_CTR_AESNI_ROUND(kb);
+			ka = key[14];
+			ZT_AES_CTR_AESNI_ROUND(kc);
+			ZT_AES_CTR_AESNI_ROUND(kd);
+			_mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka)));
+			_mm_storeu_si128((__m128i *)(out + 16),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 16)),_mm_aesenclast_si128(c1,ka)));
+			_mm_storeu_si128((__m128i *)(out + 32),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 32)),_mm_aesenclast_si128(c2,ka)));
+			_mm_storeu_si128((__m128i *)(out + 48),_mm_xor_si128(_mm_loadu_si128((const __m128i *)(in + 48)),_mm_aesenclast_si128(c3,ka)));
+			in += 64;
+			out += 64;
+			len -= 64;
+		}
+
+		__m128i k2 = key[2];
+		__m128i k3 = key[3];
+		__m128i k4 = key[4];
+		__m128i k5 = key[5];
+		__m128i k6 = key[6];
+		__m128i k7 = key[7];
+
+		while (len >= 16) {
+			__m128i c0 = _mm_xor_si128(ctr0,k0);
+			ctr0 = _mm_shuffle_epi8(ctr0,swap128);
+			ctr0 = _mm_shuffle_epi8(_mm_add_epi64(ctr0,_mm_set_epi64x((long long)((~((uint64_t)_mm_extract_epi64(ctr0,0))) < 1ULL),1LL)),swap128);
+			c0 = _mm_aesenc_si128(c0,k1);
+			c0 = _mm_aesenc_si128(c0,k2);
+			c0 = _mm_aesenc_si128(c0,k3);
+			c0 = _mm_aesenc_si128(c0,k4);
+			c0 = _mm_aesenc_si128(c0,k5);
+			c0 = _mm_aesenc_si128(c0,k6);
+			__m128i ka = key[8];
+			c0 = _mm_aesenc_si128(c0,k7);
+			__m128i kb = key[9];
+			c0 = _mm_aesenc_si128(c0,ka);
+			ka = key[10];
+			c0 = _mm_aesenc_si128(c0,kb);
+			kb = key[11];
+			c0 = _mm_aesenc_si128(c0,ka);
+			ka = key[12];
+			c0 = _mm_aesenc_si128(c0,kb);
+			kb = key[13];
+			c0 = _mm_aesenc_si128(c0,ka);
+			ka = key[14];
+			c0 = _mm_aesenc_si128(c0,kb);
+			_mm_storeu_si128((__m128i *)out,_mm_xor_si128(_mm_loadu_si128((const __m128i *)in),_mm_aesenclast_si128(c0,ka)));
+			in += 16;
+			out += 16;
+			len -= 16;
+		}
+
+		if (len) {
+			__m128i c0 = _mm_xor_si128(ctr0,k0);
+			k0 = key[8];
+			c0 = _mm_aesenc_si128(c0,k1);
+			c0 = _mm_aesenc_si128(c0,k2);
+			k1 = key[9];
+			c0 = _mm_aesenc_si128(c0,k3);
+			c0 = _mm_aesenc_si128(c0,k4);
+			k2 = key[10];
+			c0 = _mm_aesenc_si128(c0,k5);
+			c0 = _mm_aesenc_si128(c0,k6);
+			k3 = key[11];
+			c0 = _mm_aesenc_si128(c0,k7);
+			c0 = _mm_aesenc_si128(c0,k0);
+			k0 = key[12];
+			c0 = _mm_aesenc_si128(c0,k1);
+			c0 = _mm_aesenc_si128(c0,k2);
+			k1 = key[13];
+			c0 = _mm_aesenc_si128(c0,k3);
+			c0 = _mm_aesenc_si128(c0,k0);
+			k2 = key[14];
+			c0 = _mm_aesenc_si128(c0,k1);
+			c0 = _mm_aesenclast_si128(c0,k2);
+			uint8_t tmp[16];
+			_mm_storeu_si128((__m128i *)tmp,c0);
+			for(unsigned int i=0;i<len;++i)
+				out[i] = in[i] ^ tmp[i];
+		}
+	}
 #endif /* ZT_AES_AESNI ******************************************************/
 };
 

+ 98 - 17
node/Address.hpp

@@ -14,12 +14,15 @@
 #ifndef ZT_ADDRESS_HPP
 #define ZT_ADDRESS_HPP
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
+#include <cmath>
 
 #include <string>
+#include <vector>
+#include <algorithm>
 
 #include "Constants.hpp"
 #include "Utils.hpp"
@@ -34,8 +37,7 @@ class Address
 {
 public:
 	ZT_ALWAYS_INLINE Address() : _a(0) {}
-	ZT_ALWAYS_INLINE Address(const Address &a) : _a(a._a) {}
-	ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
+	explicit ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {}
 
 	/**
 	 * @param bits Raw address -- 5 bytes, big-endian byte order
@@ -43,7 +45,6 @@ public:
 	 */
 	ZT_ALWAYS_INLINE Address(const void *bits,unsigned int len) { setTo(bits,len); }
 
-	ZT_ALWAYS_INLINE Address &operator=(const Address &a) { _a = a._a; return *this; }
 	ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; }
 
 	/**
@@ -105,18 +106,13 @@ public:
 	/**
 	 * @return Hash code for use with Hashtable
 	 */
-	ZT_ALWAYS_INLINE unsigned long hashCode() const { return reinterpret_cast<unsigned long>(_a); }
+	ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_a; }
 
 	/**
 	 * @return Hexadecimal string
 	 */
 	ZT_ALWAYS_INLINE char *toString(char buf[11]) const { return Utils::hex10(_a,buf); }
 
-	/**
-	 * @return True if this address is not zero
-	 */
-	ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); }
-
 	/**
 	 * Check if this address is reserved
 	 *
@@ -126,7 +122,7 @@ public:
 	 *
 	 * @return True if address is reserved and may not be used
 	 */
-	ZT_ALWAYS_INLINE bool isReserved() const { return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); }
+	ZT_ALWAYS_INLINE bool isReserved() const { return ((!_a)||((_a >> 32U) == ZT_ADDRESS_RESERVED_PREFIX)); }
 
 	/**
 	 * @param i Value from 0 to 4 (inclusive)
@@ -134,9 +130,10 @@ public:
 	 */
 	ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); }
 
-	ZT_ALWAYS_INLINE operator unsigned int() const { return reinterpret_cast<unsigned int>(_a); }
-	ZT_ALWAYS_INLINE operator unsigned long() const { return reinterpret_cast<unsigned long>(_a); }
-	ZT_ALWAYS_INLINE operator unsigned long long() const { return reinterpret_cast<unsigned long long>(_a); }
+	ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); }
+	ZT_ALWAYS_INLINE operator unsigned int() const { return (unsigned int)_a; }
+	ZT_ALWAYS_INLINE operator unsigned long() const { return (unsigned long)_a; }
+	ZT_ALWAYS_INLINE operator unsigned long long() const { return (unsigned long long)_a; }
 
 	ZT_ALWAYS_INLINE void zero() { _a = 0; }
 
@@ -154,6 +151,90 @@ public:
 	ZT_ALWAYS_INLINE bool operator>=(const Address &a) const { return (_a >= a._a); }
 	ZT_ALWAYS_INLINE bool operator<=(const Address &a) const { return (_a <= a._a); }
 
+#if 0
+	/**
+	 * Create a list of the first N bits of a list of unique addresses with N as the minimum unique size
+	 *
+	 * The list is stored in a space-efficient packed bit format.
+	 *
+	 * @param start Starting Address iterator/pointer
+	 * @param end Ending Address iterator/pointer
+	 * @param list Pointer to location to write list
+	 * @param listCapacityBytes Number of bytes available for list
+	 * @return Number of bytes written or -1 on overflow or other error
+	 * @tparam I Input iterator type
+	 */
+	template<typename I>
+	static inline int createMinPrefixList(I start,I end,uint8_t *list,const int listCapacityBytes)
+	{
+		std::vector<Address> sortedAddrs(start,end);
+		if (sortedAddrs.empty())
+			return 0;
+		if (listCapacityBytes == 0)
+			return -1;
+		std::sort(sortedAddrs.begin(),sortedAddrs.end());
+
+		unsigned int bits = (unsigned int)fmaxf(log2f((float)(sortedAddrs.size() * 2)),3.0F);
+		uint64_t mask;
+try_additional_bits: {
+			mask = 0xffffffffffffffffULL >> (64 - bits);
+			std::vector<Address>::iterator a(sortedAddrs.begin());
+			uint64_t aa = *(a++) & mask;
+			aa |= (uint64_t)(aa == 0);
+			uint64_t lastMaskedAddress = aa;
+			while (a != sortedAddrs.end()) {
+				aa = *(a++) & mask;
+				aa |= (uint64_t)(aa == 0);
+				if (aa == lastMaskedAddress) {
+					++bits;
+					goto try_additional_bits;
+				}
+				lastMaskedAddress = aa;
+			}
+		}
+
+		int l = 0;
+		unsigned int bitPtr = 0;
+		for(I a(start);a!=end;) {
+			uint64_t aa = *(a++) & mask;
+			aa |= (uint64_t)(aa == 0);
+			unsigned int br = bits;
+			if (bitPtr > 0) {
+				unsigned int w = 8 - bitPtr;
+				if (w > br) w = br;
+				list[l] = (list[l] << w) | (((uint8_t)aa) & (0xff >> (8 - w)));
+				bitPtr += w;
+				if (bitPtr == 8) {
+					bitPtr = 0;
+					if (l >= listCapacityBytes)
+						return -1;
+					++l;
+				}
+				aa >>= w;
+				br -= w;
+			}
+			while (br >= 8) {
+				if (l >= listCapacityBytes)
+					return -1;
+				list[l++] = (uint8_t)aa;
+				br -= 8;
+				aa >>= 8;
+			}
+			if (br > 0) {
+				list[l] = (uint8_t)aa;
+				bitPtr = br;
+			}
+		}
+		if (bitPtr > 0) {
+			if (l >= listCapacityBytes)
+				return -1;
+			++l;
+		}
+
+		return l;
+	}
+#endif
+
 private:
 	uint64_t _a;
 };

+ 10 - 5
node/AtomicCounter.hpp

@@ -24,25 +24,30 @@ namespace ZeroTier {
 
 /**
  * Simple atomic counter supporting increment and decrement
+ *
+ * This is used as the reference counter in reference counted objects that
+ * work with SharedPtr<>.
  */
 class AtomicCounter
 {
 public:
-	ZT_ALWAYS_INLINE AtomicCounter() { _v = 0; }
+	ZT_ALWAYS_INLINE AtomicCounter() : _v(0) {}
 
 	ZT_ALWAYS_INLINE int load() const
 	{
 #ifdef __GNUC__
-		return __sync_or_and_fetch(const_cast<int *>(&_v),0);
+		return _v;
 #else
 		return _v.load();
 #endif
 	}
 
+	ZT_ALWAYS_INLINE void zero() { _v = 0; }
+
 	ZT_ALWAYS_INLINE int operator++()
 	{
 #ifdef __GNUC__
-		return __sync_add_and_fetch(&_v,1);
+		return __sync_add_and_fetch((int *)&_v,1);
 #else
 		return ++_v;
 #endif
@@ -51,7 +56,7 @@ public:
 	ZT_ALWAYS_INLINE int operator--()
 	{
 #ifdef __GNUC__
-		return __sync_sub_and_fetch(&_v,1);
+		return __sync_sub_and_fetch((int *)&_v,1);
 #else
 		return --_v;
 #endif
@@ -62,7 +67,7 @@ private:
 	ZT_ALWAYS_INLINE const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
 
 #ifdef __GNUC__
-	int _v;
+	volatile int _v;
 #else
 	std::atomic_int _v;
 #endif

+ 116 - 0
node/Buf.cpp

@@ -0,0 +1,116 @@
+/*
+ * 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 "Buf.hpp"
+
+#ifndef __GNUC__
+#include <atomic>
+#endif
+
+namespace ZeroTier {
+
+#ifdef __GNUC__
+static uintptr_t s_pool = 0;
+#else
+static std::atomic<uintptr_t> s_pool(0);
+#endif
+
+void Buf::operator delete(void *ptr,std::size_t sz)
+{
+	if (ptr) {
+		uintptr_t bb;
+		const uintptr_t locked = ~((uintptr_t)0);
+		for (;;) {
+#ifdef __GNUC__
+			bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's
+#else
+			bb = s_pool.fetch_or(locked);
+#endif
+			if (bb != locked)
+				break;
+		}
+
+		((Buf *)ptr)->__nextInPool = bb;
+#ifdef __GNUC__
+		__sync_fetch_and_and(&s_pool,(uintptr_t)ptr);
+#else
+		s_pool.store((uintptr_t)ptr);
+#endif
+	}
+}
+
+SharedPtr<Buf> Buf::get()
+{
+	uintptr_t bb;
+	const uintptr_t locked = ~((uintptr_t)0);
+	for (;;) {
+#ifdef __GNUC__
+		bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's
+#else
+		bb = s_pool.fetch_or(locked);
+#endif
+		if (bb != locked)
+			break;
+	}
+
+	Buf *b;
+	if (bb == 0) {
+#ifdef __GNUC__
+		__sync_fetch_and_and(&s_pool,bb);
+#else
+		s_pool.store(bb);
+#endif
+		b = (Buf *)malloc(sizeof(Buf));
+		if (!b)
+			return SharedPtr<Buf>();
+	} else {
+		b = (Buf *)bb;
+#ifdef __GNUC__
+		__sync_fetch_and_and(&s_pool,b->__nextInPool);
+#else
+		s_pool.store(b->__nextInPool);
+#endif
+	}
+
+	b->__refCount.zero();
+	return SharedPtr<Buf>(b);
+}
+
+void Buf::freePool()
+{
+	uintptr_t bb;
+	const uintptr_t locked = ~((uintptr_t)0);
+	for (;;) {
+#ifdef __GNUC__
+		bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's
+#else
+		bb = s_pool.fetch_or(locked);
+#endif
+		if (bb != locked)
+			break;
+	}
+
+#ifdef __GNUC__
+	__sync_fetch_and_and(&s_pool,(uintptr_t)0);
+#else
+	s_pool.store((uintptr_t)0);
+#endif
+
+	while (bb != 0) {
+		uintptr_t next = ((Buf *)bb)->__nextInPool;
+		free((void *)bb);
+		bb = next;
+	}
+}
+
+} // namespace ZeroTier

+ 472 - 0
node/Buf.hpp

@@ -0,0 +1,472 @@
+/*
+ * 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_BUF_HPP
+#define ZT_BUF_HPP
+
+#include "Constants.hpp"
+#include "AtomicCounter.hpp"
+#include "Utils.hpp"
+#include "SharedPtr.hpp"
+#include "Mutex.hpp"
+
+#include <cstdint>
+#include <cstring>
+#include <cstdlib>
+
+// Buffers are 16384 bytes in size because this is the smallest size that can hold any packet
+// and is a power of two. It needs to be a power of two because masking is significantly faster
+// than integer division modulus.
+#define ZT_BUF_MEM_SIZE 0x00004000
+#define ZT_BUF_MEM_MASK 0x00003fffU
+
+namespace ZeroTier {
+
+/**
+ * Buffer and methods for branch-free bounds-checked data assembly and parsing
+ *
+ * This implements an extremely fast buffer for packet assembly and parsing that avoids
+ * branching whenever possible. To be safe it must be used correctly!
+ *
+ * The read methods are prefixed by 'r', and write methods with 'w'. All methods take
+ * an iterator, which is just an int that should be initialized to 0 (or whatever starting
+ * position is desired). All read methods will advance the iterator regardless of outcome.
+ *
+ * Read and write methods fail silently in the event of overflow. They do not corrupt or
+ * access memory outside the bounds of Buf, but will otherwise produce undefined results.
+ *
+ * IT IS THE RESPONSIBILITY OF THE USER of this class to use the readOverflow() and
+ * writeOverflow() static methods to check the iterator for overflow after each series
+ * of reads and writes and BEFORE ANY PARSING or other decisions are made on the basis
+ * of the data obtained from a buffer. Failure to do so can result in bugs due
+ * to parsing and branching on undefined or corrupt data.
+ *
+ * ^^ THIS IS VERY IMPORTANT ^^
+ *
+ * A typical packet assembly consists of repeated calls to the write methods followed by
+ * a check to writeOverflow() before final packet armoring and transport. A typical packet
+ * disassembly and parsing consists of a series of read calls to obtain the packet's
+ * fields followed by a call to readOverflow() to check that these fields are valid. The
+ * packet is discarded if readOverflow() returns true. Some packet parsers may make
+ * additional reads and in this case readOverflow() must be checked after each set of
+ * reads to ensure that overflow did not occur.
+ *
+ * Buf uses a lock-free pool for extremely fast allocation and deallocation.
+ */
+class Buf
+{
+	friend class SharedPtr<Buf>;
+
+private:
+	// Direct construction isn't allowed; use get().
+	ZT_ALWAYS_INLINE Buf()
+	{}
+
+	ZT_ALWAYS_INLINE Buf(const Buf &b)
+	{}
+
+public:
+	static void operator delete(void *ptr,std::size_t sz);
+
+	/**
+	 * Get obtains a buffer from the pool or allocates a new buffer if the pool is empty
+	 *
+	 * @return Buffer
+	 */
+	static SharedPtr<Buf> get();
+
+	/**
+	 * Free buffers in the pool
+	 *
+	 * New buffers will be created and the pool repopulated if get() is called
+	 * and outstanding buffers will still be returned to the pool. This just
+	 * frees buffers currently held in reserve.
+	 */
+	static void freePool();
+
+	/**
+	 * Check for overflow beyond the size of the buffer
+	 *
+	 * This is used to check for overflow when writing. It returns true if the iterator
+	 * has passed beyond the capacity of the buffer.
+	 *
+	 * @param ii Iterator to check
+	 * @return True if iterator has read past the size of the buffer
+	 */
+	static ZT_ALWAYS_INLINE bool writeOverflow(const int &ii)
+	{ return ((ii - ZT_BUF_MEM_SIZE) > 0); }
+
+	/**
+	 * Check for overflow beyond the size of the data that should be in the buffer
+	 *
+	 * This is used to check for overflow when reading, with the second argument being the
+	 * size of the meaningful data actually present in the buffer.
+	 *
+	 * @param ii Iterator to check
+	 * @param size Size of data that should be in buffer
+	 * @return True if iterator has read past the size of the data
+	 */
+	static ZT_ALWAYS_INLINE bool readOverflow(const int &ii,const unsigned int size)
+	{ return ((ii - (int)size) > 0); }
+
+	////////////////////////////////////////////////////////////////////////////
+	// Read methods
+	////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Read a byte
+	 *
+	 * @param ii Iterator
+	 * @return Byte (undefined on overflow)
+	 */
+	ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const
+	{
+		const unsigned int s = (unsigned int)ii++;
+		return data[s & ZT_BUF_MEM_MASK];
+	}
+
+	/**
+	 * Read a 16-bit integer
+	 *
+	 * @param ii Integer
+	 * @return Integer (undefined on overflow)
+	 */
+	ZT_ALWAYS_INLINE uint16_t rI16(int &ii) const
+	{
+		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
+		ii += 2;
+#ifdef ZT_NO_TYPE_PUNNING
+		return (
+			((uint16_t)data[s] << 8U) |
+			(uint16_t)data[s + 1]);
+#else
+		return Utils::ntoh(*reinterpret_cast<const uint16_t *>(data + s));
+#endif
+	}
+
+	/**
+	 * Read a 32-bit integer
+	 *
+	 * @param ii Integer
+	 * @return Integer (undefined on overflow)
+	 */
+	ZT_ALWAYS_INLINE uint32_t rI32(int &ii) const
+	{
+		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
+		ii += 4;
+#ifdef ZT_NO_TYPE_PUNNING
+		return (
+			((uint32_t)data[s] << 24U) |
+			((uint32_t)data[s + 1] << 16U) |
+			((uint32_t)data[s + 2] << 8U) |
+			(uint32_t)data[s + 3]);
+#else
+		return Utils::ntoh(*reinterpret_cast<const uint32_t *>(data + s));
+#endif
+	}
+
+	/**
+	 * Read a 64-bit integer
+	 *
+	 * @param ii Integer
+	 * @return Integer (undefined on overflow)
+	 */
+	ZT_ALWAYS_INLINE uint64_t rI64(int &ii) const
+	{
+		const unsigned int s = (unsigned int)ii & ZT_BUF_MEM_MASK;
+		ii += 8;
+#ifdef ZT_NO_TYPE_PUNNING
+		return (
+			((uint64_t)data[s] << 56U) |
+			((uint64_t)data[s + 1] << 48U) |
+			((uint64_t)data[s + 2] << 40U) |
+			((uint64_t)data[s + 3] << 32U) |
+			((uint64_t)data[s + 4] << 24U) |
+			((uint64_t)data[s + 5] << 16U) |
+			((uint64_t)data[s + 6] << 8U) |
+			(uint64_t)data[s + 7]);
+#else
+		return Utils::ntoh(*reinterpret_cast<const uint64_t *>(data + s));
+#endif
+	}
+
+	/**
+	 * Read an object supporting the marshal/unmarshal interface
+	 *
+	 * If the return value is negative the object's state is undefined. A return value of
+	 * zero typically also indicates a problem, though this may depend on the object type.
+	 *
+	 * Since objects may be invalid even if there is no overflow, it's important to check
+	 * the return value of this function in all cases and discard invalid packets as it
+	 * indicates.
+	 *
+	 * @tparam T Object type
+	 * @param ii Iterator
+	 * @param obj Object to read
+	 * @return Bytes read or a negative value on unmarshal error (passed from object) or overflow
+	 */
+	template<typename T>
+	ZT_ALWAYS_INLINE int rO(int &ii,T &obj) const
+	{
+		if (ii < ZT_BUF_MEM_SIZE) {
+			int ms = obj.unmarshal(data + ii,ZT_BUF_MEM_SIZE - ii);
+			if (ms > 0)
+				ii += ms;
+			return ms;
+		}
+		return -1;
+	}
+
+	/**
+	 * Read a C-style string from the buffer, making a copy and advancing the iterator
+	 *
+	 * Use this if the buffer's memory may get changed between reading and processing
+	 * what is read.
+	 *
+	 * @param ii Iterator
+	 * @param buf Buffer to receive string
+	 * @param bufSize Capacity of buffer in bytes
+	 * @return Pointer to buf or NULL on overflow or error
+	 */
+	ZT_ALWAYS_INLINE char *rS(int &ii,char *const buf,const unsigned int bufSize) const
+	{
+		const char *const s = (const char *)(data + ii);
+		const int sii = ii;
+		while (ii < ZT_BUF_MEM_SIZE) {
+			if (data[ii++] == 0) {
+				memcpy(buf,s,ii - sii);
+				return buf;
+			}
+		}
+		return nullptr;
+	}
+
+	/**
+	 * Obtain a pointer to a C-style string in the buffer without copying and advance the iterator
+	 *
+	 * The iterator is advanced even if this fails and returns NULL so that readOverflow()
+	 * will indicate that an overflow occurred. As with other reads the string's contents are
+	 * undefined if readOverflow() returns true.
+	 *
+	 * This version avoids a copy and so is faster if the buffer won't be modified between
+	 * reading and processing.
+	 *
+	 * @param ii Iterator
+	 * @return Pointer to null-terminated C-style string or NULL on overflow or error
+	 */
+	ZT_ALWAYS_INLINE const char *rSnc(int &ii) const
+	{
+		const char *const s = (const char *)(data + ii);
+		while (ii < ZT_BUF_MEM_SIZE) {
+			if (data[ii++] == 0)
+				return s;
+		}
+		return nullptr;
+	}
+
+	/**
+	 * Read a byte array from the buffer, making a copy and advancing the iterator
+	 *
+	 * Use this if the buffer's memory may get changed between reading and processing
+	 * what is read.
+	 *
+	 * @param ii Iterator
+	 * @param bytes Buffer to contain data to read
+	 * @param len Length of buffer
+	 * @return Pointer to data or NULL on overflow or error
+	 */
+	ZT_ALWAYS_INLINE void *rB(int &ii,void *bytes,unsigned int len) const
+	{
+		const void *const b = (const void *)(data + ii);
+		if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) {
+			memcpy(bytes,b,len);
+			return bytes;
+		}
+		return nullptr;
+	}
+
+	/**
+	 * Obtain a pointer to a field in the buffer without copying and advance the iterator
+	 *
+	 * The iterator is advanced even if this fails and returns NULL so that readOverflow()
+	 * will indicate that an overflow occurred.
+	 *
+	 * This version avoids a copy and so is faster if the buffer won't be modified between
+	 * reading and processing.
+	 *
+	 * @param ii Iterator
+	 * @param len Length of data field to obtain a pointer to
+	 * @return Pointer to field or NULL on overflow
+	 */
+	ZT_ALWAYS_INLINE const void *rBnc(int &ii,unsigned int len) const
+	{
+		const void *const b = (const void *)(data + ii);
+		return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr;
+	}
+
+	////////////////////////////////////////////////////////////////////////////
+	// Write methods
+	////////////////////////////////////////////////////////////////////////////
+
+	/**
+	 * Write a byte
+	 *
+	 * @param ii Iterator
+	 * @param n Byte
+	 */
+	ZT_ALWAYS_INLINE void wI(int &ii,uint8_t n)
+	{
+		const unsigned int s = (unsigned int)ii++;
+		data[s & ZT_BUF_MEM_MASK] = n;
+	}
+
+	/**
+	 * Write a 16-bit integer in big-endian byte order
+	 *
+	 * @param ii Iterator
+	 * @param n Integer
+	 */
+	ZT_ALWAYS_INLINE void wI(int &ii,uint16_t n)
+	{
+		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
+		ii += 2;
+#ifdef ZT_NO_TYPE_PUNNING
+		data[s] = (uint8_t)(n >> 8U);
+		data[s + 1] = (uint8_t)n;
+#else
+		*reinterpret_cast<uint16_t *>(data + s) = Utils::hton(n);
+#endif
+	}
+
+	/**
+	 * Write a 32-bit integer in big-endian byte order
+	 *
+	 * @param ii Iterator
+	 * @param n Integer
+	 */
+	ZT_ALWAYS_INLINE void wI(int &ii,uint32_t n)
+	{
+		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
+		ii += 4;
+#ifdef ZT_NO_TYPE_PUNNING
+		data[s] = (uint8_t)(n >> 24U);
+		data[s + 1] = (uint8_t)(n >> 16U);
+		data[s + 2] = (uint8_t)(n >> 8U);
+		data[s + 3] = (uint8_t)n;
+#else
+		*reinterpret_cast<uint32_t *>(data + s) = Utils::hton(n);
+#endif
+	}
+
+	/**
+	 * Write a 64-bit integer in big-endian byte order
+	 *
+	 * @param ii Iterator
+	 * @param n Integer
+	 */
+	ZT_ALWAYS_INLINE void wI(int &ii,uint64_t n)
+	{
+		const unsigned int s = ((unsigned int)ii) & ZT_BUF_MEM_MASK;
+		ii += 8;
+#ifdef ZT_NO_TYPE_PUNNING
+		data[s] = (uint8_t)(n >> 56U);
+		data[s + 1] = (uint8_t)(n >> 48U);
+		data[s + 2] = (uint8_t)(n >> 40U);
+		data[s + 3] = (uint8_t)(n >> 32U);
+		data[s + 4] = (uint8_t)(n >> 24U);
+		data[s + 5] = (uint8_t)(n >> 16U);
+		data[s + 6] = (uint8_t)(n >> 8U);
+		data[s + 7] = (uint8_t)n;
+#else
+		*reinterpret_cast<uint64_t *>(data + s) = Utils::hton(n);
+#endif
+	}
+
+	/**
+	 * Write an object implementing the marshal interface
+	 *
+	 * @tparam T Object type
+	 * @param ii Iterator
+	 * @param t Object to write
+	 */
+	template<typename T>
+	ZT_ALWAYS_INLINE void wO(int &ii,T &t)
+	{
+		const unsigned int s = (unsigned int)ii;
+		if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) {
+			int ms = t.marshal(data + s);
+			if (ms > 0)
+				ii += ms;
+		} else {
+			ii += T::marshalSizeMax(); // mark as overflowed even if we didn't do anything
+		}
+	}
+
+	/**
+	 * Write a C-style null-terminated string (including the trailing zero)
+	 *
+	 * @param ii Iterator
+	 * @param s String to write (writes an empty string if this is NULL)
+	 */
+	ZT_ALWAYS_INLINE void wS(int &ii,const char *s)
+	{
+		if (s) {
+			char c;
+			do {
+				c = *(s++);
+				wI(ii,(uint8_t)c);
+			} while (c);
+		} else {
+			wI(ii,(uint8_t)0);
+		}
+	}
+
+	/**
+	 * Write a byte array
+	 *
+	 * @param ii Iterator
+	 * @param bytes Bytes to write
+	 * @param len Size of data in bytes
+	 */
+	ZT_ALWAYS_INLINE void wB(int &ii,const void *const bytes,const unsigned int len)
+	{
+		unsigned int s = (unsigned int)ii;
+		if ((ii += (int)len) <= ZT_BUF_MEM_SIZE)
+			memcpy(data + s,bytes,len);
+	}
+
+	////////////////////////////////////////////////////////////////////////////
+
+	ZT_ALWAYS_INLINE Buf &operator=(const Buf &b)
+	{
+		if (&b != this)
+			memcpy(data,b.data,ZT_BUF_MEM_SIZE);
+		return *this;
+	}
+
+	/**
+	 * Raw buffer
+	 *
+	 * The extra eight bytes permit silent overflow of integer types without reading or writing
+	 * beyond Buf's memory and without branching or extra masks. They can be ignored otherwise.
+	 */
+	uint8_t data[ZT_BUF_MEM_SIZE + 8];
+
+private:
+	volatile uintptr_t __nextInPool;
+	AtomicCounter __refCount;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 46 - 54
node/C25519.cpp

@@ -7,16 +7,11 @@ Derived from public domain code by D. J. Bernstein.
 // Modified very slightly for ZeroTier One by Adam Ierymenko
 // This code remains in the public domain.
 
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
 
-#include "Constants.hpp"
 #include "C25519.hpp"
 #include "SHA512.hpp"
-#include "Buffer.hpp"
-#include "Hashtable.hpp"
-#include "Mutex.hpp"
 
 #ifdef __WINDOWS__
 #pragma warning(disable: 4146)
@@ -28,10 +23,7 @@ Derived from public domain code by D. J. Bernstein.
 
 namespace {
 
-#define crypto_int32 int32_t
 #define crypto_uint32 uint32_t
-#define crypto_int64 int64_t
-#define crypto_uint64 uint64_t
 #define crypto_hash_sha512_BYTES 64
 
 //////////////////////////////////////////////////////////////////////////////
@@ -41,7 +33,7 @@ typedef uint8_t u8;
 typedef int32_t s32;
 typedef int64_t limb;
 
-static ZT_ALWAYS_INLINE void fsum(limb *output, const limb *in) {
+static inline void fsum(limb *output, const limb *in) {
   unsigned i;
   for (i = 0; i < 10; i += 2) {
     output[0+i] = output[0+i] + in[0+i];
@@ -49,21 +41,21 @@ static ZT_ALWAYS_INLINE void fsum(limb *output, const limb *in) {
   }
 }
 
-static ZT_ALWAYS_INLINE void fdifference(limb *output, const limb *in) {
+static inline void fdifference(limb *output, const limb *in) {
   unsigned i;
   for (i = 0; i < 10; ++i) {
     output[i] = in[i] - output[i];
   }
 }
 
-static ZT_ALWAYS_INLINE void fscalar_product(limb *output, const limb *in, const limb scalar) {
+static inline void fscalar_product(limb *output, const limb *in, const limb scalar) {
   unsigned i;
   for (i = 0; i < 10; ++i) {
     output[i] = in[i] * scalar;
   }
 }
 
-static ZT_ALWAYS_INLINE void fproduct(limb *output, const limb *in2, const limb *in) {
+static inline void fproduct(limb *output, const limb *in2, const limb *in) {
   output[0] =       ((limb) ((s32) in2[0])) * ((s32) in[0]);
   output[1] =       ((limb) ((s32) in2[0])) * ((s32) in[1]) +
                     ((limb) ((s32) in2[1])) * ((s32) in[0]);
@@ -166,7 +158,7 @@ static ZT_ALWAYS_INLINE void fproduct(limb *output, const limb *in2, const limb
   output[18] = 2 *  ((limb) ((s32) in2[9])) * ((s32) in[9]);
 }
 
-static ZT_ALWAYS_INLINE void freduce_degree(limb *output) {
+static inline void freduce_degree(limb *output) {
   output[8] += output[18] << 4;
   output[8] += output[18] << 1;
   output[8] += output[18];
@@ -200,7 +192,7 @@ static ZT_ALWAYS_INLINE void freduce_degree(limb *output) {
 #error "This code only works on a two's complement system"
 #endif
 
-static ZT_ALWAYS_INLINE limb div_by_2_26(const limb v)
+static inline limb div_by_2_26(const limb v)
 {
   /* High word of v; no shift needed. */
   const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
@@ -212,7 +204,7 @@ static ZT_ALWAYS_INLINE limb div_by_2_26(const limb v)
   return (v + roundoff) >> 26;
 }
 
-static ZT_ALWAYS_INLINE limb div_by_2_25(const limb v)
+static inline limb div_by_2_25(const limb v)
 {
   /* High word of v; no shift needed*/
   const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
@@ -224,7 +216,7 @@ static ZT_ALWAYS_INLINE limb div_by_2_25(const limb v)
   return (v + roundoff) >> 25;
 }
 
-static ZT_ALWAYS_INLINE void freduce_coefficients(limb *output) {
+static inline void freduce_coefficients(limb *output) {
   unsigned i;
 
   output[10] = 0;
@@ -277,7 +269,7 @@ static inline void fmul(limb *output, const limb *in, const limb *in2) {
   memcpy(output, t, sizeof(limb) * 10);
 }
 
-static ZT_ALWAYS_INLINE void fsquare_inner(limb *output, const limb *in) {
+static inline void fsquare_inner(limb *output, const limb *in) {
   output[0] =       ((limb) ((s32) in[0])) * ((s32) in[0]);
   output[1] =  2 *  ((limb) ((s32) in[0])) * ((s32) in[1]);
   output[2] =  2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
@@ -347,7 +339,7 @@ static inline void fsquare(limb *output, const limb *in) {
   memcpy(output, t, sizeof(limb) * 10);
 }
 
-static ZT_ALWAYS_INLINE void fexpand(limb *output, const u8 *input) {
+static inline void fexpand(limb *output, const u8 *input) {
 #define F(n,start,shift,mask) \
   output[n] = ((((limb) input[start + 0]) | \
                 ((limb) input[start + 1]) << 8 | \
@@ -370,7 +362,7 @@ static ZT_ALWAYS_INLINE void fexpand(limb *output, const u8 *input) {
 #error "This code only works when >> does sign-extension on negative numbers"
 #endif
 
-static ZT_ALWAYS_INLINE s32 s32_eq(s32 a, s32 b) {
+static inline s32 s32_eq(s32 a, s32 b) {
   a = ~(a ^ b);
   a &= a << 16;
   a &= a << 8;
@@ -380,7 +372,7 @@ static ZT_ALWAYS_INLINE s32 s32_eq(s32 a, s32 b) {
   return a >> 31;
 }
 
-static ZT_ALWAYS_INLINE s32 s32_gte(s32 a, s32 b) {
+static inline s32 s32_gte(s32 a, s32 b) {
   a -= b;
   /* a >= 0 iff a >= b. */
   return ~(a >> 31);
@@ -560,7 +552,7 @@ static inline void fmonty(limb *x2, limb *z2,  /* output 2Q */
   /* |z2|i| < 2^26 */
 }
 
-static ZT_ALWAYS_INLINE void swap_conditional(limb a[19], limb b[19], limb iswap) {
+static inline void swap_conditional(limb a[19], limb b[19], limb iswap) {
   unsigned i;
   const s32 swap = (s32) -iswap;
 
@@ -701,7 +693,7 @@ static inline void crypto_scalarmult(u8 *mypublic, const u8 *secret, const u8 *b
 }
 
 static const unsigned char base[32] = {9};
-static ZT_ALWAYS_INLINE void crypto_scalarmult_base(unsigned char *q,const unsigned char *n) { crypto_scalarmult(q,n,base); }
+static inline void crypto_scalarmult_base(unsigned char *q,const unsigned char *n) { crypto_scalarmult(q,n,base); }
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
@@ -759,7 +751,7 @@ typedef struct
 
 static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y);
 
-static ZT_ALWAYS_INLINE crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+static inline crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
 {
 	crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
 	x -= 1; /* 4294967295: yes; 0..65534: no */
@@ -767,7 +759,7 @@ static ZT_ALWAYS_INLINE crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /*
 	return x;
 }
 
-static ZT_ALWAYS_INLINE crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+static inline crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
 {
 	unsigned int x = a;
 	x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
@@ -776,10 +768,10 @@ static ZT_ALWAYS_INLINE crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-
 	return x;
 }
 
-static ZT_ALWAYS_INLINE crypto_uint32 times19(crypto_uint32 a) { return (a << 4) + (a << 1) + a; }
-static ZT_ALWAYS_INLINE crypto_uint32 times38(crypto_uint32 a) { return (a << 5) + (a << 2) + (a << 1); }
+static inline crypto_uint32 times19(crypto_uint32 a) { return (a << 4) + (a << 1) + a; }
+static inline crypto_uint32 times38(crypto_uint32 a) { return (a << 5) + (a << 2) + (a << 1); }
 
-static ZT_ALWAYS_INLINE void reduce_add_sub(fe25519 *r)
+static inline void reduce_add_sub(fe25519 *r)
 {
 	int i,rep;
 	for(rep=0;rep<4;rep++)
@@ -797,7 +789,7 @@ static ZT_ALWAYS_INLINE void reduce_add_sub(fe25519 *r)
 	}
 }
 
-static ZT_ALWAYS_INLINE void reduce_mul(fe25519 *r)
+static inline void reduce_mul(fe25519 *r)
 {
 	int i,rep;
 	for(rep=0;rep<2;rep++)
@@ -815,7 +807,7 @@ static ZT_ALWAYS_INLINE void reduce_mul(fe25519 *r)
 	}
 }
 
-static ZT_ALWAYS_INLINE void fe25519_freeze(fe25519 *r)
+static inline void fe25519_freeze(fe25519 *r)
 {
 	int i;
 	crypto_uint32 mm = equal(r->v[31],127);
@@ -831,14 +823,14 @@ static ZT_ALWAYS_INLINE void fe25519_freeze(fe25519 *r)
 	r->v[0] -= mm&237;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_unpack(fe25519 *r, const unsigned char x[32])
+static inline void fe25519_unpack(fe25519 *r, const unsigned char x[32])
 {
 	int i;
 	for(i=0;i<32;i++) r->v[i] = x[i];
 	r->v[31] &= 127;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_pack(unsigned char r[32], const fe25519 *x)
+static inline void fe25519_pack(unsigned char r[32], const fe25519 *x)
 {
 	int i;
 	fe25519 y = *x;
@@ -859,7 +851,7 @@ static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
 	return 1;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
+static inline void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b)
 {
 	int i;
 	crypto_uint32 mask = b;
@@ -867,27 +859,27 @@ static ZT_ALWAYS_INLINE void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned
 	for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]);
 }
 
-static ZT_ALWAYS_INLINE unsigned char fe25519_getparity(const fe25519 *x)
+static inline unsigned char fe25519_getparity(const fe25519 *x)
 {
 	fe25519 t = *x;
 	fe25519_freeze(&t);
 	return t.v[0] & 1;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_setone(fe25519 *r)
+static inline void fe25519_setone(fe25519 *r)
 {
 	int i;
 	r->v[0] = 1;
 	for(i=1;i<32;i++) r->v[i]=0;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_setzero(fe25519 *r)
+static inline void fe25519_setzero(fe25519 *r)
 {
 	int i;
 	for(i=0;i<32;i++) r->v[i]=0;
 }
 
-static ZT_ALWAYS_INLINE void fe25519_neg(fe25519 *r, const fe25519 *x)
+static inline void fe25519_neg(fe25519 *r, const fe25519 *x)
 {
 	fe25519 t;
 	int i;
@@ -896,14 +888,14 @@ static ZT_ALWAYS_INLINE void fe25519_neg(fe25519 *r, const fe25519 *x)
 	fe25519_sub(r, r, &t);
 }
 
-static ZT_ALWAYS_INLINE void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
+static inline void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y)
 {
 	int i;
 	for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
 	reduce_add_sub(r);
 }
 
-static ZT_ALWAYS_INLINE void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
+static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y)
 {
 	int i;
 	crypto_uint32 t[32];
@@ -914,7 +906,7 @@ static ZT_ALWAYS_INLINE void fe25519_sub(fe25519 *r, const fe25519 *x, const fe2
 	reduce_add_sub(r);
 }
 
-static ZT_ALWAYS_INLINE void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
+static inline void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y)
 {
 	int i,j;
 	crypto_uint32 t[63];
@@ -931,7 +923,7 @@ static ZT_ALWAYS_INLINE void fe25519_mul(fe25519 *r, const fe25519 *x, const fe2
 	reduce_mul(r);
 }
 
-static ZT_ALWAYS_INLINE void fe25519_square(fe25519 *r, const fe25519 *x) { fe25519_mul(r, x, x); }
+static inline void fe25519_square(fe25519 *r, const fe25519 *x) { fe25519_mul(r, x, x); }
 
 static inline void fe25519_invert(fe25519 *r, const fe25519 *x)
 {
@@ -1057,7 +1049,7 @@ static inline void fe25519_pow2523(fe25519 *r, const fe25519 *x)
 static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
 static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F};
 
-static ZT_ALWAYS_INLINE crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
+static inline crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */
 {
 	unsigned int x = a;
 	x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
@@ -1065,7 +1057,7 @@ static ZT_ALWAYS_INLINE crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-
 	return x;
 }
 
-static ZT_ALWAYS_INLINE void reduce_add_sub(sc25519 *r)
+static inline void reduce_add_sub(sc25519 *r)
 {
 	crypto_uint32 pb = 0;
 	crypto_uint32 b;
@@ -1144,7 +1136,7 @@ static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
 	barrett_reduce(r, t);
 }
 
-static ZT_ALWAYS_INLINE void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
+static inline void sc25519_to32bytes(unsigned char r[32], const sc25519 *x)
 {
 	int i;
 	for(i=0;i<32;i++) r[i] = x->v[i];
@@ -1181,7 +1173,7 @@ static inline void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y)
 	barrett_reduce(r, t);
 }
 
-static ZT_ALWAYS_INLINE void sc25519_window3(signed char r[85], const sc25519 *s)
+static inline void sc25519_window3(signed char r[85], const sc25519 *s)
 {
 	char carry;
 	int i;
@@ -1218,7 +1210,7 @@ static ZT_ALWAYS_INLINE void sc25519_window3(signed char r[85], const sc25519 *s
 	r[84] += carry;
 }
 
-static ZT_ALWAYS_INLINE void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
+static inline void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2)
 {
 	int i;
 	for(i=0;i<31;i++)
@@ -2107,21 +2099,21 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = {
  {{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}}
 };
 
-static ZT_ALWAYS_INLINE void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
+static inline void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p)
 {
 	fe25519_mul(&r->x, &p->x, &p->t);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->z, &p->z, &p->t);
 }
 
-static ZT_ALWAYS_INLINE void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
+static inline void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p)
 {
 	fe25519_mul(&r->x, &p->x, &p->t);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->z, &p->z, &p->t);
 }
 
-static ZT_ALWAYS_INLINE void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
+static inline void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p)
 {
 	p1p1_to_p2_2(r, p);
 	fe25519_mul(&r->t, &p->x, &p->y);
@@ -2190,13 +2182,13 @@ static inline void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p)
 }
 
 /* Constant-time version of: if(b) r = p */
-static ZT_ALWAYS_INLINE void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
+static inline void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b)
 {
 	fe25519_cmov(&r->x, &p->x, b);
 	fe25519_cmov(&r->y, &p->y, b);
 }
 
-static ZT_ALWAYS_INLINE unsigned char equal(signed char b,signed char c)
+static inline unsigned char equal(signed char b,signed char c)
 {
 	unsigned char ub = b;
 	unsigned char uc = c;
@@ -2207,7 +2199,7 @@ static ZT_ALWAYS_INLINE unsigned char equal(signed char b,signed char c)
 	return (unsigned char)y;
 }
 
-static ZT_ALWAYS_INLINE unsigned char negative(signed char b)
+static inline unsigned char negative(signed char b)
 {
 	unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
 	x >>= 63; /* 1: yes; 0: no */
@@ -2356,7 +2348,7 @@ static inline void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s)
 	}
 }
 
-static ZT_ALWAYS_INLINE void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
+static inline void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen)
 {
 	unsigned long long i;
 

+ 2 - 2
node/C25519.hpp

@@ -32,7 +32,7 @@ public:
 	/**
 	 * Generate a C25519 elliptic curve key pair
 	 */
-	static ZT_ALWAYS_INLINE void generate(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
+	static inline void generate(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 	{
 		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
 		_calcPubDH(pub,priv);
@@ -53,7 +53,7 @@ public:
 	 * @tparam F Type of 'cond'
 	 */
 	template<typename F>
-	static ZT_ALWAYS_INLINE void generateSatisfying(F cond,uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
+	static inline void generateSatisfying(F cond,uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN])
 	{
 		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
 		_calcPubED(pub,priv); // do Ed25519 key -- bytes 32-63 of pub and priv

+ 5 - 16
node/CMakeLists.txt

@@ -7,8 +7,8 @@ endif(WIN32)
 
 set(core_headers
 	Address.hpp
-	AES.hpp
 	AtomicCounter.hpp
+	Buf.hpp
 	Buffer.hpp
 	C25519.hpp
 	Capability.hpp
@@ -29,6 +29,7 @@ set(core_headers
 	Network.hpp
 	NetworkConfig.hpp
 	Node.hpp
+	OS.hpp
 	Packet.hpp
 	Path.hpp
 	Peer.hpp
@@ -50,13 +51,14 @@ set(core_headers
 
 set(core_src
 	AES.cpp
-	AES-aesni.c
+	Buf.cpp
 	C25519.cpp
 	Credential.cpp
 	ECC384.cpp
 	Identity.cpp
 	IncomingPacket.cpp
 	InetAddress.cpp
+	Locator.cpp
 	Membership.cpp
 	Network.cpp
 	NetworkConfig.cpp
@@ -69,23 +71,10 @@ set(core_src
 	SelfAwareness.cpp
 	SHA512.cpp
 	Switch.cpp
-	Trace.cpp
+	Topology.cpp
 	Utils.cpp
 )
 
 add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers})
 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
 target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})
-
-#if(UNIX)
-#	set_source_files_properties(
-#		AES.cpp
-#		AES-aesni.c
-#		ECC384.cpp
-#		Salsa20.cpp
-#		C25519.cpp
-#		Poly1305.cpp
-#		PROPERTIES
-#		COMPILE_FLAGS "-Wall -O3"
-#	)
-#endif(UNIX)

+ 15 - 15
node/Capability.hpp

@@ -14,9 +14,9 @@
 #ifndef ZT_CAPABILITY_HPP
 #define ZT_CAPABILITY_HPP
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 
 #include "Constants.hpp"
 #include "Credential.hpp"
@@ -61,7 +61,7 @@ class Capability : public Credential
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
 
-	ZT_ALWAYS_INLINE Capability() :
+	inline Capability() :
 		_nwid(0),
 		_ts(0),
 		_id(0),
@@ -80,7 +80,7 @@ public:
 	 * @param rules Network flow rules for this capability
 	 * @param ruleCount Number of flow rules
 	 */
-	ZT_ALWAYS_INLINE Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) :
+	inline Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) :
 		_nwid(nwid),
 		_ts(ts),
 		_id(id),
@@ -94,32 +94,32 @@ public:
 	/**
 	 * @return Rules -- see ruleCount() for size of array
 	 */
-	ZT_ALWAYS_INLINE const ZT_VirtualNetworkRule *rules() const { return _rules; }
+	inline const ZT_VirtualNetworkRule *rules() const { return _rules; }
 
 	/**
 	 * @return Number of rules in rules()
 	 */
-	ZT_ALWAYS_INLINE unsigned int ruleCount() const { return _ruleCount; }
+	inline unsigned int ruleCount() const { return _ruleCount; }
 
 	/**
 	 * @return ID and evaluation order of this capability in network
 	 */
-	ZT_ALWAYS_INLINE uint32_t id() const { return _id; }
+	inline uint32_t id() const { return _id; }
 
 	/**
 	 * @return Network ID for which this capability was issued
 	 */
-	ZT_ALWAYS_INLINE uint64_t networkId() const { return _nwid; }
+	inline uint64_t networkId() const { return _nwid; }
 
 	/**
 	 * @return Timestamp
 	 */
-	ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; }
+	inline int64_t timestamp() const { return _ts; }
 
 	/**
 	 * @return Last 'to' address in chain of custody
 	 */
-	ZT_ALWAYS_INLINE Address issuedTo() const
+	inline Address issuedTo() const
 	{
 		Address i2;
 		for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
@@ -165,7 +165,7 @@ public:
 	 *
 	 * @param RR Runtime environment to provide for peer lookup, etc.
 	 */
-	ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	template<unsigned int C>
 	static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
@@ -460,10 +460,10 @@ public:
 	}
 
 	// Provides natural sort order by ID
-	ZT_ALWAYS_INLINE bool operator<(const Capability &c) const { return (_id < c._id); }
+	inline bool operator<(const Capability &c) const { return (_id < c._id); }
 
-	ZT_ALWAYS_INLINE bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
-	ZT_ALWAYS_INLINE bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
+	inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
+	inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
 
 private:
 	uint64_t _nwid;

+ 17 - 17
node/CertificateOfMembership.hpp

@@ -14,8 +14,8 @@
 #ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
 #define ZT_CERTIFICATEOFMEMBERSHIP_HPP
 
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
 
 #include <string>
 #include <stdexcept>
@@ -69,7 +69,7 @@ class CertificateOfMembership : public Credential
 	friend class Credential;
 
 public:
-	static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
+	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; }
 
 	/**
 	 * Reserved qualifier IDs
@@ -101,7 +101,7 @@ public:
 	/**
 	 * Create an empty certificate of membership
 	 */
-	ZT_ALWAYS_INLINE CertificateOfMembership() :
+	inline CertificateOfMembership() :
 		_qualifierCount(0),
 		_signatureLength(0) {}
 
@@ -113,7 +113,7 @@ public:
 	 * @param nwid Network ID
 	 * @param issuedTo Certificate recipient
 	 */
-	ZT_ALWAYS_INLINE CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
+	inline CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
 	{
 		_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
 		_qualifiers[0].value = timestamp;
@@ -135,22 +135,22 @@ public:
 	 * @param startAt Position to start in buffer
 	 */
 	template<unsigned int C>
-	ZT_ALWAYS_INLINE CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
+	inline CertificateOfMembership(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
 
 	/**
 	 * @return True if there's something here
 	 */
-	ZT_ALWAYS_INLINE operator bool() const { return (_qualifierCount != 0); }
+	inline operator bool() const { return (_qualifierCount != 0); }
 
 	/**
 	 * @return Credential ID, always 0 for COMs
 	 */
-	ZT_ALWAYS_INLINE uint32_t id() const { return 0; }
+	inline uint32_t id() const { return 0; }
 
 	/**
 	 * @return Timestamp for this cert and maximum delta for timestamp
 	 */
-	ZT_ALWAYS_INLINE int64_t timestamp() const
+	inline int64_t timestamp() const
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
@@ -162,7 +162,7 @@ public:
 	/**
 	 * @return Address to which this cert was issued
 	 */
-	ZT_ALWAYS_INLINE Address issuedTo() const
+	inline Address issuedTo() const
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
@@ -174,7 +174,7 @@ public:
 	/**
 	 * @return Network ID for which this cert was issued
 	 */
-	ZT_ALWAYS_INLINE uint64_t networkId() const
+	inline uint64_t networkId() const
 	{
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
@@ -211,7 +211,7 @@ public:
 		}
 	}
 
-	ZT_ALWAYS_INLINE void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
+	inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); }
 
 	/**
 	 * Compare two certificates for parameter agreement
@@ -294,17 +294,17 @@ public:
 	 * @param RR Runtime environment for looking up peers
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 */
-	ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	/**
 	 * @return True if signed
 	 */
-	ZT_ALWAYS_INLINE bool isSigned() const { return (_signedBy); }
+	inline bool isSigned() const { return (_signedBy); }
 
 	/**
 	 * @return Address that signed this certificate or null address if none
 	 */
-	ZT_ALWAYS_INLINE const Address &signedBy() const { return _signedBy; }
+	inline const Address &signedBy() const { return _signedBy; }
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b) const
@@ -369,7 +369,7 @@ public:
 		return (p - startAt);
 	}
 
-	ZT_ALWAYS_INLINE bool operator==(const CertificateOfMembership &c) const
+	inline bool operator==(const CertificateOfMembership &c) const
 	{
 		if (_signedBy != c._signedBy)
 			return false;
@@ -385,7 +385,7 @@ public:
 		}
 		return (memcmp(_signature,c._signature,_signatureLength) == 0);
 	}
-	ZT_ALWAYS_INLINE bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
+	inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); }
 
 private:
 	struct _Qualifier

+ 23 - 23
node/CertificateOfOwnership.hpp

@@ -14,10 +14,10 @@
 #ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
 #define ZT_CERTIFICATEOFOWNERSHIP_HPP
 
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
 
 #include "Constants.hpp"
 #include "Credential.hpp"
@@ -46,7 +46,7 @@ class CertificateOfOwnership : public Credential
 	friend class Credential;
 
 public:
-	static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
+	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; }
 
 	enum Thing
 	{
@@ -56,12 +56,12 @@ public:
 		THING_IPV6_ADDRESS = 3
 	};
 
-	ZT_ALWAYS_INLINE CertificateOfOwnership()
+	inline CertificateOfOwnership()
 	{
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 	}
 
-	ZT_ALWAYS_INLINE CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id)
+	inline CertificateOfOwnership(const uint64_t nwid,const int64_t ts,const Address &issuedTo,const uint32_t id)
 	{
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 		_networkId = nwid;
@@ -70,19 +70,19 @@ public:
 		_issuedTo = issuedTo;
 	}
 
-	ZT_ALWAYS_INLINE uint64_t networkId() const { return _networkId; }
-	ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; }
-	ZT_ALWAYS_INLINE uint32_t id() const { return _id; }
-	ZT_ALWAYS_INLINE const Address &issuedTo() const { return _issuedTo; }
-	ZT_ALWAYS_INLINE const Address &signer() const { return _signedBy; }
-	ZT_ALWAYS_INLINE const uint8_t *signature() const { return _signature; }
-	ZT_ALWAYS_INLINE unsigned int signatureLength() const { return _signatureLength; }
+	inline uint64_t networkId() const { return _networkId; }
+	inline int64_t timestamp() const { return _ts; }
+	inline uint32_t id() const { return _id; }
+	inline const Address &issuedTo() const { return _issuedTo; }
+	inline const Address &signer() const { return _signedBy; }
+	inline const uint8_t *signature() const { return _signature; }
+	inline unsigned int signatureLength() const { return _signatureLength; }
 
-	ZT_ALWAYS_INLINE unsigned int thingCount() const { return (unsigned int)_thingCount; }
-	ZT_ALWAYS_INLINE Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
-	ZT_ALWAYS_INLINE const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
+	inline unsigned int thingCount() const { return (unsigned int)_thingCount; }
+	inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; }
+	inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; }
 
-	ZT_ALWAYS_INLINE bool owns(const InetAddress &ip) const
+	inline bool owns(const InetAddress &ip) const
 	{
 		if (ip.ss_family == AF_INET)
 			return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
@@ -91,7 +91,7 @@ public:
 		return false;
 	}
 
-	ZT_ALWAYS_INLINE bool owns(const MAC &mac) const
+	inline bool owns(const MAC &mac) const
 	{
 		uint8_t tmp[6];
 		mac.copyTo(tmp,6);
@@ -136,7 +136,7 @@ public:
 		return false;
 	}
 
-	ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
+	inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); }
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
@@ -206,10 +206,10 @@ public:
 	}
 
 	// Provides natural sort order by ID
-	ZT_ALWAYS_INLINE bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
+	inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); }
 
-	ZT_ALWAYS_INLINE bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
-	ZT_ALWAYS_INLINE bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
+	inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); }
+	inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); }
 
 private:
 	inline bool _owns(const Thing &t,const void *v,unsigned int l) const

+ 22 - 432
node/Constants.hpp

@@ -14,11 +14,8 @@
 #ifndef ZT_CONSTANTS_HPP
 #define ZT_CONSTANTS_HPP
 
-/****************************************************************************/
-/* Core includes and OS/platform setup stuff                                */
-/****************************************************************************/
-
 #include "../include/ZeroTierCore.h"
+#include "OS.hpp"
 
 #if __has_include("version.h")
 #include "version.h"
@@ -29,187 +26,25 @@
 #define ZEROTIER_ONE_VERSION_BUILD 255
 #endif
 
-//
-// This include file also auto-detects and canonicalizes some environment
-// information defines:
-//
-// __LINUX__
-// __APPLE__
-// __BSD__ (OSX also defines this)
-// __UNIX_LIKE__ (Linux, BSD, etc.)
-// __WINDOWS__
-//
-// Also makes sure __BYTE_ORDER is defined reasonably.
-//
-
-// Hack: make sure __GCC__ is defined on old GCC compilers
-#ifndef __GCC__
-#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
-#define __GCC__
-#endif
-#endif
-
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
-#ifndef __LINUX__
-#define __LINUX__
-#endif
-#ifndef __UNIX_LIKE__
-#define __UNIX_LIKE__
-#endif
-#include <endian.h>
-#endif
-
-#ifdef __APPLE__
-#include <TargetConditionals.h>
-#ifndef __UNIX_LIKE__
-#define __UNIX_LIKE__
-#endif
-#ifndef __BSD__
-#define __BSD__
-#endif
-#include <machine/endian.h>
-#endif
-
-#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
-#ifndef __UNIX_LIKE__
-#define __UNIX_LIKE__
-#endif
-#ifndef __BSD__
-#define __BSD__
-#endif
-#include <sys/endian.h>
-#ifndef __BYTE_ORDER
-#define __BYTE_ORDER _BYTE_ORDER
-#define __LITTLE_ENDIAN _LITTLE_ENDIAN
-#define __BIG_ENDIAN _BIG_ENDIAN
-#endif
-#endif
-
-#if defined(_WIN32) || defined(_WIN64)
-#ifndef __WINDOWS__
-#define __WINDOWS__
-#endif
-#ifndef NOMINMAX
-#define NOMINMAX
-#endif
-#pragma warning(disable : 4290)
-#pragma warning(disable : 4996)
-#pragma warning(disable : 4101)
-#undef __UNIX_LIKE__
-#undef __BSD__
-#include <WinSock2.h>
-#include <Windows.h>
-#endif
-
-#ifdef __NetBSD__
-#ifndef RTF_MULTICAST
-#define RTF_MULTICAST 0x20000000
-#endif
-#endif
-
-// Define ZT_NO_TYPE_PUNNING to disable reckless casts on anything other than x86 and x86_64.
-#if (!(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || defined(_M_X64) || defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_) || defined(__I86__) || defined(__INTEL__) || defined(__386)))
-#ifndef ZT_NO_TYPE_PUNNING
-#define ZT_NO_TYPE_PUNNING
-#endif
-#endif
-
-// Assume little endian if not defined on Mac and Windows as these don't run on any BE architectures.
-#if (defined(__APPLE__) || defined(__WINDOWS__)) && (!defined(__BYTE_ORDER))
-#undef __BYTE_ORDER
-#undef __LITTLE_ENDIAN
-#undef __BIG_ENDIAN
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#define __BYTE_ORDER 1234
-#endif
-#ifndef __BYTE_ORDER
-#include <endian.h>
-#endif
-
-#ifdef __WINDOWS__
-#define ZT_PATH_SEPARATOR '\\'
-#define ZT_PATH_SEPARATOR_S "\\"
-#define ZT_EOL_S "\r\n"
-#else
-#define ZT_PATH_SEPARATOR '/'
-#define ZT_PATH_SEPARATOR_S "/"
-#define ZT_EOL_S "\n"
-#endif
-
-#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__)
-#define ZT_ALWAYS_INLINE inline __attribute__((always_inline))
-#ifndef likely
-#define likely(x) __builtin_expect((x),1)
-#endif
-#ifndef unlikely
-#define unlikely(x) __builtin_expect((x),0)
-#endif
-#else
-#ifndef likely
-#define ZT_ALWAYS_INLINE inline
-#define likely(x) (x)
-#endif
-#ifndef unlikely
-#define unlikely(x) (x)
-#endif
-#endif
-
-#if defined(__WINDOWS__) && !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER)
-#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
-#else
-#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
-#endif
-
-#if __cplusplus > 199711L
-#ifndef __CPP11__
-#define __CPP11__
-#endif
-#endif
-
-#ifdef SOCKET
-#define ZT_SOCKET SOCKET
-#else
-#define ZT_SOCKET int
-#endif
-#ifdef INVALID_SOCKET
-#define ZT_INVALID_SOCKET INVALID_SOCKET
-#else
-#define ZT_INVALID_SOCKET -1
-#endif
-
-/****************************************************************************/
-/* Internal ZeroTier constants                                              */
-/****************************************************************************/
-
 /**
  * Length of a ZeroTier address in bytes
  */
 #define ZT_ADDRESS_LENGTH 5
 
-/**
- * Length of a hexadecimal ZeroTier address
- */
-#define ZT_ADDRESS_LENGTH_HEX 10
-
 /**
  * Addresses beginning with this byte are reserved for the joy of in-band signaling
  */
 #define ZT_ADDRESS_RESERVED_PREFIX 0xff
 
 /**
- * Secure DNS name for ZeroTier's default root
- *
- * This resolves via GeoDNS to the (probably) nearest actual root server's locator.
+ * Maximum DNS or URL name size for an Endpoint (set so that max marshaled endpoint size is 64 bytes)
  */
-#define ZT_DEFAULT_ROOT_NAME "ztl-aj4zes4l6zumq64na6borruuvd6diw2koxrjcaatolcekt2gj5rrhric.ztl-6lhxeo7n3z7kzkgcqzj3ndliaq.zerotier.network"
+#define ZT_ENDPOINT_MAX_NAME_SIZE 61
 
 /**
- * Default locator for default root
- *
- * This is used before the root has been successfully fetched, or if DNS is unavailable.
+ * Size of an identity hash (SHA384)
  */
-#define ZT_DEFAULT_ROOT_LOCATOR "AAAAAW2OuYyfOkbxvzAAduZvqzPihUmmLuIGTRhDJzwsMAukXD8gvvAtutIlcju1mpu0sTU1cwlhruz1oWOs5HfM6wcnAluZrBSlFmoJowAEBLm0DVInCQS5tA1SAbsGKgJuoMgVAAAAAAAAAAAAACcJBioCbqDIFQAAAAAAAAAAAAABuwAAYDvTNB2snbn7TYom4PBTh/ohRgCnI2/A/nfKakGCb+2hGJTtxTCiGTzKZdbjd0vyKAKQLJxhj7RaoCo3XjPn8w9nDEmhdNCgCM/IITCJIzc9tEKFsSQnJY4VmB3dopBAfQAA"
+#define ZT_IDENTITY_HASH_SIZE 48
 
 /**
  * Default virtual network MTU (not physical)
@@ -282,246 +117,34 @@
 #define ZT_RELAY_MAX_HOPS 4
 
 /**
- * Expire time for multicast 'likes' and indirect multicast memberships in ms
- */
-#define ZT_MULTICAST_LIKE_EXPIRE 600000
-
-/**
- * Period for multicast LIKE re-announcements to connected nodes
- */
-#define ZT_MULTICAST_ANNOUNCE_PERIOD 60000
-
-/**
- * Period for multicast GATHER on multicast groups
- */
-#define ZT_MULTICAST_GATHER_PERIOD ZT_MULTICAST_ANNOUNCE_PERIOD
-
-/**
- * Period for multicast GATHER if there are no known recipients
- */
-#define ZT_MULTICAST_GATHER_PERIOD_WHEN_NO_RECIPIENTS 2500
-
-/**
- * Timeout for outgoing multicasts
- *
- * This is how long we wait for explicit or implicit gather results.
- */
-#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
-
-/**
- * How frequently to check for changes to the system's network interfaces. When
- * the service decides to use this constant it's because we want to react more
- * quickly to new interfaces that pop up or go down.
- */
-#define ZT_MULTIPATH_BINDER_REFRESH_PERIOD 5000
-
-/**
- * Packets are only used for QoS/ACK statistical sampling if their packet ID is divisible by
- * this integer. This is to provide a mechanism for both peers to agree on which packets need
- * special treatment without having to exchange information. Changing this value would be
- * a breaking change and would necessitate a protocol version upgrade. Since each incoming and
- * outgoing packet ID is checked against this value its evaluation is of the form:
- * (id & (divisor - 1)) == 0, thus the divisor must be a power of 2.
- *
- * This value is set at (16) so that given a normally-distributed RNG output we will sample
- * 1/16th (or ~6.25%) of packets.
- */
-#define ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR 0x10
-
-/**
- * Time horizon for VERB_QOS_MEASUREMENT and VERB_ACK packet processing cutoff
- */
-#define ZT_PATH_QOS_ACK_CUTOFF_TIME 30000
-
-/**
- * Maximum number of VERB_QOS_MEASUREMENT and VERB_ACK packets allowed to be
- * processed within cutoff time. Separate totals are kept for each type but
- * the limit is the same for both.
- *
- * This limits how often this peer will compute statistical estimates
- * of various QoS measures from a VERB_QOS_MEASUREMENT or VERB_ACK packets to
- * CUTOFF_LIMIT times per CUTOFF_TIME milliseconds per peer to prevent
- * this from being useful for DOS amplification attacks.
- */
-#define ZT_PATH_QOS_ACK_CUTOFF_LIMIT 128
-
-/**
- * Path choice history window size. This is used to keep track of which paths were
- * previously selected so that we can maintain a target allocation over time.
- */
-#define ZT_MULTIPATH_PROPORTION_WIN_SZ 128
-
-/**
- * Interval used for rate-limiting the computation of path quality estimates.
- */
-#define ZT_PATH_QUALITY_COMPUTE_INTERVAL 1000
-
-/**
- * Number of samples to consider when computing real-time path statistics
- */
-#define ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ 128
-
-/**
- * Number of samples to consider when computing performing long-term path quality analysis.
- * By default this value is set to ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ but can
- * be set to any value greater than that to observe longer-term path quality behavior.
- */
-#define ZT_PATH_QUALITY_METRIC_WIN_SZ ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ
-
-/**
- * Maximum acceptable Packet Delay Variance (PDV) over a path
- */
-#define ZT_PATH_MAX_PDV 1000
-
-/**
- * Maximum acceptable time interval between expectation and receipt of at least one ACK over a path
- */
-#define ZT_PATH_MAX_AGE 30000
-
-/**
- * Maximum acceptable mean latency over a path
- */
-#define ZT_PATH_MAX_MEAN_LATENCY 1000
-
-/**
- * How much each factor contributes to the "stability" score of a path
- */
-#define ZT_PATH_CONTRIB_PDV                    (1.0 / 3.0)
-#define ZT_PATH_CONTRIB_LATENCY                (1.0 / 3.0)
-#define ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE (1.0 / 3.0)
-
-/**
- * How much each factor contributes to the "quality" score of a path
- */
-#define ZT_PATH_CONTRIB_STABILITY  (0.75 / 3.0)
-#define ZT_PATH_CONTRIB_THROUGHPUT (1.50 / 3.0)
-#define ZT_PATH_CONTRIB_SCOPE      (0.75 / 3.0)
-
-/**
- * How often a QoS packet is sent
- */
-#define ZT_PATH_QOS_INTERVAL 3000
-
-/**
- * Min and max acceptable sizes for a VERB_QOS_MEASUREMENT packet
- */
-#define ZT_PATH_MIN_QOS_PACKET_SZ 8 + 1
-#define ZT_PATH_MAX_QOS_PACKET_SZ 1400
-
-/**
- * How many ID:sojourn time pairs in a single QoS packet
- */
-#define ZT_PATH_QOS_TABLE_SIZE ((ZT_PATH_MAX_QOS_PACKET_SZ * 8) / (64 + 16))
-
-/**
- * Maximum number of outgoing packets we monitor for QoS information
- */
-#define ZT_PATH_MAX_OUTSTANDING_QOS_RECORDS 128
-
-/**
- * Timeout for QoS records
- */
-#define ZT_PATH_QOS_TIMEOUT (ZT_PATH_QOS_INTERVAL * 2)
-
-/**
- * How often the service tests the path throughput
- */
-#define ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL (ZT_PATH_ACK_INTERVAL * 8)
-
-/**
- * Minimum amount of time between each ACK packet
- */
-#define ZT_PATH_ACK_INTERVAL 1000
-
-/**
- * How often an aggregate link statistics report is emitted into this tracing system
- */
-#define ZT_PATH_AGGREGATE_STATS_REPORT_INTERVAL 60000
-
-/**
- * How much an aggregate link's component paths can vary from their target allocation
- * before the link is considered to be in a state of imbalance.
- */
-#define ZT_PATH_IMBALANCE_THRESHOLD 0.20
-
-/**
- * Max allowable time spent in any queue
- */
-#define ZT_QOS_TARGET 5 // ms
-
-/**
- * Time period where the time spent in the queue by a packet should fall below
- * target at least once
- */
-#define ZT_QOS_INTERVAL 100 // ms
-
-/**
- * The number of bytes that each queue is allowed to send during each DRR cycle.
- * This approximates a single-byte-based fairness queuing scheme
- */
-#define ZT_QOS_QUANTUM ZT_DEFAULT_MTU
-
-/**
- * The maximum total number of packets that can be queued among all
- * active/inactive, old/new queues
- */
-#define ZT_QOS_MAX_ENQUEUED_PACKETS 1024
-
-/**
- * Number of QoS queues (buckets)
- */
-#define ZT_QOS_NUM_BUCKETS 9
-
-/**
- * All unspecified traffic is put in this bucket. Anything in a bucket with a smaller
- * value is de-prioritized. Anything in a bucket with a higher value is prioritized over
- * other traffic.
- */
-#define ZT_QOS_DEFAULT_BUCKET 0
-
-/**
- * Do not accept HELLOs over a given path more often than this
- */
-#define ZT_PATH_HELLO_RATE_LIMIT 1000
-
-/**
- * Delay between full-fledge pings of directly connected peers
+ * Period between keepalives sent to paths if no other traffic has been sent
  *
  * See https://conferences.sigcomm.org/imc/2010/papers/p260.pdf for
  * some real world data on NAT UDP timeouts. From the paper: "the
  * lowest measured timeout when a binding has seen bidirectional
  * traffic is 54 sec." 30 seconds is faster than really necessary.
  */
-#define ZT_PEER_PING_PERIOD 30000
+#define ZT_PATH_KEEPALIVE_PERIOD 30000
 
 /**
- * Delay between refreshes of locators via DNS or other methods
+ * Timeout for path aliveness (measured from last receive)
  */
-#define ZT_DYNAMIC_ROOT_UPDATE_PERIOD 120000
+#define ZT_PATH_ACTIVITY_TIMEOUT ((ZT_PATH_KEEPALIVE_PERIOD * 2) + 5000)
 
 /**
- * Timeout for overall peer activity (measured from last receive)
+ * Delay between full HELLO messages between peers
  */
-#ifndef ZT_SDK
-#define ZT_PEER_ACTIVITY_TIMEOUT 500000
-#else
-#define ZT_PEER_ACTIVITY_TIMEOUT 30000
-#endif
+#define ZT_PEER_PING_PERIOD 60000
 
 /**
- * Rescan for best/fastest root every N milliseconds
- */
-#define ZT_FIND_BEST_ROOT_PERIOD 2000
-
-/**
- * General rate limit timeout for multiple packet types (HELLO, etc.)
+ * Timeout for overall peer activity (measured from last receive)
  */
-#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
+#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_PING_PERIOD * 2) + 5000)
 
 /**
- * General limit for max RTT for requests over the network
+ * Maximum interval between sort/prioritize of paths for a peer
  */
-#define ZT_GENERAL_RTT_LIMIT 5000
+#define ZT_PEER_PRIORITIZE_PATHS_INTERVAL 5000
 
 /**
  * Delay between requests for updated network autoconf information
@@ -531,15 +154,6 @@
  */
 #define ZT_NETWORK_AUTOCONF_DELAY 60000
 
-/**
- * Minimum interval between attempts by relays to unite peers
- *
- * When a relay gets a packet destined for another peer, it sends both peers
- * a RENDEZVOUS message no more than this often. This instructs the peers
- * to attempt NAT-t and gives each the other's corresponding IP:port pair.
- */
-#define ZT_MIN_UNITE_INTERVAL 30000
-
 /**
  * Sanity limit on maximum bridge routes
  *
@@ -566,34 +180,10 @@
  */
 #define ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH 120000
 
-/**
- * Time horizon for push direct paths cutoff
- */
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000
-
-/**
- * Maximum number of direct path pushes within cutoff time
- *
- * This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses
- * per CUTOFF_TIME milliseconds per peer to prevent this from being
- * useful for DOS amplification attacks.
- */
-#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8
-
 /**
  * Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
  */
-#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8
-
-/**
- * Time horizon for VERB_NETWORK_CREDENTIALS cutoff
- */
-#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
-
-/**
- * Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
- */
-#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
+#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
 
 /**
  * WHOIS rate limit (we allow these to be pretty fast)
@@ -632,12 +222,7 @@
  */
 #define ZT_SIGNATURE_BUFFER_SIZE 96
 
-/**
- * Desired / recommended min stack size for threads (used on some platforms to reset thread stack size)
- */
-#define ZT_THREAD_MIN_STACK_SIZE 1048576
-
-// Internal cryptographic algorithm IDs
+// Internal cryptographic algorithm IDs (these match relevant identity types)
 #define ZT_CRYPTO_ALG_C25519 0
 #define ZT_CRYPTO_ALG_P384 1
 
@@ -651,4 +236,9 @@
 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202
 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
 
+/* Ethernet frame types that might be relevant to us */
+#define ZT_ETHERTYPE_IPV4 0x0800
+#define ZT_ETHERTYPE_ARP 0x0806
+#define ZT_ETHERTYPE_IPV6 0x86dd
+
 #endif

+ 1 - 1
node/Credential.cpp

@@ -26,7 +26,7 @@
 namespace ZeroTier {
 
 template<typename CRED>
-static ZT_ALWAYS_INLINE Credential::VerifyResult _credVerify(const RuntimeEnvironment *const RR,void *tPtr,CRED credential)
+static inline Credential::VerifyResult _credVerify(const RuntimeEnvironment *const RR,void *tPtr,CRED credential)
 {
 	const Address signedBy(credential.signer());
 	const uint64_t networkId = credential.networkId();

+ 4 - 4
node/Credential.hpp

@@ -18,10 +18,10 @@
 #include <memory>
 #include <stdexcept>
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
 
 #include "Constants.hpp"
 

+ 23 - 23
node/Dictionary.hpp

@@ -19,7 +19,7 @@
 #include "Buffer.hpp"
 #include "Address.hpp"
 
-#include <stdint.h>
+#include <cstdint>
 
 namespace ZeroTier {
 
@@ -49,9 +49,9 @@ template<unsigned int C>
 class Dictionary
 {
 public:
-	ZT_ALWAYS_INLINE Dictionary() { memset(_d,0,sizeof(_d)); }
-	ZT_ALWAYS_INLINE Dictionary(const char *s) { this->load(s); }
-	ZT_ALWAYS_INLINE Dictionary(const char *s,unsigned int len)
+	inline Dictionary() { memset(_d,0,sizeof(_d)); }
+	inline Dictionary(const char *s) { this->load(s); }
+	inline Dictionary(const char *s,unsigned int len)
 	{
 		for(unsigned int i=0;i<C;++i) {
 			if ((s)&&(i < len)) {
@@ -62,15 +62,15 @@ public:
 		}
 		_d[C - 1] = (char)0;
 	}
-	ZT_ALWAYS_INLINE Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
+	inline Dictionary(const Dictionary &d) { memcpy(_d,d._d,C); }
 
-	ZT_ALWAYS_INLINE Dictionary &operator=(const Dictionary &d)
+	inline Dictionary &operator=(const Dictionary &d)
 	{
 		memcpy(_d,d._d,C);
 		return *this;
 	}
 
-	ZT_ALWAYS_INLINE operator bool() const { return (_d[0] != 0); }
+	inline operator bool() const { return (_d[0] != 0); }
 
 	/**
 	 * Load a dictionary from a C-string
@@ -78,7 +78,7 @@ public:
 	 * @param s Dictionary in string form
 	 * @return False if 's' was longer than our capacity
 	 */
-	ZT_ALWAYS_INLINE bool load(const char *s)
+	inline bool load(const char *s)
 	{
 		for(unsigned int i=0;i<C;++i) {
 			if (s) {
@@ -94,12 +94,12 @@ public:
 	/**
 	 * Delete all entries
 	 */
-	ZT_ALWAYS_INLINE void clear() { memset(_d,0,sizeof(_d)); }
+	inline void clear() { memset(_d,0,sizeof(_d)); }
 
 	/**
 	 * @return Size of dictionary in bytes not including terminating NULL
 	 */
-	ZT_ALWAYS_INLINE unsigned int sizeBytes() const
+	inline unsigned int sizeBytes() const
 	{
 		for(unsigned int i=0;i<C;++i) {
 			if (!_d[i])
@@ -217,7 +217,7 @@ public:
 	 * @tparam BC Buffer capacity (usually inferred)
 	 */
 	template<unsigned int BC>
-	ZT_ALWAYS_INLINE bool get(const char *key,Buffer<BC> &dest) const
+	inline bool get(const char *key,Buffer<BC> &dest) const
 	{
 		const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
 		if (r >= 0) {
@@ -236,7 +236,7 @@ public:
 	 * @param dfl Default value if not found in dictionary
 	 * @return Boolean value of key or 'dfl' if not found
 	 */
-	ZT_ALWAYS_INLINE bool getB(const char *key,bool dfl = false) const
+	inline bool getB(const char *key,bool dfl = false) const
 	{
 		char tmp[4];
 		if (this->get(key,tmp,sizeof(tmp)) >= 0)
@@ -251,7 +251,7 @@ public:
 	 * @param dfl Default value or 0 if unspecified
 	 * @return Decoded hex UInt value or 'dfl' if not found
 	 */
-	ZT_ALWAYS_INLINE uint64_t getUI(const char *key,uint64_t dfl = 0) const
+	inline uint64_t getUI(const char *key,uint64_t dfl = 0) const
 	{
 		char tmp[128];
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
@@ -266,7 +266,7 @@ public:
 	 * @param dfl Default value or 0 if unspecified
 	 * @return Decoded hex UInt value or 'dfl' if not found
 	 */
-	ZT_ALWAYS_INLINE int64_t getI(const char *key,int64_t dfl = 0) const
+	inline int64_t getI(const char *key,int64_t dfl = 0) const
 	{
 		char tmp[128];
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
@@ -366,7 +366,7 @@ public:
 	/**
 	 * Add a boolean as a '1' or a '0'
 	 */
-	ZT_ALWAYS_INLINE bool add(const char *key,bool value)
+	inline bool add(const char *key,bool value)
 	{
 		return this->add(key,(value) ? "1" : "0",1);
 	}
@@ -374,7 +374,7 @@ public:
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 */
-	ZT_ALWAYS_INLINE bool add(const char *key,uint64_t value)
+	inline bool add(const char *key,uint64_t value)
 	{
 		char tmp[32];
 		return this->add(key,Utils::hex(value,tmp),-1);
@@ -383,7 +383,7 @@ public:
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 */
-	ZT_ALWAYS_INLINE bool add(const char *key,int64_t value)
+	inline bool add(const char *key,int64_t value)
 	{
 		char tmp[32];
 		if (value >= 0) {
@@ -397,7 +397,7 @@ public:
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 */
-	ZT_ALWAYS_INLINE bool add(const char *key,const Address &a)
+	inline bool add(const char *key,const Address &a)
 	{
 		char tmp[32];
 		return this->add(key,Utils::hex(a.toInt(),tmp),-1);
@@ -409,7 +409,7 @@ public:
 	 * @tparam BC Buffer capacity (usually inferred)
 	 */
 	template<unsigned int BC>
-	ZT_ALWAYS_INLINE bool add(const char *key,const Buffer<BC> &value)
+	inline bool add(const char *key,const Buffer<BC> &value)
 	{
 		return this->add(key,(const char *)value.data(),(int)value.size());
 	}
@@ -418,7 +418,7 @@ public:
 	 * @param key Key to check
 	 * @return True if key is present
 	 */
-	ZT_ALWAYS_INLINE bool contains(const char *key) const
+	inline bool contains(const char *key) const
 	{
 		char tmp[2];
 		return (this->get(key,tmp,2) >= 0);
@@ -427,10 +427,10 @@ public:
 	/**
 	 * @return Value of C template parameter
 	 */
-	ZT_ALWAYS_INLINE unsigned int capacity() const { return C; }
+	inline unsigned int capacity() const { return C; }
 
-	ZT_ALWAYS_INLINE const char *data() const { return _d; }
-	ZT_ALWAYS_INLINE char *unsafeData() { return _d; }
+	inline const char *data() const { return _d; }
+	inline char *unsafeData() { return _d; }
 
 private:
 	char _d[C];

+ 6 - 7
node/ECC384.cpp

@@ -4,10 +4,9 @@
 // This code is under the BSD 2-clause license, not ZeroTier's license
 //////////////////////////////////////////////////////////////////////////////
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
 
 #include "Constants.hpp"
 #include "ECC384.hpp"
@@ -141,7 +140,7 @@ static ZT_ALWAYS_INLINE void vli_set(uint64_t *p_dest, uint64_t *p_src)
 }
 
 /* Returns sign of p_left - p_right. */
-static ZT_ALWAYS_INLINE int vli_cmp(uint64_t *p_left, uint64_t *p_right)
+static inline int vli_cmp(uint64_t *p_left, uint64_t *p_right)
 {
 	int i;
 	for(i = NUM_ECC_DIGITS-1; i >= 0; --i)
@@ -376,7 +375,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left)
 
 /* Computes p_result = (p_left + p_right) % p_mod.
    Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */
-static ZT_ALWAYS_INLINE void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
+static inline void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
 {
 	uint64_t l_carry = vli_add(p_result, p_left, p_right);
 	if(l_carry || vli_cmp(p_result, p_mod) >= 0)
@@ -387,7 +386,7 @@ static ZT_ALWAYS_INLINE void vli_modAdd(uint64_t *p_result, uint64_t *p_left, ui
 
 /* Computes p_result = (p_left - p_right) % p_mod.
    Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */
-static ZT_ALWAYS_INLINE void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
+static inline void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod)
 {
 	uint64_t l_borrow = vli_sub(p_result, p_left, p_right);
 	if(l_borrow)

+ 248 - 0
node/Endpoint.hpp

@@ -0,0 +1,248 @@
+/*
+ * 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_ENDPOINT_HPP
+#define ZT_ENDPOINT_HPP
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+#include <cstring>
+
+#include "Constants.hpp"
+#include "InetAddress.hpp"
+#include "Address.hpp"
+#include "Utils.hpp"
+
+#define ZT_ENDPOINT_MARSHAL_SIZE_MAX (ZT_ENDPOINT_MAX_NAME_SIZE+3)
+
+namespace ZeroTier {
+
+/**
+ * Endpoint variant specifying some form of network endpoint
+ */
+class Endpoint
+{
+public:
+	enum Type
+	{
+		NIL =      0, // NIL value
+		INETADDR = 1, // InetAddress (v4 or v6)
+		DNSNAME =  2, // DNS name and port that resolves to InetAddress
+		ZEROTIER = 3, // ZeroTier Address (for relaying and meshy behavior)
+		URL =      4, // URL for http/https/ws/etc. (not implemented yet)
+		ETHERNET = 5  // 48-bit LAN-local Ethernet address
+	};
+
+	ZT_ALWAYS_INLINE Endpoint() { memset(reinterpret_cast<void *>(this),0,sizeof(Endpoint)); }
+
+	explicit ZT_ALWAYS_INLINE Endpoint(const InetAddress &sa) : _t(INETADDR) { _v.sa = sa; }
+	ZT_ALWAYS_INLINE Endpoint(const Address &zt,const uint8_t identityHash[ZT_IDENTITY_HASH_SIZE]) : _t(ZEROTIER) { _v.zt.a = zt.toInt(); memcpy(_v.zt.idh,identityHash,ZT_IDENTITY_HASH_SIZE); }
+	ZT_ALWAYS_INLINE Endpoint(const char *name,const int port) : _t(DNSNAME) { Utils::scopy(_v.dns.name,sizeof(_v.dns.name),name); _v.dns.port = port; }
+	explicit ZT_ALWAYS_INLINE Endpoint(const char *url) : _t(URL) { Utils::scopy(_v.url,sizeof(_v.url),url); }
+
+	ZT_ALWAYS_INLINE const InetAddress *sockaddr() const { return (_t == INETADDR) ? reinterpret_cast<const InetAddress *>(&_v.sa) : nullptr; }
+	ZT_ALWAYS_INLINE const char *dnsName() const { return (_t == DNSNAME) ? _v.dns.name : nullptr; }
+	ZT_ALWAYS_INLINE int dnsPort() const { return (_t == DNSNAME) ? _v.dns.port : -1; }
+	ZT_ALWAYS_INLINE Address ztAddress() const { return (_t == ZEROTIER) ? Address(_v.zt.a) : Address(); }
+	ZT_ALWAYS_INLINE const uint8_t *ztIdentityHash() const { return (_t == ZEROTIER) ? _v.zt.idh : nullptr; }
+	ZT_ALWAYS_INLINE const char *url() const { return (_t == URL) ? _v.url : nullptr; }
+	ZT_ALWAYS_INLINE MAC ethernet() const { return (_t == ETHERNET) ? MAC(_v.eth) : MAC(); }
+
+	ZT_ALWAYS_INLINE Type type() const { return _t; }
+
+	ZT_ALWAYS_INLINE bool operator==(const Endpoint &ep) const
+	{
+		if (_t == ep._t) {
+			switch(_t) {
+				case INETADDR: return (*sockaddr() == *ep.sockaddr());
+				case DNSNAME:  return ((_v.dns.port == ep._v.dns.port)&&(strcmp(_v.dns.name,ep._v.dns.name) == 0));
+				case ZEROTIER: return ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) == 0));
+				case URL:      return (strcmp(_v.url,ep._v.url) == 0);
+				case ETHERNET: return (_v.eth == ep._v.eth);
+				default:       return true;
+			}
+		}
+		return false;
+	}
+	ZT_ALWAYS_INLINE bool operator!=(const Endpoint &ep) const { return (!(*this == ep)); }
+	ZT_ALWAYS_INLINE bool operator<(const Endpoint &ep) const
+	{
+		if ((int)_t < (int)ep._t) {
+			return true;
+		} else if (_t == ep._t) {
+			int ncmp;
+			switch(_t) {
+				case INETADDR: return (*sockaddr() < *ep.sockaddr());
+				case DNSNAME:
+					ncmp = strcmp(_v.dns.name,ep._v.dns.name);
+					return ((ncmp < 0) ? true : (ncmp == 0)&&(_v.dns.port < ep._v.dns.port));
+				case ZEROTIER: return (_v.zt.a < ep._v.zt.a) ? true : ((_v.zt.a == ep._v.zt.a)&&(memcmp(_v.zt.idh,ep._v.zt.idh,sizeof(_v.zt.idh)) < 0));
+				case URL:      return (strcmp(_v.url,ep._v.url) < 0);
+				case ETHERNET: return (_v.eth < ep._v.eth);
+				default:       return false;
+			}
+		}
+		return false;
+	}
+	ZT_ALWAYS_INLINE bool operator>(const Endpoint &ep) const { return (ep < *this); }
+	ZT_ALWAYS_INLINE bool operator<=(const Endpoint &ep) const { return !(ep < *this); }
+	ZT_ALWAYS_INLINE bool operator>=(const Endpoint &ep) const { return !(*this < ep); }
+
+	// Marshal interface ///////////////////////////////////////////////////////
+	static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_ENDPOINT_MARSHAL_SIZE_MAX; }
+	inline int marshal(uint8_t data[ZT_ENDPOINT_MARSHAL_SIZE_MAX]) const
+	{
+		int p;
+		switch(_t) {
+			case INETADDR:
+				data[0] = (uint8_t)INETADDR;
+				return 1 + reinterpret_cast<const InetAddress *>(&_v.sa)->marshal(data+1);
+			case DNSNAME:
+				data[0] = (uint8_t)DNSNAME;
+				p = 1;
+				for (;;) {
+					if ((data[p] = (uint8_t)_v.dns.name[p-1]) == 0)
+						break;
+					++p;
+					if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
+						return -1;
+				}
+				data[p++] = (uint8_t)(_v.dns.port >> 8U);
+				data[p++] = (uint8_t)_v.dns.port;
+				return p;
+			case ZEROTIER:
+				data[0] = (uint8_t)ZEROTIER;
+				data[1] = (uint8_t)(_v.zt.a >> 32U);
+				data[2] = (uint8_t)(_v.zt.a >> 24U);
+				data[3] = (uint8_t)(_v.zt.a >> 16U);
+				data[4] = (uint8_t)(_v.zt.a >> 8U);
+				data[5] = (uint8_t)_v.zt.a;
+				memcpy(data + 6,_v.zt.idh,ZT_IDENTITY_HASH_SIZE);
+				return (ZT_IDENTITY_HASH_SIZE + 6);
+			case URL:
+				data[0] = (uint8_t)URL;
+				p = 1;
+				for (;;) {
+					if ((data[p] = (uint8_t)_v.url[p-1]) == 0)
+						break;
+					++p;
+					if (p == (ZT_ENDPOINT_MAX_NAME_SIZE+1))
+						return -1;
+				}
+				return p;
+			case ETHERNET:
+				data[0] = (uint8_t)ETHERNET;
+				data[1] = (uint8_t)(_v.eth >> 40U);
+				data[2] = (uint8_t)(_v.eth >> 32U);
+				data[3] = (uint8_t)(_v.eth >> 24U);
+				data[4] = (uint8_t)(_v.eth >> 16U);
+				data[5] = (uint8_t)(_v.eth >> 8U);
+				data[6] = (uint8_t)_v.eth;
+				return 7;
+			default:
+				data[0] = (uint8_t)NIL;
+				return 1;
+		}
+	}
+	inline int unmarshal(const uint8_t *restrict data,const int len)
+	{
+		if (len <= 0)
+			return -1;
+		int p;
+		switch((Type)data[0]) {
+			case NIL:
+				_t = NIL;
+				return 1;
+			case INETADDR:
+				_t = INETADDR;
+				return reinterpret_cast<InetAddress *>(&_v.sa)->unmarshal(data+1,len-1);
+			case DNSNAME:
+				if (len < 4)
+					return -1;
+				_t = DNSNAME;
+				p = 1;
+				for (;;) {
+					if ((_v.dns.name[p-1] = (char)data[p]) == 0) {
+						++p;
+						break;
+					}
+					++p;
+					if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= (len-2)))
+						return -1;
+				}
+				_v.dns.port = (uint16_t)(((unsigned int)data[p++]) << 8U);
+				_v.dns.port |= (uint16_t)data[p++];
+				return p;
+			case ZEROTIER:
+				if (len < (ZT_IDENTITY_HASH_SIZE + 6))
+					return -1;
+				_t = ZEROTIER;
+				_v.zt.a = ((uint64_t)data[1]) << 32U;
+				_v.zt.a |= ((uint64_t)data[2]) << 24U;
+				_v.zt.a |= ((uint64_t)data[3]) << 16U;
+				_v.zt.a |= ((uint64_t)data[4]) << 8U;
+				_v.zt.a |= (uint64_t)data[5];
+				memcpy(_v.zt.idh,data + 6,ZT_IDENTITY_HASH_SIZE);
+				return (ZT_IDENTITY_HASH_SIZE + 6);
+			case URL:
+				if (len < 2)
+					return -1;
+				_t = URL;
+				p = 1;
+				for (;;) {
+					if ((_v.url[p-1] = (char)data[p]) == 0) {
+						++p;
+						break;
+					}
+					++p;
+					if ((p >= (ZT_ENDPOINT_MAX_NAME_SIZE+1))||(p >= len))
+						return -1;
+				}
+				return p;
+			case ETHERNET:
+				if (len < 7)
+					return -1;
+				_t = ZEROTIER;
+				_v.eth = ((uint64_t)data[1]) << 40U;
+				_v.eth |= ((uint64_t)data[2]) << 32U;
+				_v.eth |= ((uint64_t)data[3]) << 24U;
+				_v.eth |= ((uint64_t)data[4]) << 16U;
+				_v.eth |= ((uint64_t)data[5]) << 8U;
+				_v.eth |= (uint64_t)data[6];
+				return 7;
+		}
+		return false;
+	}
+	////////////////////////////////////////////////////////////////////////////
+
+private:
+	Type _t;
+	union {
+		struct sockaddr_storage sa;
+		struct {
+			char name[ZT_ENDPOINT_MAX_NAME_SIZE];
+			uint16_t port;
+		} dns;
+		struct {
+			uint64_t a;
+			uint8_t idh[ZT_IDENTITY_HASH_SIZE];
+		} zt;
+		char url[ZT_ENDPOINT_MAX_NAME_SIZE];
+		uint64_t eth;
+	} _v;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 16 - 16
node/Hashtable.hpp

@@ -94,7 +94,7 @@ public:
 	/**
 	 * @param bc Initial capacity in buckets (default: 32, must be nonzero)
 	 */
-	ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) :
+	inline Hashtable(unsigned long bc = 32) :
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
 		_bc(bc),
 		_s(0)
@@ -105,7 +105,7 @@ public:
 			_t[i] = (_Bucket *)0;
 	}
 
-	ZT_ALWAYS_INLINE Hashtable(const Hashtable<K,V> &ht) :
+	inline Hashtable(const Hashtable<K,V> &ht) :
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
 		_bc(ht._bc),
 		_s(ht._s)
@@ -125,13 +125,13 @@ public:
 		}
 	}
 
-	ZT_ALWAYS_INLINE ~Hashtable()
+	inline ~Hashtable()
 	{
 		this->clear();
 		::free(_t);
 	}
 
-	ZT_ALWAYS_INLINE Hashtable &operator=(const Hashtable<K,V> &ht)
+	inline Hashtable &operator=(const Hashtable<K,V> &ht)
 	{
 		this->clear();
 		if (ht._s) {
@@ -149,7 +149,7 @@ public:
 	/**
 	 * Erase all entries
 	 */
-	ZT_ALWAYS_INLINE void clear()
+	inline void clear()
 	{
 		if (_s) {
 			for(unsigned long i=0;i<_bc;++i) {
@@ -168,7 +168,7 @@ public:
 	/**
 	 * @return Vector of all keys
 	 */
-	ZT_ALWAYS_INLINE typename std::vector<K> keys() const
+	inline typename std::vector<K> keys() const
 	{
 		typename std::vector<K> k;
 		if (_s) {
@@ -191,7 +191,7 @@ public:
 	 * @tparam Type of V (generally inferred)
 	 */
 	template<typename C>
-	ZT_ALWAYS_INLINE void appendKeys(C &v) const
+	inline void appendKeys(C &v) const
 	{
 		if (_s) {
 			for(unsigned long i=0;i<_bc;++i) {
@@ -207,7 +207,7 @@ public:
 	/**
 	 * @return Vector of all entries (pairs of K,V)
 	 */
-	ZT_ALWAYS_INLINE typename std::vector< std::pair<K,V> > entries() const
+	inline typename std::vector< std::pair<K,V> > entries() const
 	{
 		typename std::vector< std::pair<K,V> > k;
 		if (_s) {
@@ -227,7 +227,7 @@ public:
 	 * @param k Key
 	 * @return Pointer to value or NULL if not found
 	 */
-	ZT_ALWAYS_INLINE V *get(const K k)
+	inline V *get(const K k)
 	{
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
@@ -237,14 +237,14 @@ public:
 		}
 		return (V *)0;
 	}
-	ZT_ALWAYS_INLINE const V *get(const K k) const { return const_cast<Hashtable *>(this)->get(k); }
+	inline const V *get(const K k) const { return const_cast<Hashtable *>(this)->get(k); }
 
 	/**
 	 * @param k Key
 	 * @param v Value to fill with result
 	 * @return True if value was found and set (if false, v is not modified)
 	 */
-	ZT_ALWAYS_INLINE bool get(const K &k,V &v) const
+	inline bool get(const K &k,V &v) const
 	{
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
@@ -261,7 +261,7 @@ public:
 	 * @param k Key to check
 	 * @return True if key is present
 	 */
-	ZT_ALWAYS_INLINE bool contains(const K &k) const
+	inline bool contains(const K &k) const
 	{
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
@@ -276,7 +276,7 @@ public:
 	 * @param k Key
 	 * @return True if value was present
 	 */
-	ZT_ALWAYS_INLINE bool erase(const K &k)
+	inline bool erase(const K &k)
 	{
 		const unsigned long bidx = _hc(k) % _bc;
 		_Bucket *lastb = (_Bucket *)0;
@@ -301,7 +301,7 @@ public:
 	 * @param v Value
 	 * @return Reference to value in table
 	 */
-	ZT_ALWAYS_INLINE V &set(const K &k,const V &v)
+	inline V &set(const K &k,const V &v)
 	{
 		const unsigned long h = _hc(k);
 		unsigned long bidx = h % _bc;
@@ -331,7 +331,7 @@ public:
 	 * @param k Key
 	 * @return Value, possibly newly created
 	 */
-	ZT_ALWAYS_INLINE V &operator[](const K k)
+	inline V &operator[](const K k)
 	{
 		const unsigned long h = _hc(k);
 		unsigned long bidx = h % _bc;
@@ -376,7 +376,7 @@ private:
 	static ZT_ALWAYS_INLINE unsigned long _hc(void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); }
 	static ZT_ALWAYS_INLINE unsigned long _hc(const void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); }
 
-	ZT_ALWAYS_INLINE void _grow()
+	inline void _grow()
 	{
 		const unsigned long nc = _bc * 2;
 		_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));

+ 212 - 8
node/Identity.cpp

@@ -11,10 +11,8 @@
  */
 /****/
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
+#include <cstring>
+#include <cstdint>
 
 #include "Constants.hpp"
 #include "Identity.hpp"
@@ -100,8 +98,13 @@ void Identity::generate(const Type t)
 	delete [] genmem;
 
 	if (t == P384) {
+		// We sign with both because in pure FIPS environments we might have to say
+		// that we do not rely on any non-FIPS algorithms, or may even have to disable
+		// them.
 		ECC384GenerateKey(_pub.p384,_priv.p384);
-		C25519::sign(_priv.c25519,_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.p384s);
+		C25519::sign(_priv.c25519,_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.c25519s);
+		SHA384(digest,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE);
+		ECC384ECDSASign(_priv.p384,digest,_pub.p384s);
 	}
 }
 
@@ -116,7 +119,10 @@ bool Identity::locallyValidate() const
 		case C25519:
 			break;
 		case P384:
-			if (!C25519::verify(_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.p384s,ZT_C25519_SIGNATURE_LEN))
+			if (!C25519::verify(_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.c25519s,ZT_C25519_SIGNATURE_LEN))
+				return false;
+			SHA384(digest,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE);
+			if (!ECC384ECDSAVerify(_pub.p384,digest,_pub.p384s))
 				return false;
 		default:
 			return false;
@@ -135,6 +141,108 @@ bool Identity::locallyValidate() const
 	return false;
 }
 
+bool Identity::hash(uint8_t h[48],const bool includePrivate) const
+{
+	switch(_type) {
+
+	case C25519:
+		if ((_hasPrivate)&&(includePrivate))
+			SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+		else SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+		return true;
+
+	case P384:
+		if ((_hasPrivate)&&(includePrivate))
+			SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv));
+		else SHA384(h,&_pub,sizeof(_pub));
+		return true;
+
+	}
+	return false;
+}
+
+unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
+{
+	if (_hasPrivate) {
+		switch(_type) {
+
+			case C25519:
+				if (siglen >= ZT_C25519_SIGNATURE_LEN) {
+					C25519::sign(_priv.c25519,_pub.c25519,data,len,sig);
+					return ZT_C25519_SIGNATURE_LEN;
+				}
+
+			case P384:
+				if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
+					// When signing with P384 we also hash the C25519 public key as an
+					// extra measure to ensure that only this identity can verify.
+					uint8_t h[48];
+					SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+					ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
+					return ZT_ECC384_SIGNATURE_SIZE;
+				}
+
+		}
+	}
+	return 0;
+}
+
+bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
+{
+	switch(_type) {
+
+		case C25519:
+			return C25519::verify(_pub.c25519,data,len,sig,siglen);
+
+		case P384:
+			if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
+				uint8_t h[48];
+				SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
+			}
+			break;
+
+	}
+	return false;
+}
+
+bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
+{
+	uint8_t rawkey[128];
+	uint8_t h[64];
+	if (_hasPrivate) {
+		if (_type == C25519) {
+
+			if ((id._type == C25519)||(id._type == P384)) {
+				// If we are a C25519 key we can agree with another C25519 key or with only the
+				// C25519 portion of a type 1 P-384 key.
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			}
+
+		} else if (_type == P384) {
+
+			if (id._type == P384) {
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
+				SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			} else if (id._type == C25519) {
+				// If the other identity is a C25519 identity we can agree using only that type.
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			}
+
+		}
+	}
+	return false;
+}
+
 char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
 {
 	switch(_type) {
@@ -224,7 +332,7 @@ bool Identity::fromString(const char *str)
 				switch(_type) {
 
 					case C25519:
-						if (Utils::unhex(f,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
+						if (Utils::unhex(f,strlen(f),_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) {
 							_address.zero();
 							return false;
 						}
@@ -245,7 +353,7 @@ bool Identity::fromString(const char *str)
 					switch(_type) {
 
 						case C25519:
-							if (Utils::unhex(f,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
+							if (Utils::unhex(f,strlen(f),_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) {
 								_address.zero();
 								return false;
 							} else {
@@ -278,3 +386,99 @@ bool Identity::fromString(const char *str)
 }
 
 } // namespace ZeroTier
+
+extern "C" {
+
+ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type)
+{
+	if ((type != ZT_IDENTITY_TYPE_C25519)&&(type != ZT_IDENTITY_TYPE_P384))
+		return nullptr;
+	try {
+		ZeroTier::Identity *id = new ZeroTier::Identity();
+		id->generate((ZeroTier::Identity::Type)type);
+		return reinterpret_cast<ZT_Identity *>(id);
+	} catch ( ... ) {
+		return nullptr;
+	}
+}
+
+ZT_Identity *ZT_Identity_fromString(const char *idStr)
+{
+	if (!idStr)
+		return nullptr;
+	try {
+		ZeroTier::Identity *id = new ZeroTier::Identity();
+		if (!id->fromString(idStr)) {
+			delete id;
+			return nullptr;
+		}
+		return reinterpret_cast<ZT_Identity *>(id);
+	} catch ( ... ) {
+		return nullptr;
+	}
+}
+
+int ZT_Identity_validate(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->locallyValidate() ? 1 : 0;
+}
+
+unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength)
+{
+	if (!id)
+		return 0;
+	if (signatureBufferLength < ZT_SIGNATURE_BUFFER_SIZE)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->sign(data,len,signature,signatureBufferLength);
+}
+
+int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen)
+{
+	if ((!id)||(!signature)||(!sigLen))
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->verify(data,len,signature,sigLen) ? 1 : 0;
+}
+
+enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id)
+{
+	if (!id)
+		return (ZT_Identity_Type)0;
+	return (enum ZT_Identity_Type)reinterpret_cast<const ZeroTier::Identity *>(id)->type();
+}
+
+char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate)
+{
+	if ((!id)||(!buf)||(capacity < ZT_IDENTITY_STRING_BUFFER_LENGTH))
+		return nullptr;
+	reinterpret_cast<const ZeroTier::Identity *>(id)->toString(includePrivate != 0,buf);
+	return buf;
+}
+
+int ZT_Identity_hasPrivate(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->hasPrivate() ? 1 : 0;
+}
+
+uint64_t ZT_Identity_address(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->address().toInt();
+}
+
+void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate)
+{
+	reinterpret_cast<const ZeroTier::Identity *>(id)->hash(h,includePrivate != 0);
+}
+
+ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id)
+{
+	if (id)
+		delete reinterpret_cast<ZeroTier::Identity *>(id);
+}
+
+}

+ 129 - 174
node/Identity.hpp

@@ -14,8 +14,8 @@
 #ifndef ZT_IDENTITY_HPP
 #define ZT_IDENTITY_HPP
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <cstdio>
+#include <cstdlib>
 
 #include "Constants.hpp"
 #include "Utils.hpp"
@@ -27,6 +27,11 @@
 
 #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
 
+#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE)
+#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)
+
+#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE)
+
 namespace ZeroTier {
 
 /**
@@ -52,15 +57,17 @@ public:
 	};
 
 	ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
-	ZT_ALWAYS_INLINE Identity(const char *str)
-	{
-		if (!fromString(str))
-			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE;
-	}
-	template<unsigned int C>
-	ZT_ALWAYS_INLINE Identity(const Buffer<C> &b,unsigned int startAt = 0) { deserialize(b,startAt); }
+	ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(&this->_priv),sizeof(this->_priv)); }
 
-	ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast<void *>(this),sizeof(Identity)); }
+	/**
+	 * Construct identity from string
+	 *
+	 * If the identity is not basically valid (no deep checking is done) the result will
+	 * be a null identity.
+	 *
+	 * @param str Identity in canonical string format
+	 */
+	explicit ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); }
 
 	/**
 	 * Set identity to NIL value (all zero)
@@ -68,18 +75,18 @@ public:
 	ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
 
 	/**
-	 * @return Identity type
+	 * @return Identity type (undefined if identity is null or invalid)
 	 */
 	ZT_ALWAYS_INLINE Type type() const { return _type; }
 
 	/**
 	 * Generate a new identity (address, key pair)
 	 *
-	 * This is a time consuming operation.
+	 * This is a time consuming operation taking up to 5-10 seconds on some slower systems.
 	 *
 	 * @param t Type of identity to generate
 	 */
-	void generate(const Type t);
+	void generate(Type t);
 
 	/**
 	 * Check the validity of this identity's pairing of key to address
@@ -94,41 +101,12 @@ public:
 	ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; }
 
 	/**
-	 * Compute the SHA512 hash of our private key (if we have one)
+	 * This generates a SHA384 hash of this identity's keys.
 	 *
-	 * @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length)
-	 * @return True on success, false if no private key
-	 */
-	ZT_ALWAYS_INLINE bool sha512PrivateKey(void *const sha) const
-	{
-		if (_hasPrivate) {
-			switch(_type) {
-				case C25519:
-					SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
-					return true;
-				case P384:
-					SHA512(sha,&_priv,sizeof(_priv));
-					return true;
-			}
-		}
-		return false;
-	}
-
-	/**
 	 * @param h Buffer to receive SHA384 of public key(s)
+	 * @param includePrivate If true, hash private key(s) as well
 	 */
-	ZT_ALWAYS_INLINE bool hash(uint8_t h[48]) const
-	{
-		switch(_type) {
-			case C25519:
-				SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-				return true;
-			case P384:
-				SHA384(h,&_pub,sizeof(_pub));
-				return true;
-		}
-		return false;
-	}
+	bool hash(uint8_t h[48],bool includePrivate = false) const;
 
 	/**
 	 * Sign a message with this identity (private key required)
@@ -142,31 +120,7 @@ public:
 	 * @param siglen Length of buffer
 	 * @return Number of bytes actually written to sig or 0 on error
 	 */
-	ZT_ALWAYS_INLINE unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
-	{
-		if (_hasPrivate) {
-			switch(_type) {
-
-				case C25519:
-					if (siglen >= ZT_C25519_SIGNATURE_LEN) {
-						C25519::sign(_priv.c25519,_pub.c25519,data,len,sig);
-						return ZT_C25519_SIGNATURE_LEN;
-					}
-
-				case P384:
-					if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
-						// Signature hash includes the C25519/Ed25519 public key after the message.
-						// This is an added guard against divorcing these two bound keys.
-						uint8_t h[48];
-						SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-						ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
-						return ZT_ECC384_SIGNATURE_SIZE;
-					}
-
-			}
-		}
-		return 0;
-	}
+	unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const;
 
 	/**
 	 * Verify a message signature against this identity
@@ -177,21 +131,7 @@ public:
 	 * @param siglen Length of signature in bytes
 	 * @return True if signature validates and data integrity checks
 	 */
-	ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
-	{
-		switch(_type) {
-			case C25519:
-				return C25519::verify(_pub.c25519,data,len,sig,siglen);
-			case P384:
-				if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
-					uint8_t h[48];
-					SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-					return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
-				}
-				break;
-		}
-		return false;
-	}
+	bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const;
 
 	/**
 	 * Shortcut method to perform key agreement with another identity
@@ -202,78 +142,21 @@ public:
 	 * @param key Result parameter to fill with key bytes
 	 * @return Was agreement successful?
 	 */
-	ZT_ALWAYS_INLINE bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
-	{
-		uint8_t rawkey[128];
-		uint8_t h[64];
-		if (_hasPrivate) {
-			if (_type == C25519) {
-				if ((id._type == C25519)||(id._type == P384)) {
-					// If we are a C25519 key we can agree with another C25519 key or with only the
-					// C25519 portion of a type 1 P-384 key.
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				}
-			} else if (_type == P384) {
-				if (id._type == P384) {
-					// Perform key agreement over both curves for the same reason that C25519 public
-					// keys are included in P-384 signature inputs: to bind the keys together so
-					// that a type 1 identity with the same C25519 public key (and therefore address)
-					// but a different P-384 key will not work.
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
-					SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				} else if (id._type == C25519) {
-					// If the other identity is a C25519 identity we can agree using only that type.
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				}
-			}
-		}
-		return false;
-	}
+	bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const;
 
 	/**
 	 * @return This identity's address
 	 */
 	ZT_ALWAYS_INLINE const Address &address() const { return _address; }
 
-	/**
-	 * Attempt to generate an older type identity from a newer type
-	 *
-	 * If this identity has its private key this is not transferred to
-	 * the downgraded identity.
-	 *
-	 * @param dest Destination to fill with downgraded identity
-	 * @param toType Desired identity type
-	 */
-	ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType)
-	{
-		if ((_type == P384)&&(toType == C25519)) {
-			dest._address = _address;
-			dest._type = C25519;
-			dest._hasPrivate = false;
-			memcpy(dest._pub.c25519,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-			return true;
-		}
-		return false;
-	}
-
 	/**
 	 * Serialize this identity (binary)
 	 *
 	 * @param b Destination buffer to append to
 	 * @param includePrivate If true, include private key component (if present) (default: false)
-	 * @throws std::out_of_range Buffer too small
 	 */
 	template<unsigned int C>
-	ZT_ALWAYS_INLINE void serialize(Buffer<C> &b,bool includePrivate = false) const
+	inline void serialize(Buffer<C> &b,bool includePrivate = false) const
 	{
 		_address.appendTo(b);
 		switch(_type) {
@@ -291,9 +174,7 @@ public:
 
 			case P384:
 				b.append((uint8_t)P384);
-				b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-				b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE);
-				b.append(_pub.p384s,ZT_C25519_SIGNATURE_LEN);
+				b.append(&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE);
 				if ((_hasPrivate)&&(includePrivate)) {
 					b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE));
 					b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
@@ -301,7 +182,7 @@ public:
 				} else {
 					b.append((uint8_t)0);
 				}
-				b.append((uint16_t)0); // size of additional fields
+				b.append((uint8_t)0); // size of additional fields (should have included such a thing in v0!)
 				break;
 
 		}
@@ -316,11 +197,9 @@ public:
 	 * @param b Buffer containing serialized data
 	 * @param startAt Index within buffer of serialized data (default: 0)
 	 * @return Length of serialized data read from buffer
-	 * @throws std::out_of_range Serialized data invalid
-	 * @throws std::invalid_argument Serialized data invalid
 	 */
 	template<unsigned int C>
-	ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
 	{
 		_hasPrivate = false;
 		unsigned int p = startAt;
@@ -347,12 +226,8 @@ public:
 				break;
 
 			case P384:
-				memcpy(_pub.c25519,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN);
-				p += ZT_C25519_PUBLIC_KEY_LEN;
-				memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE);
-				p += ZT_ECC384_PUBLIC_KEY_SIZE;
-				memcpy(_pub.p384s,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
-				p += ZT_ECC384_SIGNATURE_SIZE;
+				memcpy(&_pub,b.field(p,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE);
+				p += ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE;
 				pkl = (unsigned int)b[p++];
 				if (pkl) {
 					if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE))
@@ -365,7 +240,7 @@ public:
 				} else {
 					_hasPrivate = false;
 				}
-				p += b.template at<uint16_t>(p) + 2;
+				p += b.template at<uint8_t>(p) + 2;
 				break;
 
 			default:
@@ -381,7 +256,7 @@ public:
 	 *
 	 * @param includePrivate If true, include private key (if it exists)
 	 * @param buf Buffer to store string
-	 * @return ASCII string representation of identity
+	 * @return ASCII string representation of identity (pointer to buf)
 	 */
 	char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
 
@@ -399,18 +274,15 @@ public:
 	/**
 	 * @return True if this identity contains something
 	 */
-	ZT_ALWAYS_INLINE operator bool() const { return (_address); }
+	explicit ZT_ALWAYS_INLINE operator bool() const { return (_address); }
 
 	ZT_ALWAYS_INLINE bool operator==(const Identity &id) const
 	{
 		if ((_address == id._address)&&(_type == id._type)) {
 			switch(_type) {
-				case C25519:
-					return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
-				case P384:
-					return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
-				default:
-					return false;
+				case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0);
+				// case P384:
+				default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0);
 			}
 		}
 		return false;
@@ -424,10 +296,9 @@ public:
 				return true;
 			if (_type == id._type) {
 				switch(_type) {
-					case C25519:
-						return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
-					case P384:
-						return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
+					case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0);
+					// case P384:
+					default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0);
 				}
 			}
 		}
@@ -440,18 +311,102 @@ public:
 
 	ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); }
 
+	// Marshal interface ///////////////////////////////////////////////////////
+	static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; }
+	inline int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const
+	{
+		_address.copyTo(data,ZT_ADDRESS_LENGTH);
+		switch(_type) {
+
+			case C25519:
+				data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				if ((includePrivate)&&(_hasPrivate)) {
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN;
+					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+				}
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+
+			case P384:
+				data[ZT_ADDRESS_LENGTH] = (uint8_t)P384;
+				memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+				if ((includePrivate)&&(_hasPrivate)) {
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE;
+					memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+					data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1);
+				}
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0;
+				data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1] = 0;
+				return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+
+		}
+		return -1;
+	}
+	inline int unmarshal(const uint8_t *restrict data,const int len)
+	{
+		if (len < (ZT_ADDRESS_LENGTH + 1))
+			return -1;
+		unsigned int privlen;
+		switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) {
+
+			case C25519:
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1))
+					return -1;
+				memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN);
+				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN];
+				if (privlen == ZT_C25519_PRIVATE_KEY_LEN) {
+					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN))
+						return -1;
+					_hasPrivate = true;
+					memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN);
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN);
+				} else if (privlen == 0) {
+					_hasPrivate = false;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1);
+				}
+				break;
+
+			case P384:
+				if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2))
+					return -1;
+				memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE);
+				privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE];
+				if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) {
+					if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))
+						return -1;
+					_hasPrivate = true;
+					memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE);
+					privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE];
+					if (len < (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)))
+						return -1;
+					return (int)(privlen + (unsigned int)(ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
+				} else if (privlen == 0) {
+					_hasPrivate = false;
+					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);
+				}
+				break;
+
+		}
+		return -1;
+	}
+	////////////////////////////////////////////////////////////////////////////
+
 private:
 	Address _address;
-	Type _type;
+	Type _type; // _type determines which fields in _priv and _pub are used
 	bool _hasPrivate;
 	ZT_PACKED_STRUCT(struct { // don't re-order these
 		uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
 		uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
 	}) _priv;
 	ZT_PACKED_STRUCT(struct { // don't re-order these
-		uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN];
-		uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE];
-		uint8_t p384s[ZT_C25519_SIGNATURE_LEN]; // signature of both keys with ed25519 to confirm type 0 extension to type 1
+		uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys
+		uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE];  // NIST P-384 public key
+		uint8_t c25519s[ZT_C25519_SIGNATURE_LEN]; // signature of both keys with ed25519
+		uint8_t p384s[ZT_ECC384_SIGNATURE_SIZE];  // signature of both keys with p384
 	}) _pub;
 };
 

+ 332 - 594
node/IncomingPacket.cpp

@@ -29,238 +29,49 @@
 #include "Revocation.hpp"
 #include "Trace.hpp"
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
+#include <cstring>
+#include <cstdlib>
 
 #include <list>
 
 namespace ZeroTier {
 
-bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
-{
-	const Address sourceAddress(source());
-
-	try {
-		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
-		const unsigned int c = cipher();
-		bool trusted = false;
-		if (c == ZT_PROTO_CIPHER_SUITE__NONE) {
-			// If this is marked as a packet via a trusted path, check source address and path ID.
-			// Obviously if no trusted paths are configured this always returns false and such
-			// packets are dropped on the floor.
-			const uint64_t tpid = trustedPathId();
-			if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
-				trusted = true;
-			} else {
-				RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
-				return true;
-			}
-		} else if ((c == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
-			// Only HELLO is allowed in the clear, but will still have a MAC
-			return _doHELLO(RR,tPtr,false);
-		}
-
-		const SharedPtr<Peer> peer(RR->topology->get(sourceAddress));
-		if (peer) {
-			if (!trusted) {
-				if (!dearmor(peer->key())) {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
-					_path->recordInvalidPacket();
-					return true;
-				}
-			}
-
-			if (!uncompress()) {
-				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
-				return true;
-			}
-
-			const Packet::Verb v = verb();
-			bool r = true;
-			switch(v) {
-				//case Packet::VERB_NOP:
-				default: // ignore unknown verbs, but if they pass auth check they are "received"
-					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,0);
-					break;
-				case Packet::VERB_HELLO:                      r = _doHELLO(RR,tPtr,true); break;
-				case Packet::VERB_ACK:                        r = _doACK(RR,tPtr,peer); break;
-				case Packet::VERB_QOS_MEASUREMENT:            r = _doQOS_MEASUREMENT(RR,tPtr,peer); break;
-				case Packet::VERB_ERROR:                      r = _doERROR(RR,tPtr,peer); break;
-				case Packet::VERB_OK:                         r = _doOK(RR,tPtr,peer); break;
-				case Packet::VERB_WHOIS:                      r = _doWHOIS(RR,tPtr,peer); break;
-				case Packet::VERB_RENDEZVOUS:                 r = _doRENDEZVOUS(RR,tPtr,peer); break;
-				case Packet::VERB_FRAME:                      r = _doFRAME(RR,tPtr,peer); break;
-				case Packet::VERB_EXT_FRAME:                  r = _doEXT_FRAME(RR,tPtr,peer); break;
-				case Packet::VERB_ECHO:                       r = _doECHO(RR,tPtr,peer); break;
-				case Packet::VERB_MULTICAST_LIKE:             r = _doMULTICAST_LIKE(RR,tPtr,peer); break;
-				case Packet::VERB_NETWORK_CREDENTIALS:        r = _doNETWORK_CREDENTIALS(RR,tPtr,peer); break;
-				case Packet::VERB_NETWORK_CONFIG_REQUEST:     r = _doNETWORK_CONFIG_REQUEST(RR,tPtr,peer); break;
-				case Packet::VERB_NETWORK_CONFIG:             r = _doNETWORK_CONFIG(RR,tPtr,peer); break;
-				case Packet::VERB_MULTICAST_GATHER:           r = _doMULTICAST_GATHER(RR,tPtr,peer); break;
-				case Packet::VERB_MULTICAST_FRAME:            r = _doMULTICAST_FRAME(RR,tPtr,peer); break;
-				case Packet::VERB_PUSH_DIRECT_PATHS:          r = _doPUSH_DIRECT_PATHS(RR,tPtr,peer); break;
-				case Packet::VERB_USER_MESSAGE:               r = _doUSER_MESSAGE(RR,tPtr,peer); break;
-				case Packet::VERB_REMOTE_TRACE:               r = _doREMOTE_TRACE(RR,tPtr,peer); break;
-				case Packet::VERB_SET_LOCATOR: break;
-				case Packet::VERB_WILL_RELAY: break;
-				case Packet::VERB_EPHEMERAL_KEY: break;
-			}
-			return r;
-		} else {
-			RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
-			return false;
-		}
-	} catch (int ztExcCode) {
-		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
-		return true;
-	} catch ( ... ) {
-		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
-		return true;
-	}
-}
-
-bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
-	const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
-	const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
-	const Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
-	uint64_t networkId = 0;
-
-	/* Security note: we do not gate doERROR() with expectingReplyTo() to
-	 * avoid having to log every outgoing packet ID. Instead we put the
-	 * logic to determine whether we should consider an ERROR in each
-	 * error handler. In most cases these are only trusted in specific
-	 * circumstances. */
-
-	switch(errorCode) {
-
-		case Packet::ERROR_OBJ_NOT_FOUND:
-			// Object not found, currently only meaningful from network controllers.
-			if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
-				networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
-				const SharedPtr<Network> network(RR->node->network(networkId));
-				if ((network)&&(network->controller() == peer->address()))
-					network->setNotFound();
-			}
-			break;
-
-		case Packet::ERROR_UNSUPPORTED_OPERATION:
-			// This can be sent in response to any operation, though right now we only
-			// consider it meaningful from network controllers. This would indicate
-			// that the queried node does not support acting as a controller.
-			if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
-				networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
-				const SharedPtr<Network> network(RR->node->network(networkId));
-				if ((network)&&(network->controller() == peer->address()))
-					network->setNotFound();
-			}
-			break;
-
-		case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
-			// Peers can send this to ask for a cert for a network.
-			networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
-			const SharedPtr<Network> network(RR->node->network(networkId));
-			const int64_t now = RR->node->now();
-			if ((network)&&(network->config().com))
-				network->pushCredentialsNow(tPtr,peer->address(),now);
-		}	break;
-
-		case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
-			// Network controller: network access denied.
-			networkId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
-			const SharedPtr<Network> network(RR->node->network(networkId));
-			if ((network)&&(network->controller() == peer->address()))
-				network->setAccessDenied();
-		}	break;
-
-		case Packet::ERROR_MULTICAST_STFU: {
-			// Members of networks can use this error to indicate that they no longer
-			// want to receive multicasts on a given channel.
-			const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
-			RR->mc->remove(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD),mg,peer->address());
-		}	break;
-
-		default: break;
-	}
-
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_ERROR,inRePacketId,inReVerb,networkId);
-
-	return true;
-}
-
-bool IncomingPacket::_doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
-	if (!peer->rateGateACK(RR->node->now()))
-		return true;
-	/* Dissect incoming ACK packet. From this we can estimate current throughput of the path, establish known
-	 * maximums and detect packet loss. */
-	if (peer->localMultipathSupport()) {
-		int32_t ackedBytes;
-		if (payloadLength() != sizeof(ackedBytes)) {
-			return true; // ignore
-		}
-		memcpy(&ackedBytes, payload(), sizeof(ackedBytes));
-		_path->receivedAck(RR->node->now(), Utils::ntoh(ackedBytes));
-		peer->inferRemoteMultipathEnabled();
-	}
-
-	return true;
-}
+namespace {
+//////////////////////////////////////////////////////////////////////////////
+// Implementation of each protocol verb                                     //
+//////////////////////////////////////////////////////////////////////////////
 
-bool IncomingPacket::_doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid,const SharedPtr<Path> &path)
 {
-	if (!peer->rateGateQoS(RR->node->now()))
-		return true;
-
-	/* Dissect incoming QoS packet. From this we can compute latency values and their variance.
-	 * The latency variance is used as a measure of "jitter". */
-	if (peer->localMultipathSupport()) {
-		if (payloadLength() > ZT_PATH_MAX_QOS_PACKET_SZ || payloadLength() < ZT_PATH_MIN_QOS_PACKET_SZ) {
-			return true; // ignore
-		}
-		const int64_t now = RR->node->now();
-		uint64_t rx_id[ZT_PATH_QOS_TABLE_SIZE];
-		uint16_t rx_ts[ZT_PATH_QOS_TABLE_SIZE];
-		char *begin = (char *)payload();
-		char *ptr = begin;
-		int count = 0;
-		int len = payloadLength();
-		// Read packet IDs and latency compensation intervals for each packet tracked by this QoS packet
-		while (ptr < (begin + len) && (count < ZT_PATH_QOS_TABLE_SIZE)) {
-			memcpy((void*)&rx_id[count], ptr, sizeof(uint64_t));
-			ptr+=sizeof(uint64_t);
-			memcpy((void*)&rx_ts[count], ptr, sizeof(uint16_t));
-			ptr+=sizeof(uint16_t);
-			count++;
-		}
-		_path->receivedQoS(now, count, rx_id, rx_ts);
-		peer->inferRemoteMultipathEnabled();
-	}
-
-	return true;
+	Packet outp(pkt.source(),RR->identity.address(),Packet::VERB_ERROR);
+	outp.append((uint8_t)pkt.verb());
+	outp.append(pkt.packetId());
+	outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
+	outp.append(nwid);
+	outp.armor(peer->key(),true);
+	path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 }
 
-bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated)
+ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const bool alreadyAuthenticated,const SharedPtr<Path> &path)
 {
 	const int64_t now = RR->node->now();
 
-	const uint64_t pid = packetId();
-	const Address fromAddress(source());
-	const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
-	const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
-	const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
-	const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
-	const int64_t timestamp = at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
+	const uint64_t pid = pkt.packetId();
+	const Address fromAddress(pkt.source());
+	const unsigned int protoVersion = pkt[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+	const unsigned int vMajor = pkt[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
+	const unsigned int vMinor = pkt[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
+	const unsigned int vRevision = pkt.at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
+	const int64_t timestamp = pkt.at<int64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
 	Identity id;
-	unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
+	unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
 
 	if (protoVersion < ZT_PROTO_VERSION_MIN) {
-		RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old");
+		RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"protocol version too old");
 		return true;
 	}
 	if (fromAddress != id.address()) {
-		RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"identity/address mismatch");
+		RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"identity/address mismatch");
 		return true;
 	}
 
@@ -272,32 +83,32 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 				// Identity is different from the one we already have -- address collision
 
 				// Check rate limits
-				if (!RR->node->rateGateIdentityVerification(now,_path->address()))
+				if (!RR->node->rateGateIdentityVerification(now,path->address()))
 					return true;
 
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 				if (RR->identity.agree(id,key)) {
-					if (dearmor(key)) { // ensure packet is authentic, otherwise drop
-						RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision");
+					if (pkt.dearmor(key)) { // ensure packet is authentic, otherwise drop
+						RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"address collision");
 						Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
 						outp.append((uint8_t)Packet::VERB_HELLO);
 						outp.append((uint64_t)pid);
 						outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
 						outp.armor(key,true);
-						_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+						path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 					} else {
-						RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+						RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
 					}
 				} else {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity");
+					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid identity");
 				}
 
 				return true;
 			} else {
 				// Identity is the same as the one we already have -- check packet integrity
 
-				if (!dearmor(peer->key())) {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+				if (!pkt.dearmor(peer->key())) {
+					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
 					return true;
 				}
 
@@ -309,26 +120,26 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 
 		// Sanity check: this basically can't happen
 		if (alreadyAuthenticated) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state");
+			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"illegal alreadyAuthenticated state");
 			return true;
 		}
 
 		// Check rate limits
-		if (!RR->node->rateGateIdentityVerification(now,_path->address())) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"rate limit exceeded");
+		if (!RR->node->rateGateIdentityVerification(now,path->address())) {
+			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"rate limit exceeded");
 			return true;
 		}
 
 		// Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
 		SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
-		if (!dearmor(newPeer->key())) {
-			RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+		if (!pkt.dearmor(newPeer->key())) {
+			RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
 			return true;
 		}
 
 		// Check that identity's address is valid as per the derivation function
 		if (!id.locallyValidate()) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity");
+			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"invalid identity");
 			return true;
 		}
 
@@ -340,12 +151,12 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 	// VALID -- if we made it here, packet passed identity and authenticity checks!
 
 	// Get address to which this packet was sent to learn our external surface address if packet was direct.
-	if (hops() == 0) {
+	if (pkt.hops() == 0) {
 		InetAddress externalSurfaceAddress;
-		if (ptr < size()) {
-			ptr += externalSurfaceAddress.deserialize(*this,ptr);
-			if ((externalSurfaceAddress)&&(hops() == 0))
-				RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now);
+		if (ptr < pkt.size()) {
+			ptr += externalSurfaceAddress.deserialize(pkt,ptr);
+			if ((externalSurfaceAddress)&&(pkt.hops() == 0))
+				RR->sa->iam(tPtr,id.address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now);
 		}
 	}
 
@@ -360,20 +171,82 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
 	outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
-	_path->address().serialize(outp);
+	path->address().serialize(outp);
 	outp.armor(peer->key(),true);
-	_path->send(RR,tPtr,outp.data(),outp.size(),now);
+	path->send(RR,tPtr,outp.data(),outp.size(),now);
 
 	peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
-	peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_HELLO,0,Packet::VERB_NOP,0);
+	peer->received(tPtr,path,pkt.hops(),pid,pkt.payloadLength(),Packet::VERB_HELLO,0,Packet::VERB_NOP,0);
+
+	return true;
+}
+
+ZT_ALWAYS_INLINE bool _doERROR(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+{
+	const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
+	const uint64_t inRePacketId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
+	const Packet::ErrorCode errorCode = (Packet::ErrorCode)pkt[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
+	uint64_t networkId = 0;
+
+	/* Security note: we do not gate doERROR() with expectingReplyTo() to
+	 * avoid having to log every outgoing packet ID. Instead we put the
+	 * logic to determine whether we should consider an ERROR in each
+	 * error handler. In most cases these are only trusted in specific
+	 * circumstances. */
+
+	switch(errorCode) {
+
+		case Packet::ERROR_OBJ_NOT_FOUND:
+			// Object not found, currently only meaningful from network controllers.
+			if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+				networkId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+				const SharedPtr<Network> network(RR->node->network(networkId));
+				if ((network)&&(network->controller() == peer->address()))
+					network->setNotFound();
+			}
+			break;
+
+		case Packet::ERROR_UNSUPPORTED_OPERATION:
+			// This can be sent in response to any operation, though right now we only
+			// consider it meaningful from network controllers. This would indicate
+			// that the queried node does not support acting as a controller.
+			if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
+				networkId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+				const SharedPtr<Network> network(RR->node->network(networkId));
+				if ((network)&&(network->controller() == peer->address()))
+					network->setNotFound();
+			}
+			break;
+
+		case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
+			// Peers can send this to ask for a cert for a network.
+			networkId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+			const SharedPtr<Network> network(RR->node->network(networkId));
+			const int64_t now = RR->node->now();
+			if ((network)&&(network->config().com))
+				network->pushCredentialsNow(tPtr,peer->address(),now);
+		}	break;
+
+		case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
+			// Network controller: network access denied.
+			networkId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+			const SharedPtr<Network> network(RR->node->network(networkId));
+			if ((network)&&(network->controller() == peer->address()))
+				network->setAccessDenied();
+		}	break;
+
+		default: break;
+	}
+
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_ERROR,inRePacketId,inReVerb,networkId);
 
 	return true;
 }
 
-bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
-	const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
+	const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
+	const uint64_t inRePacketId = pkt.at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
 	uint64_t networkId = 0;
 
 	if (!RR->node->expectingReplyTo(inRePacketId))
@@ -382,34 +255,34 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 	switch(inReVerb) {
 
 		case Packet::VERB_HELLO: {
-			const uint64_t latency = RR->node->now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
-			const unsigned int vProto = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
-			const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
-			const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
-			const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+			const uint64_t latency = RR->node->now() - pkt.at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP);
+			const unsigned int vProto = pkt[ZT_PROTO_VERB_HELLO__OK__IDX_PROTOCOL_VERSION];
+			const unsigned int vMajor = pkt[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION];
+			const unsigned int vMinor = pkt[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
+			const unsigned int vRevision = pkt.at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
 			if (vProto < ZT_PROTO_VERSION_MIN)
 				return true;
 
-			if (hops() == 0) {
-				_path->updateLatency((unsigned int)latency,RR->node->now());
-				if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < size()) {
+			if (pkt.hops() == 0) {
+				if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < pkt.size()) {
 					InetAddress externalSurfaceAddress;
-					externalSurfaceAddress.deserialize(*this,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2);
+					externalSurfaceAddress.deserialize(pkt,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2);
 					if (externalSurfaceAddress)
-						RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now());
+						RR->sa->iam(tPtr,peer->address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now());
 				}
 			}
 
+			peer->updateLatency((unsigned int)latency);
 			peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 		}	break;
 
 		case Packet::VERB_WHOIS:
 			if (RR->topology->isRoot(peer->identity())) {
 				unsigned int p = ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY;
-				while (p < size()) {
+				while (p < pkt.size()) {
 					try {
 						Identity id;
-						p += id.deserialize(*this,p);
+						p += id.deserialize(pkt,p);
 						if (id)
 							RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
 					} catch ( ... ) {
@@ -420,47 +293,36 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 			break;
 
 		case Packet::VERB_NETWORK_CONFIG_REQUEST: {
-			networkId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+			networkId = pkt.at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD);
 			const SharedPtr<Network> network(RR->node->network(networkId));
 			if (network)
-				network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+				network->handleConfigChunk(tPtr,pkt.packetId(),pkt.source(),pkt,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
 		}	break;
 
 		case Packet::VERB_MULTICAST_GATHER: {
-			// TODO
-			/*
-			networkId = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
-			const SharedPtr<Network> network(RR->node->network(networkId));
-			if (network) {
-				const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
-				const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
-				if (((ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6) + (count * 5)) <= size())
-					RR->mc->addMultiple(tPtr,RR->node->now(),networkId,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
-			}
-			*/
 		}	break;
 
 		default: break;
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_OK,inRePacketId,inReVerb,networkId);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_OK,inRePacketId,inReVerb,networkId);
 
 	return true;
 }
 
-bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (!peer->rateGateInboundWhoisRequest(RR->node->now()))
 		return true;
 
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	outp.append((unsigned char)Packet::VERB_WHOIS);
-	outp.append(packetId());
+	outp.append(pkt.packetId());
 
 	unsigned int count = 0;
 	unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
-	while ((ptr + ZT_ADDRESS_LENGTH) <= size()) {
-		const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+	while ((ptr + ZT_ADDRESS_LENGTH) <= pkt.size()) {
+		const Address addr(pkt.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		ptr += ZT_ADDRESS_LENGTH;
 
 		const Identity id(RR->topology->getIdentity(tPtr,addr));
@@ -475,89 +337,90 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
 
 	if (count > 0) {
 		outp.armor(peer->key(),true);
-		_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+		path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,0);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,0);
 
 	return true;
 }
 
-bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (RR->topology->isRoot(peer->identity())) {
-		const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+		uint16_t junk = (uint16_t)Utils::random();
+		const Address with(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		const SharedPtr<Peer> rendezvousWith(RR->topology->get(with));
 		if (rendezvousWith) {
-			const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
-			const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
+			const unsigned int port = pkt.at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
+			const unsigned int addrlen = pkt[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 			if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
-				InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
-				if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) {
-					const uint64_t junk = Utils::random();
-					RR->node->putPacket(tPtr,_path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
-					rendezvousWith->sendHELLO(tPtr,_path->localSocket(),atAddr,RR->node->now());
+				InetAddress atAddr(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
+				if (rendezvousWith->shouldTryPath(tPtr,RR->node->now(),peer,atAddr)) {
+					if (atAddr.isV4())
+						RR->node->putPacket(tPtr,path->localSocket(),atAddr,&junk,2,2); // IPv4 "firewall opener"
+					rendezvousWith->sendHELLO(tPtr,path->localSocket(),atAddr,RR->node->now());
 				}
 			}
 		}
 	}
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,0);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,0);
 	return true;
 }
 
-bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
+	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
 	const SharedPtr<Network> network(RR->node->network(nwid));
 	if (network) {
 		if (network->gate(tPtr,peer)) {
-			if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
-				const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
+			if (pkt.size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
+				const unsigned int etherType = pkt.at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
 				const MAC sourceMac(peer->address(),nwid);
-				const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
-				const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+				const unsigned int frameLen = pkt.size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
+				const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(pkt.data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
 				if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
 					RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 			}
 		} else {
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 		}
 	}
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_FRAME,0,Packet::VERB_NOP,nwid);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_FRAME,0,Packet::VERB_NOP,nwid);
 	return true;
 }
 
-bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
+	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
 	const SharedPtr<Network> network(RR->node->network(nwid));
 	if (network) {
-		const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
+		const unsigned int flags = pkt[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
 
 		unsigned int comLen = 0;
 		if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
 			CertificateOfMembership com;
-			comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
+			comLen = com.deserialize(pkt,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
 			if (com)
 				network->addCredential(tPtr,com);
 		}
 
 		if (!network->gate(tPtr,peer)) {
-			RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true);
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			RR->t->incomingNetworkAccessDenied(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,true);
+			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 		}
 
-		if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
-			const unsigned int etherType = at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
-			const MAC to(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
-			const MAC from(field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
-			const unsigned int frameLen = size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
-			const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
+		if (pkt.size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) {
+			const unsigned int etherType = pkt.at<uint16_t>(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE);
+			const MAC to(pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO);
+			const MAC from(pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM);
+			const unsigned int frameLen = pkt.size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD);
+			const uint8_t *const frameData = (const uint8_t *)pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
 
 			if ((!from)||(from == network->mac())) {
-				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
+				peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 				return true;
 			}
 
@@ -567,20 +430,20 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 						if (network->config().permitsBridging(peer->address())) {
 							network->learnBridgeRoute(from,peer->address());
 						} else {
-							RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
-							peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
+							RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
+							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							return true;
 						}
 					} else if (to != network->mac()) {
 						if (to.isMulticast()) {
 							if (network->config().multicastLimit == 0) {
-								RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
-								peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
+								RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
+								peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 								return true;
 							}
 						} else if (!network->config().permitsBridging(RR->identity.address())) {
-							RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
-							peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
+							RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
+							peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 							return true;
 						}
 					}
@@ -591,73 +454,42 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 			}
 		}
 
-		if ((flags & 0x10) != 0) { // ACK requested
+		if ((flags & 0x10U) != 0) { // ACK requested
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((uint8_t)Packet::VERB_EXT_FRAME);
-			outp.append((uint64_t)packetId());
+			outp.append((uint64_t)pkt.packetId());
 			outp.append((uint64_t)nwid);
 			outp.armor(peer->key(),true);
-			_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+			path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 		}
 
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid);
 	return true;
 }
 
-bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doECHO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (!peer->rateGateEchoRequest(RR->node->now()))
 		return true;
 
-	const uint64_t pid = packetId();
+	const uint64_t pid = pkt.packetId();
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	outp.append((unsigned char)Packet::VERB_ECHO);
 	outp.append((uint64_t)pid);
-	if (size() > ZT_PACKET_IDX_PAYLOAD)
-		outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
+	if (pkt.size() > ZT_PACKET_IDX_PAYLOAD)
+		outp.append(reinterpret_cast<const unsigned char *>(pkt.data()) + ZT_PACKET_IDX_PAYLOAD,pkt.size() - ZT_PACKET_IDX_PAYLOAD);
 	outp.armor(peer->key(),true);
-	_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
-
-	peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,0);
-
-	return true;
-}
-
-bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
-	const int64_t now = RR->node->now();
-	bool authorized = false;
-	uint64_t lastNwid = 0;
+	path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 
-	// Packet contains a series of 18-byte network,MAC,ADI tuples
-	for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;(ptr+18)<=size();ptr+=18) {
-		// TODO
-		/*
-		const uint64_t nwid = at<uint64_t>(ptr);
-		if (nwid != lastNwid) {
-			lastNwid = nwid;
-			SharedPtr<Network> network(RR->node->network(nwid));
-			if (network)
-				authorized = network->gate(tPtr,peer);
-			//if (!authorized)
-			//	authorized = ((RR->topology->amUpstream())||(RR->node->localControllerHasAuthorized(now,nwid,peer->address())));
-		}
-		if (authorized)
-			RR->mc->add(tPtr,now,nwid,MulticastGroup(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14)),peer->address());
-		*/
-	}
+	peer->received(tPtr,path,pkt.hops(),pid,pkt.payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,0);
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,0);
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doNETWORK_CREDENTIALS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	if (!peer->rateGateCredentialsReceived(RR->node->now()))
-		return true;
-
 	CertificateOfMembership com;
 	Capability cap;
 	Tag tag;
@@ -666,8 +498,8 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 	SharedPtr<Network> network;
 
 	unsigned int p = ZT_PACKET_IDX_PAYLOAD;
-	while ((p < size())&&((*this)[p] != 0)) {
-		p += com.deserialize(*this,p);
+	while ((p < pkt.size())&&(pkt[p] != 0)) {
+		p += com.deserialize(pkt,p);
 		if (com) {
 			network = RR->node->network(com.networkId());
 			if (network) {
@@ -678,10 +510,10 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 	}
 	++p; // skip trailing 0 after COMs if present
 
-	if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
-		const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
+	if (p < pkt.size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
+		const unsigned int numCapabilities = pkt.at<uint16_t>(p); p += 2;
 		for(unsigned int i=0;i<numCapabilities;++i) {
-			p += cap.deserialize(*this,p);
+			p += cap.deserialize(pkt,p);
 			if ((!network)||(network->id() != cap.networkId()))
 				network = RR->node->network(cap.networkId());
 			if (network) {
@@ -690,11 +522,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 			}
 		}
 
-		if (p >= size()) return true;
+		if (p >= pkt.size()) return true;
 
-		const unsigned int numTags = at<uint16_t>(p); p += 2;
+		const unsigned int numTags = pkt.at<uint16_t>(p); p += 2;
 		for(unsigned int i=0;i<numTags;++i) {
-			p += tag.deserialize(*this,p);
+			p += tag.deserialize(pkt,p);
 			if ((!network)||(network->id() != tag.networkId()))
 				network = RR->node->network(tag.networkId());
 			if (network) {
@@ -703,11 +535,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 			}
 		}
 
-		if (p >= size()) return true;
+		if (p >= pkt.size()) return true;
 
-		const unsigned int numRevocations = at<uint16_t>(p); p += 2;
+		const unsigned int numRevocations = pkt.at<uint16_t>(p); p += 2;
 		for(unsigned int i=0;i<numRevocations;++i) {
-			p += revocation.deserialize(*this,p);
+			p += revocation.deserialize(pkt,p);
 			if ((!network)||(network->id() != revocation.networkId()))
 				network = RR->node->network(revocation.networkId());
 			if (network) {
@@ -716,11 +548,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 			}
 		}
 
-		if (p >= size()) return true;
+		if (p >= pkt.size()) return true;
 
-		const unsigned int numCoos = at<uint16_t>(p); p += 2;
+		const unsigned int numCoos = pkt.at<uint16_t>(p); p += 2;
 		for(unsigned int i=0;i<numCoos;++i) {
-			p += coo.deserialize(*this,p);
+			p += coo.deserialize(pkt,p);
 			if ((!network)||(network->id() != coo.networkId()))
 				network = RR->node->network(coo.networkId());
 			if (network) {
@@ -730,22 +562,22 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,(network) ? network->id() : 0);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,(network) ? network->id() : 0);
 
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG_REQUEST(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
-	const unsigned int hopCount = hops();
-	const uint64_t requestPacketId = packetId();
+	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+	const unsigned int hopCount = pkt.hops();
+	const uint64_t requestPacketId = pkt.packetId();
 
 	if (RR->localNetworkController) {
-		const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
-		const char *metaDataBytes = (metaDataLength != 0) ? (const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
+		const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= pkt.size()) ? pkt.at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN) : 0;
+		const char *metaDataBytes = (metaDataLength != 0) ? (const char *)pkt.field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,metaDataLength) : (const char *)0;
 		const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> metaData(metaDataBytes,metaDataLength);
-		RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData);
+		RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : path->address(),requestPacketId,peer->identity(),metaData);
 	} else {
 		Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
 		outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
@@ -753,46 +585,46 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void
 		outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
 		outp.append(nwid);
 		outp.armor(peer->key(),true);
-		_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+		path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 	}
 
-	peer->received(tPtr,_path,hopCount,requestPacketId,payloadLength(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,nwid);
+	peer->received(tPtr,path,hopCount,requestPacketId,pkt.payloadLength(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,nwid);
 
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
+	const SharedPtr<Network> network(RR->node->network(pkt.at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
 	if (network) {
-		const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
+		const uint64_t configUpdateId = network->handleConfigChunk(tPtr,pkt.packetId(),pkt.source(),pkt,ZT_PACKET_IDX_PAYLOAD);
 		if (configUpdateId) {
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((uint8_t)Packet::VERB_ECHO);
-			outp.append((uint64_t)packetId());
+			outp.append((uint64_t)pkt.packetId());
 			outp.append((uint64_t)network->id());
 			outp.append((uint64_t)configUpdateId);
 			outp.armor(peer->key(),true);
-			_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+			path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 		}
 	}
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,(network) ? network->id() : 0);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,(network) ? network->id() : 0);
 	return true;
 }
 
-bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doMULTICAST_GATHER(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
-	const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
-	const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
-	const unsigned int gatherLimit = at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
+	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
+	const unsigned int flags = pkt[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
+	const MulticastGroup mg(MAC(pkt.field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),pkt.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
+	const unsigned int gatherLimit = pkt.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
 
 	const SharedPtr<Network> network(RR->node->network(nwid));
 
 	if ((flags & 0x01) != 0) {
 		try {
 			CertificateOfMembership com;
-			com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
+			com.deserialize(pkt,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
 			if ((com)&&(network))
 				network->addCredential(tPtr,com);
 		} catch ( ... ) {} // discard invalid COMs
@@ -800,7 +632,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 
 	if (network) {
 		if (!network->gate(tPtr,peer)) {
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 		}
 	}
@@ -823,242 +655,148 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 		*/
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,nwid);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,nwid);
 
 	return true;
 }
 
-bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
-{
-	unsigned int offset = ZT_PACKET_IDX_PAYLOAD;
-	const uint64_t nwid = at<uint64_t>(offset); offset += 8;
-	const unsigned int flags = (*this)[offset]; ++offset;
-
-	const SharedPtr<Network> network(RR->node->network(nwid));
-	if (network) {
-		if ((flags & 0x01) != 0) {
-			// This is deprecated but may still be sent by old peers
-			CertificateOfMembership com;
-			offset += com.deserialize(*this,offset);
-			if (com)
-				network->addCredential(tPtr,com);
-		}
-
-		if (!network->gate(tPtr,peer)) {
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
-			return false;
-		}
-
-		unsigned int gatherLimit = 0;
-		if ((flags & 0x02) != 0) {
-			gatherLimit = at<uint32_t>(offset); offset += 4;
-		}
-
-		MAC from;
-		if ((flags & 0x04) != 0) {
-			from.setTo(field(offset,6),6); offset += 6;
-		} else {
-			from.fromAddress(peer->address(),nwid);
-		}
-
-		const unsigned int recipientsOffset = offset;
-		std::list<Address> recipients;
-		if ((flags & 0x08) != 0) {
-			const unsigned int rc = at<uint16_t>(offset); offset += 2;
-			for(unsigned int i=0;i<rc;++i) {
-				const Address a(field(offset,5),5);
-				if ((a != peer->address())&&(a != RR->identity.address())) {
-					recipients.push_back(a);
-				}
-				offset += 5;
-			}
-		}
-		const unsigned int afterRecipientsOffset = offset;
-
-		const MulticastGroup to(MAC(field(offset,6),6),at<uint32_t>(offset + 6)); offset += 10;
-		const unsigned int etherType = at<uint16_t>(offset); offset += 2;
-		const unsigned int frameLen = size() - offset;
-
-		if (network->config().multicastLimit == 0) {
-			RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
-			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,nwid);
-			return true;
-		}
-		if (!to.mac().isMulticast()) {
-			RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
-			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,nwid);
-			return true;
-		}
-		if ((!from)||(from.isMulticast())||(from == network->mac())) {
-			RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
-			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,nwid);
-			return true;
-		}
-
-		if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
-			const uint8_t *const frameData = ((const uint8_t *)unsafeData()) + offset;
-			if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) {
-				RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
-			}
-		}
-
-		if (!recipients.empty()) {
-			// TODO
-			/*
-			const std::vector<Address> anchors = network->config().anchors();
-			const bool amAnchor = (std::find(anchors.begin(),anchors.end(),RR->identity.address()) != anchors.end());
-
-			for(std::list<Address>::iterator ra(recipients.begin());ra!=recipients.end();) {
-				SharedPtr<Peer> recipient(RR->topology->get(*ra));
-				if ((recipient)&&((recipient->remoteVersionProtocol() < 10)||(amAnchor))) {
-					Packet outp(*ra,RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
-					outp.append(field(ZT_PACKET_IDX_PAYLOAD,recipientsOffset - ZT_PACKET_IDX_PAYLOAD),recipientsOffset - ZT_PACKET_IDX_PAYLOAD);
-					outp.append(field(afterRecipientsOffset,size() - afterRecipientsOffset),size() - afterRecipientsOffset);
-					RR->sw->send(tPtr,outp,true);
-					recipients.erase(ra++);
-				} else ++ra;
-			}
-
-			if (!recipients.empty()) {
-				Packet outp(recipients.front(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
-				recipients.pop_front();
-				outp.append(field(ZT_PACKET_IDX_PAYLOAD,recipientsOffset - ZT_PACKET_IDX_PAYLOAD),recipientsOffset - ZT_PACKET_IDX_PAYLOAD);
-				if (!recipients.empty()) {
-					outp.append((uint16_t)recipients.size());
-					for(std::list<Address>::iterator ra(recipients.begin());ra!=recipients.end();++ra)
-						ra->appendTo(outp);
-				}
-				outp.append(field(afterRecipientsOffset,size() - afterRecipientsOffset),size() - afterRecipientsOffset);
-				RR->sw->send(tPtr,outp,true);
-			}
-			*/
-		}
-
-		if (gatherLimit) { // DEPRECATED but still supported
-			/*
-			Packet outp(source(),RR->identity.address(),Packet::VERB_OK);
-			outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME);
-			outp.append(packetId());
-			outp.append(nwid);
-			to.mac().appendTo(outp);
-			outp.append((uint32_t)to.adi());
-			outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
-			if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
-				outp.armor(peer->key(),true);
-				_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
-			}
-			*/
-		}
-
-		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,nwid);
-		return true;
-	} else {
-		_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
-		return false;
-	}
-}
-
-bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const int64_t now = RR->node->now();
 
-	// First, subject this to a rate limit
-	if (!peer->rateGatePushDirectPaths(now)) {
-		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0);
-		return true;
-	}
+	if (peer->rateGateInboundPushDirectPaths(now)) {
+		uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
+		memset(countPerScope,0,sizeof(countPerScope));
+
+		unsigned int count = pkt.at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+		unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
+		uint16_t junk = (uint16_t)Utils::random();
+
+		while (count--) {
+			/* unsigned int flags = (*this)[ptr++]; */ ++ptr;
+			unsigned int extLen = pkt.at<uint16_t>(ptr); ptr += 2;
+			ptr += extLen; // unused right now
+			unsigned int addrType = pkt[ptr++];
+			unsigned int addrLen = pkt[ptr++];
+
+			switch(addrType) {
+				case 4: {
+					const InetAddress a(pkt.field(ptr,4),4,pkt.at<uint16_t>(ptr + 4));
+					if (peer->shouldTryPath(tPtr,now,peer,a)) {
+						if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+							RR->node->putPacket(tPtr,path->localSocket(),a,&junk,2,2); // IPv4 "firewall opener"
+							++junk;
+							peer->sendHELLO(tPtr,-1,a,now);
+						}
+					}
+				}	break;
+				case 6: {
+					const InetAddress a(pkt.field(ptr,16),16,pkt.at<uint16_t>(ptr + 16));
+					if (peer->shouldTryPath(tPtr,now,peer,a)) {
+						if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
+							peer->sendHELLO(tPtr,-1,a,now);
+					}
+				}	break;
+			}
 
-	// Second, limit addresses by scope and type
-	uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
-	memset(countPerScope,0,sizeof(countPerScope));
-
-	unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
-	unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
-
-	while (count--) { // if ptr overflows Buffer will throw
-		/* unsigned int flags = (*this)[ptr++]; */ ++ptr;
-		unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
-		ptr += extLen; // unused right now
-		unsigned int addrType = (*this)[ptr++];
-		unsigned int addrLen = (*this)[ptr++];
-
-		switch(addrType) {
-			case 4: {
-				const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
-				if ((!peer->hasActivePathTo(now,a)) && // not already known
-				    (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),-1,a)) ) // should use path
-				{
-					if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
-						peer->sendHELLO(tPtr,-1,a,now);
-				}
-			}	break;
-			case 6: {
-				const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
-				if ((!peer->hasActivePathTo(now,a)) && // not already known
-				    (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),-1,a)) ) // should use path
-				{
-					if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
-						peer->sendHELLO(tPtr,-1,a,now);
-				}
-			}	break;
+			ptr += addrLen;
 		}
-		ptr += addrLen;
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0);
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0);
 
 	return true;
 }
 
-bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+ZT_ALWAYS_INLINE bool _doUSER_MESSAGE(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
-	if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
+	if (likely(pkt.size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
 		ZT_UserMessage um;
 		um.origin = peer->address().toInt();
-		um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
-		um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
-		um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
+		um.typeId = pkt.at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
+		um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(pkt.data()) + ZT_PACKET_IDX_PAYLOAD + 8);
+		um.length = pkt.size() - (ZT_PACKET_IDX_PAYLOAD + 8);
 		RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
 	}
-
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,0);
-
+	peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,0);
 	return true;
 }
 
-bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+//////////////////////////////////////////////////////////////////////////////
+} // anonymous namespace
+
+bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 {
-	ZT_RemoteTrace rt;
-	const char *ptr = reinterpret_cast<const char *>(data()) + ZT_PACKET_IDX_PAYLOAD;
-	const char *const eof = reinterpret_cast<const char *>(data()) + size();
-	rt.origin = peer->address().toInt();
-	rt.data = const_cast<char *>(ptr); // start of first string
-	while (ptr < eof) {
-		if (!*ptr) { // end of string
-			rt.len = (unsigned int)(ptr - rt.data);
-			if ((rt.len > 0)&&(rt.len <= ZT_MAX_REMOTE_TRACE_SIZE)) {
-				RR->node->postEvent(tPtr,ZT_EVENT_REMOTE_TRACE,&rt);
+	const Address sourceAddress(source());
+
+	try {
+		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
+		const unsigned int c = cipher();
+		bool trusted = false;
+		if (c == ZT_PROTO_CIPHER_SUITE__NONE) {
+			// If this is marked as a packet via a trusted path, check source address and path ID.
+			// Obviously if no trusted paths are configured this always returns false and such
+			// packets are dropped on the floor.
+			const uint64_t tpid = trustedPathId();
+			if (RR->topology->shouldInboundPathBeTrusted(_path->address(),tpid)) {
+				trusted = true;
+			} else {
+				RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"path not trusted");
+				return true;
 			}
-			rt.data = const_cast<char *>(++ptr); // start of next string, if any
-		} else {
-			++ptr;
+		} else if ((c == ZT_PROTO_CIPHER_SUITE__POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
+			// Only HELLO is allowed in the clear, but will still have a MAC
+			return _doHELLO(*this,RR,tPtr,false,_path);
 		}
-	}
 
-	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,0);
+		const SharedPtr<Peer> peer(RR->topology->get(sourceAddress));
+		if (peer) {
+			if (!trusted) {
+				if (!dearmor(peer->key())) {
+					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
+					return true;
+				}
+			}
 
-	return true;
-}
+			if (!uncompress()) {
+				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),Packet::VERB_NOP,"LZ4 decompression failed");
+				return true;
+			}
 
-void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid)
-{
-	Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
-	outp.append((uint8_t)verb());
-	outp.append(packetId());
-	outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
-	outp.append(nwid);
-	outp.armor(peer->key(),true);
-	_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
+			const Packet::Verb v = verb();
+			bool r = true;
+			switch(v) {
+				//case Packet::VERB_NOP:
+				default: // ignore unknown verbs, but if they pass auth check they are "received"
+					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,0);
+					break;
+				case Packet::VERB_HELLO:                      r = _doHELLO(*this,RR,tPtr,true,_path);                  break;
+				case Packet::VERB_ERROR:                      r = _doERROR(*this,RR,tPtr,peer,_path);                  break;
+				case Packet::VERB_OK:                         r = _doOK(*this,RR,tPtr,peer,_path);                     break;
+				case Packet::VERB_WHOIS:                      r = _doWHOIS(*this,RR,tPtr,peer,_path);                  break;
+				case Packet::VERB_RENDEZVOUS:                 r = _doRENDEZVOUS(*this,RR,tPtr,peer,_path);             break;
+				case Packet::VERB_FRAME:                      r = _doFRAME(*this,RR,tPtr,peer,_path);                  break;
+				case Packet::VERB_EXT_FRAME:                  r = _doEXT_FRAME(*this,RR,tPtr,peer,_path);              break;
+				case Packet::VERB_ECHO:                       r = _doECHO(*this,RR,tPtr,peer,_path);                   break;
+				case Packet::VERB_NETWORK_CREDENTIALS:        r = _doNETWORK_CREDENTIALS(*this,RR,tPtr,peer,_path);    break;
+				case Packet::VERB_NETWORK_CONFIG_REQUEST:     r = _doNETWORK_CONFIG_REQUEST(*this,RR,tPtr,peer,_path); break;
+				case Packet::VERB_NETWORK_CONFIG:             r = _doNETWORK_CONFIG(*this,RR,tPtr,peer,_path);         break;
+				case Packet::VERB_MULTICAST_GATHER:           r = _doMULTICAST_GATHER(*this,RR,tPtr,peer,_path);       break;
+				case Packet::VERB_PUSH_DIRECT_PATHS:          r = _doPUSH_DIRECT_PATHS(*this,RR,tPtr,peer,_path);      break;
+				case Packet::VERB_USER_MESSAGE:               r = _doUSER_MESSAGE(*this,RR,tPtr,peer,_path);           break;
+			}
+			return r;
+		} else {
+			RR->sw->requestWhois(tPtr,RR->node->now(),sourceAddress);
+			return false;
+		}
+	} catch (int ztExcCode) {
+		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
+		return true;
+	} catch ( ... ) {
+		RR->t->incomingPacketInvalid(tPtr,_path,packetId(),sourceAddress,hops(),verb(),"unexpected exception in tryDecode()");
+		return true;
+	}
 }
 
 } // namespace ZeroTier

+ 1 - 34
node/IncomingPacket.hpp

@@ -14,8 +14,6 @@
 #ifndef ZT_INCOMINGPACKET_HPP
 #define ZT_INCOMINGPACKET_HPP
 
-#include <stdexcept>
-
 #include "Packet.hpp"
 #include "Path.hpp"
 #include "Utils.hpp"
@@ -43,17 +41,10 @@ namespace ZeroTier {
 class RuntimeEnvironment;
 class Network;
 
-/**
- * Subclass of packet that handles the decoding of it
- */
 class IncomingPacket : public Packet
 {
 public:
-	ZT_ALWAYS_INLINE IncomingPacket() :
-		Packet(),
-		_receiveTime(0)
-	{
-	}
+	ZT_ALWAYS_INLINE IncomingPacket() : Packet(),_receiveTime(0),_path() {}
 
 	/**
 	 * Create a new packet-in-decode
@@ -108,30 +99,6 @@ public:
 	ZT_ALWAYS_INLINE uint64_t receiveTime() const { return _receiveTime; }
 
 private:
-	// These are called internally to handle packet contents once it has
-	// been authenticated, decrypted, decompressed, and classified.
-	bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
-	bool _doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-	bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
-
-	void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
-
 	uint64_t _receiveTime;
 	SharedPtr<Path> _path;
 };

+ 61 - 63
node/InetAddress.cpp

@@ -11,11 +11,8 @@
  */
 /****/
 
-#include <stdio.h>
-#include <string.h>
-#include <stdint.h>
-
-#include <string>
+#include <cstring>
+#include <cstdint>
 
 #include "Constants.hpp"
 #include "InetAddress.hpp"
@@ -33,20 +30,20 @@ InetAddress::IpScope InetAddress::ipScope() const
 
 		case AF_INET: {
 			const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
-			switch(ip >> 24) {
+			switch(ip >> 24U) {
 				case 0x00: return IP_SCOPE_NONE;                                   // 0.0.0.0/8 (reserved, never used)
 				case 0x06: return IP_SCOPE_PSEUDOPRIVATE;                          // 6.0.0.0/8 (US Army)
 				case 0x0a: return IP_SCOPE_PRIVATE;                                // 10.0.0.0/8
-				case 0x0b: return IP_SCOPE_PSEUDOPRIVATE;                          // 11.0.0.0/8 (US DoD)
-				case 0x15: return IP_SCOPE_PSEUDOPRIVATE;                          // 21.0.0.0/8 (US DDN-RVN)
-				case 0x16: return IP_SCOPE_PSEUDOPRIVATE;                          // 22.0.0.0/8 (US DISA)
-				case 0x19: return IP_SCOPE_PSEUDOPRIVATE;                          // 25.0.0.0/8 (UK Ministry of Defense)
-				case 0x1a: return IP_SCOPE_PSEUDOPRIVATE;                          // 26.0.0.0/8 (US DISA)
-				case 0x1c: return IP_SCOPE_PSEUDOPRIVATE;                          // 28.0.0.0/8 (US DSI-North)
-				case 0x1d: return IP_SCOPE_PSEUDOPRIVATE;                          // 29.0.0.0/8 (US DISA)
-				case 0x1e: return IP_SCOPE_PSEUDOPRIVATE;                          // 30.0.0.0/8 (US DISA)
-				case 0x33: return IP_SCOPE_PSEUDOPRIVATE;                          // 51.0.0.0/8 (UK Department of Social Security)
-				case 0x37: return IP_SCOPE_PSEUDOPRIVATE;                          // 55.0.0.0/8 (US DoD)
+				case 0x0b: //return IP_SCOPE_PSEUDOPRIVATE;                        // 11.0.0.0/8 (US DoD)
+				case 0x15: //return IP_SCOPE_PSEUDOPRIVATE;                        // 21.0.0.0/8 (US DDN-RVN)
+				case 0x16: //return IP_SCOPE_PSEUDOPRIVATE;                        // 22.0.0.0/8 (US DISA)
+				case 0x19: //return IP_SCOPE_PSEUDOPRIVATE;                        // 25.0.0.0/8 (UK Ministry of Defense)
+				case 0x1a: //return IP_SCOPE_PSEUDOPRIVATE;                        // 26.0.0.0/8 (US DISA)
+				case 0x1c: //return IP_SCOPE_PSEUDOPRIVATE;                        // 28.0.0.0/8 (US DSI-North)
+				case 0x1d: //return IP_SCOPE_PSEUDOPRIVATE;                        // 29.0.0.0/8 (US DISA)
+				case 0x1e: //return IP_SCOPE_PSEUDOPRIVATE;                        // 30.0.0.0/8 (US DISA)
+				case 0x33: //return IP_SCOPE_PSEUDOPRIVATE;                        // 51.0.0.0/8 (UK Department of Social Security)
+				case 0x37: //return IP_SCOPE_PSEUDOPRIVATE;                        // 55.0.0.0/8 (US DoD)
 				case 0x38: return IP_SCOPE_PSEUDOPRIVATE;                          // 56.0.0.0/8 (US Postal Service)
 				case 0x64:
 					if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE;    // 100.64.0.0/10
@@ -164,7 +161,7 @@ bool InetAddress::fromString(const char *ipSlashPort)
 	unsigned int port = 0;
 	if (*portAt) {
 		*(portAt++) = (char)0;
-		port = Utils::strToUInt(portAt) & 0xffff;
+		port = Utils::strToUInt(portAt) & 0xffffU;
 	}
 
 	if (strchr(buf,':')) {
@@ -189,7 +186,7 @@ InetAddress InetAddress::netmask() const
 	InetAddress r(*this);
 	switch(r.ss_family) {
 		case AF_INET:
-			reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
+			reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
 			break;
 		case AF_INET6: {
 			uint64_t nm[2];
@@ -211,7 +208,7 @@ InetAddress InetAddress::broadcast() const
 {
 	if (ss_family == AF_INET) {
 		InetAddress r(*this);
-		reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits()));
+		reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffffU >> netmaskBits()));
 		return r;
 	}
 	return InetAddress();
@@ -222,7 +219,7 @@ InetAddress InetAddress::network() const
 	InetAddress r(*this);
 	switch(r.ss_family) {
 		case AF_INET:
-			reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
+			reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
 			break;
 		case AF_INET6: {
 			uint64_t nm[2];
@@ -294,7 +291,7 @@ bool InetAddress::isNetwork() const
 			if (bits >= 32)
 				return false;
 			uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
-			return ((ip & (0xffffffff >> bits)) == 0);
+			return ((ip & (0xffffffffU >> bits)) == 0);
 		}
 		case AF_INET6: {
 			unsigned int bits = netmaskBits();
@@ -304,7 +301,7 @@ bool InetAddress::isNetwork() const
 				return false;
 			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
 			unsigned int p = bits / 8;
-			if ((ip[p++] & (0xff >> (bits % 8))) != 0)
+			if ((ip[p++] & (0xffU >> (bits % 8))) != 0)
 				return false;
 			while (p < 16) {
 				if (ip[p++])
@@ -378,48 +375,49 @@ bool InetAddress::operator<(const InetAddress &a) const
 
 InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
 {
-	struct sockaddr_in6 sin6;
-	sin6.sin6_family = AF_INET6;
-	sin6.sin6_addr.s6_addr[0] = 0xfe;
-	sin6.sin6_addr.s6_addr[1] = 0x80;
-	sin6.sin6_addr.s6_addr[2] = 0x00;
-	sin6.sin6_addr.s6_addr[3] = 0x00;
-	sin6.sin6_addr.s6_addr[4] = 0x00;
-	sin6.sin6_addr.s6_addr[5] = 0x00;
-	sin6.sin6_addr.s6_addr[6] = 0x00;
-	sin6.sin6_addr.s6_addr[7] = 0x00;
-	sin6.sin6_addr.s6_addr[8] = mac[0] & 0xfd;
-	sin6.sin6_addr.s6_addr[9] = mac[1];
-	sin6.sin6_addr.s6_addr[10] = mac[2];
-	sin6.sin6_addr.s6_addr[11] = 0xff;
-	sin6.sin6_addr.s6_addr[12] = 0xfe;
-	sin6.sin6_addr.s6_addr[13] = mac[3];
-	sin6.sin6_addr.s6_addr[14] = mac[4];
-	sin6.sin6_addr.s6_addr[15] = mac[5];
-	sin6.sin6_port = Utils::hton((uint16_t)64);
-	return InetAddress(sin6);
+	InetAddress r;
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_addr.s6_addr[0] = 0xfe;
+	sin6->sin6_addr.s6_addr[1] = 0x80;
+	sin6->sin6_addr.s6_addr[2] = 0x00;
+	sin6->sin6_addr.s6_addr[3] = 0x00;
+	sin6->sin6_addr.s6_addr[4] = 0x00;
+	sin6->sin6_addr.s6_addr[5] = 0x00;
+	sin6->sin6_addr.s6_addr[6] = 0x00;
+	sin6->sin6_addr.s6_addr[7] = 0x00;
+	sin6->sin6_addr.s6_addr[8] = mac[0] & 0xfdU;
+	sin6->sin6_addr.s6_addr[9] = mac[1];
+	sin6->sin6_addr.s6_addr[10] = mac[2];
+	sin6->sin6_addr.s6_addr[11] = 0xff;
+	sin6->sin6_addr.s6_addr[12] = 0xfe;
+	sin6->sin6_addr.s6_addr[13] = mac[3];
+	sin6->sin6_addr.s6_addr[14] = mac[4];
+	sin6->sin6_addr.s6_addr[15] = mac[5];
+	sin6->sin6_port = Utils::hton((uint16_t)64);
+	return r;
 }
 
 InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
 {
 	InetAddress r;
-	struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfd;
-	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56);
-	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48);
-	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40);
-	sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32);
-	sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24);
-	sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16);
-	sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8);
+	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56U);
+	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48U);
+	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40U);
+	sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32U);
+	sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24U);
+	sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16U);
+	sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8U);
 	sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid;
 	sin6->sin6_addr.s6_addr[9] = 0x99;
 	sin6->sin6_addr.s6_addr[10] = 0x93;
-	sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32);
-	sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24);
-	sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16);
-	sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8);
+	sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32U);
+	sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24U);
+	sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16U);
+	sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8U);
 	sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
 	sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
 	return r;
@@ -427,19 +425,19 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
 
 InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
 {
-	nwid ^= (nwid >> 32);
+	nwid ^= (nwid >> 32U);
 	InetAddress r;
-	struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfc;
-	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
-	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
-	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
+	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24U);
+	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16U);
+	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8U);
 	sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
-	sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
-	sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
-	sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
-	sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
+	sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32U);
+	sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24U);
+	sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16U);
+	sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8U);
 	sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
 	sin6->sin6_addr.s6_addr[15] = 0x01;
 	sin6->sin6_port = Utils::hton((uint16_t)40);

+ 145 - 89
node/InetAddress.hpp

@@ -14,12 +14,13 @@
 #ifndef ZT_INETADDRESS_HPP
 #define ZT_INETADDRESS_HPP
 
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
+#include <cstdlib>
+#include <cstring>
+#include <cstdint>
 
-#include "Constants.hpp"
 #include "../include/ZeroTierOne.h"
+
+#include "Constants.hpp"
 #include "Utils.hpp"
 #include "MAC.hpp"
 #include "Buffer.hpp"
@@ -41,6 +42,16 @@ namespace ZeroTier {
  */
 struct InetAddress : public sockaddr_storage
 {
+private:
+	template<typename SA>
+	ZT_ALWAYS_INLINE void copySockaddrToThis(const SA *sa)
+	{
+		memcpy(reinterpret_cast<void *>(this),sa,sizeof(SA));
+		if (sizeof(SA) < sizeof(InetAddress))
+			memset(reinterpret_cast<char *>(this) + sizeof(SA),0,sizeof(InetAddress) - sizeof(SA));
+	}
+
+public:
 	/**
 	 * Loopback IPv4 address (no port)
 	 */
@@ -75,123 +86,86 @@ struct InetAddress : public sockaddr_storage
 		IP_SCOPE_PRIVATE = 7        // 10.x.x.x, 192.168.x.x, etc.
 	};
 
-	// Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core
-	// but this is safe to put here.
-	struct Hasher
-	{
-		ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); }
-	};
-
-	ZT_ALWAYS_INLINE InetAddress() { memset(this,0,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const InetAddress &a) { memcpy(this,&a,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const InetAddress *a) { memcpy(this,a,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr *sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in *sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
+	// Hasher for unordered sets and maps in C++11
+	struct Hasher { ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); } };
+
+	ZT_ALWAYS_INLINE InetAddress() { memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress)); }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr *sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in *sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
 	ZT_ALWAYS_INLINE InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
 	ZT_ALWAYS_INLINE InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
-	ZT_ALWAYS_INLINE InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
-
-	ZT_ALWAYS_INLINE void clear() { memset(this,0,sizeof(InetAddress)); }
-
-	ZT_ALWAYS_INLINE InetAddress &operator=(const InetAddress &a)
-	{
-		if (&a != this)
-			memcpy(this,&a,sizeof(InetAddress));
-		return *this;
-	}
+	explicit ZT_ALWAYS_INLINE InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
 
-	ZT_ALWAYS_INLINE InetAddress &operator=(const InetAddress *a)
-	{
-		if (a != this)
-			memcpy(this,a,sizeof(InetAddress));
-		return *this;
-	}
+	ZT_ALWAYS_INLINE void clear() { memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress)); }
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage &ss)
 	{
-		if (reinterpret_cast<const InetAddress *>(&ss) != this)
-			memcpy(this,&ss,sizeof(InetAddress));
+		memcpy(reinterpret_cast<void *>(this),&ss,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage *ss)
 	{
-		if (reinterpret_cast<const InetAddress *>(ss) != this)
-			memcpy(this,ss,sizeof(InetAddress));
+		if (ss)
+			memcpy(reinterpret_cast<void *>(this),ss,sizeof(InetAddress));
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,&sa,sizeof(struct sockaddr_in));
-		}
+		copySockaddrToThis(&sa);
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,sa,sizeof(struct sockaddr_in));
-		}
+		if (sa)
+			copySockaddrToThis(sa);
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,&sa,sizeof(struct sockaddr_in6));
-		}
+		copySockaddrToThis(&sa);
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,sa,sizeof(struct sockaddr_in6));
-		}
+		if (sa)
+			copySockaddrToThis(sa);
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			switch(sa.sa_family) {
-				case AF_INET:
-					memcpy(this,&sa,sizeof(struct sockaddr_in));
-					break;
-				case AF_INET6:
-					memcpy(this,&sa,sizeof(struct sockaddr_in6));
-					break;
-			}
-		}
+		if (sa.sa_family == AF_INET)
+			copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(&sa));
+		else if (sa.sa_family == AF_INET6)
+			copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(&sa));
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			switch(sa->sa_family) {
-				case AF_INET:
-					memcpy(this,sa,sizeof(struct sockaddr_in));
-					break;
-				case AF_INET6:
-					memcpy(this,sa,sizeof(struct sockaddr_in6));
-					break;
-			}
+		if (sa) {
+			if (sa->sa_family == AF_INET)
+				copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(sa));
+			else if (sa->sa_family == AF_INET6)
+				copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(sa));
+			return *this;
 		}
+		memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
@@ -468,16 +442,16 @@ struct InetAddress : public sockaddr_storage
 		unsigned long h = 0;
 		switch(ss_family) {
 			case AF_INET:
-				h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00) >> 8;
-				h ^= (h >> 14);
+				h = (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) & 0xffffff00U) >> 8U;
+				h ^= (h >> 14U);
 				break;
 			case AF_INET6: {
 				const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
-				h = ((unsigned long)ip[0]); h <<= 1;
-				h += ((unsigned long)ip[1]); h <<= 1;
-				h += ((unsigned long)ip[2]); h <<= 1;
-				h += ((unsigned long)ip[3]); h <<= 1;
-				h += ((unsigned long)ip[4]); h <<= 1;
+				h = ((unsigned long)ip[0]); h <<= 1U;
+				h += ((unsigned long)ip[1]); h <<= 1U;
+				h += ((unsigned long)ip[2]); h <<= 1U;
+				h += ((unsigned long)ip[3]); h <<= 1U;
+				h += ((unsigned long)ip[4]); h <<= 1U;
 				h += ((unsigned long)ip[5]);
 			}	break;
 		}
@@ -487,10 +461,85 @@ struct InetAddress : public sockaddr_storage
 	/**
 	 * @return True if address family is non-zero
 	 */
-	ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); }
+	explicit ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); }
+
+	// Marshal interface ///////////////////////////////////////////////////////
+	static ZT_ALWAYS_INLINE int marshalSizeMax() { return 19; }
+	inline int marshal(uint8_t data[19]) const
+	{
+		unsigned int port;
+		switch(ss_family) {
+			case AF_INET:
+				port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in *>(this)->sin_port);
+				data[0] = 4;
+				data[1] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[0];
+				data[2] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[1];
+				data[3] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[2];
+				data[4] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[3];
+				data[5] = (uint8_t)(port >> 8U);
+				data[6] = (uint8_t)port;
+				return 7;
+			case AF_INET6:
+				port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port);
+				data[0] = 6;
+				for(int i=0;i<16;++i)
+					data[i+1] = reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr[i];
+				data[17] = (uint8_t)(port >> 8U);
+				data[18] = (uint8_t)port;
+				return 19;
+			default:
+				data[0] = 0;
+				return 1;
+		}
+	}
+	inline int unmarshal(const uint8_t *restrict data,const int len)
+	{
+#ifdef ZT_NO_TYPE_PUNNING
+		uint16_t tmp;
+#endif
+		if (len <= 0)
+			return -1;
+		switch(data[0]) {
+			case 0:
+				return 1;
+			case 4:
+				if (len < 7)
+					return -1;
+				memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
+				reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
+				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
+#ifdef ZT_NO_TYPE_PUNNING
+				memcpy(&tmp,data + 5,2);
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
+#else
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 5));
+#endif
+				return 7;
+			case 6:
+				if (len < 19)
+					return -1;
+				memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
+				reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
+				for(int i=0;i<16;i++)
+					(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
+#ifdef ZT_NO_TYPE_PUNNING
+				memcpy(&tmp,data + 17,2);
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
+#else
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 17));
+#endif
+				return 19;
+			default:
+				return -1;
+		}
+	}
+	////////////////////////////////////////////////////////////////////////////
 
 	template<unsigned int C>
-	ZT_ALWAYS_INLINE void serialize(Buffer<C> &b) const
+	inline void serialize(Buffer<C> &b) const
 	{
 		// This is used in the protocol and must be the same as describe in places
 		// like VERB_HELLO in Packet.hpp.
@@ -512,7 +561,7 @@ struct InetAddress : public sockaddr_storage
 	}
 
 	template<unsigned int C>
-	ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
 	{
 		memset(this,0,sizeof(InetAddress));
 		unsigned int p = startAt;
@@ -607,6 +656,13 @@ struct InetAddress : public sockaddr_storage
 	static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
 };
 
+static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in *p) { return reinterpret_cast<InetAddress *>(p); }
+static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in6 *p) { return reinterpret_cast<InetAddress *>(p); }
+static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr *p) { return reinterpret_cast<InetAddress *>(p); }
+static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in *p) { return reinterpret_cast<const InetAddress *>(p); }
+static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in6 *p) { return reinterpret_cast<const InetAddress *>(p); }
+static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr *p) { return reinterpret_cast<const InetAddress *>(p); }
+
 } // namespace ZeroTier
 
 #endif

+ 106 - 0
node/Locator.cpp

@@ -0,0 +1,106 @@
+/*
+ * 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 "Locator.hpp"
+
+namespace ZeroTier {
+
+bool Locator::sign(const int64_t ts,const Identity &id)
+{
+	uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+	if (!id.hasPrivate())
+		return false;
+	_ts = ts;
+	if (_endpointCount > 0)
+		std::sort(_at,_at + _endpointCount);
+	const unsigned int signLen = marshal(signData,true);
+	_signatureLength = id.sign(signData, signLen, _signature, sizeof(_signature));
+	return (_signatureLength > 0);
+}
+
+bool Locator::verify(const Identity &id) const
+{
+	if ((_ts == 0)||(_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+		return false;
+	uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+	const unsigned int signLen = marshal(signData,true);
+	return id.verify(signData,signLen,_signature,_signatureLength);
+}
+
+int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature) const
+{
+	if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+		return -1;
+
+	Utils::putUInt64(data,(uint64_t)_ts);
+	int p = 8;
+
+	data[p++] = (uint8_t)(_endpointCount >> 8U);
+	data[p++] = (uint8_t)_endpointCount;
+	for(unsigned int i=0;i<_endpointCount;++i) {
+		int tmp = _at[i].marshal(data + p);
+		if (tmp < 0)
+			return -1;
+		p += tmp;
+	}
+
+	if (!excludeSignature) {
+		data[p++] = (uint8_t)(_signatureLength >> 8U);
+		data[p++] = (uint8_t)_signatureLength;
+		memcpy(data + p,_signature,_signatureLength);
+		p += (int)_signatureLength;
+	}
+
+	return p;
+}
+
+int Locator::unmarshal(const uint8_t *restrict data,const int len)
+{
+	if (len <= (8 + 48))
+		return -1;
+
+	_ts = (int64_t)Utils::readUInt64(data);
+	int p = 8;
+
+	if ((p + 2) > len)
+		return -1;
+	unsigned int ec = (int)data[p++];
+	ec <<= 8U;
+	ec |= data[p++];
+	if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
+		return -1;
+	_endpointCount = ec;
+	for(int i=0;i<ec;++i) {
+		int tmp = _at[i].unmarshal(data + p,len - p);
+		if (tmp < 0)
+			return -1;
+		p += tmp;
+	}
+
+	if ((p + 2) > len)
+		return -1;
+	unsigned int sl = data[p++];
+	sl <<= 8U;
+	sl |= data[p++];
+	if (sl > ZT_SIGNATURE_BUFFER_SIZE)
+		return -1;
+	_signatureLength = sl;
+	if ((p + sl) > len)
+		return -1;
+	memcpy(_signature,data + p,sl);
+	p += (int)sl;
+
+	return p;
+}
+
+} // namespace ZeroTier

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor