Browse Source

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

Grant Limberg 5 years ago
parent
commit
8e1a88c2fb
100 changed files with 6273 additions and 8012 deletions
  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
 /version.h
+/.idea
+/go/.idea
 .DS_Store
 .DS_Store
 .Trashes
 .Trashes
 *.swp
 *.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)
 cmake_minimum_required (VERSION 3.8)
+project(zerotier DESCRIPTION "ZeroTier Network Hypervisor" LANGUAGES CXX C)
 
 
 if(${CMAKE_VERSION} VERSION_LESS 3.15)
 if(${CMAKE_VERSION} VERSION_LESS 3.15)
 	cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
 	cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
@@ -7,33 +8,32 @@ else()
 endif()
 endif()
 
 
 if(WIN32)
 if(WIN32)
-	# If building on Windows, set minimum target to Windows 7
 	set(CMAKE_SYSTEM_VERSION "7" CACHE STRING INTERNAL FORCE)
 	set(CMAKE_SYSTEM_VERSION "7" CACHE STRING INTERNAL FORCE)
 endif(WIN32)
 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_MAJOR 2 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_MINOR 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_MINOR 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_REVISION 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_REVISION 0 CACHE INTERNAL "")
 set(ZEROTIER_ONE_VERSION_BUILD 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")
 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)
 if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
 	message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
 	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()
 endif()
 
 
 option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF)
 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)
 if (BUILD_CENTRAL_CONTROLLER)
 	find_package(PostgreSQL REQUIRED)
 	find_package(PostgreSQL REQUIRED)
 	set(ENABLE_SSL_SUPPORT OFF)
 	set(ENABLE_SSL_SUPPORT OFF)
@@ -45,25 +45,21 @@ if (BUILD_CENTRAL_CONTROLLER)
 	add_subdirectory("ext/librabbitmq")
 	add_subdirectory("ext/librabbitmq")
 endif(BUILD_CENTRAL_CONTROLLER)
 endif(BUILD_CENTRAL_CONTROLLER)
 
 
-set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version")
-
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 if(CMAKE_BUILD_TYPE STREQUAL "Debug")
 	add_definitions(-DZT_TRACE)
 	add_definitions(-DZT_TRACE)
 endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
 endif(CMAKE_BUILD_TYPE STREQUAL "Debug")
 
 
-project(zerotier
-	DESCRIPTION "ZeroTier Network Hypervisor"
-	LANGUAGES CXX C)
-
 if(WIN32)
 if(WIN32)
+	message("++ Setting Windows Compiler Flags ${CMAKE_BUILD_TYPE}")
 	add_definitions(-DNOMINMAX)
 	add_definitions(-DNOMINMAX)
 else(WIN32)
 else(WIN32)
 	if(APPLE)
 	if(APPLE)
 
 
-		message("Setting macOS Compiler Flags ${CMAKE_BUILD_TYPE}")
+		message("++ Setting MacOS Compiler Flags ${CMAKE_BUILD_TYPE}")
 		add_compile_options(
 		add_compile_options(
 			-Wall
 			-Wall
 			-Wno-deprecated
 			-Wno-deprecated
+			-Wno-unused-function
 			-mmacosx-version-min=10.9
 			-mmacosx-version-min=10.9
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:DEBUG>:-O0>
 			$<$<CONFIG:DEBUG>:-O0>
@@ -79,17 +75,13 @@ else(WIN32)
 			$<$<CONFIG:RELEASE>:-flto>
 			$<$<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(
 		add_compile_options(
 			-Wall
 			-Wall
 			-Wno-deprecated
 			-Wno-deprecated
+			-Wno-unused-function
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:Debug>:-g>
 			$<$<CONFIG:DEBUG>:-O0>
 			$<$<CONFIG:DEBUG>:-O0>
 			$<$<CONFIG:RELEASE>:-O3>
 			$<$<CONFIG:RELEASE>:-O3>
@@ -110,10 +102,9 @@ if (
 	CMAKE_SYSTEM_PROCESSOR MATCHES "i586" OR
 	CMAKE_SYSTEM_PROCESSOR MATCHES "i586" OR
 	CMAKE_SYSTEM_PROCESSOR MATCHES "i686"
 	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(
 	add_compile_options(
 		-maes
 		-maes
-		-mmmx
 		-mrdrnd
 		-mrdrnd
 		-mpclmul
 		-mpclmul
 		-msse
 		-msse
@@ -126,9 +117,6 @@ endif()
 if(ZT_TRACE)
 if(ZT_TRACE)
 	add_definitions(-DZT_TRACE)
 	add_definitions(-DZT_TRACE)
 endif()
 endif()
-if(ZT_DEBUG_TRACE)
-	add_definitions(-DZT_DEBUG_TRACE)
-endif()
 
 
 add_subdirectory(node)
 add_subdirectory(node)
 add_subdirectory(controller)
 add_subdirectory(controller)
@@ -136,12 +124,6 @@ add_subdirectory(osdep)
 add_subdirectory(root)
 add_subdirectory(root)
 add_subdirectory(go/native)
 add_subdirectory(go/native)
 
 
-#if(WIN32)
-#	add_subdirectory("windows/WinUI")
-#	add_subdirectory("windows/copyutil")
-#	add_definitions(-DNOMINMAX)
-#endif(WIN32)
-
 set(
 set(
 	zt_osdep
 	zt_osdep
 	zt_core
 	zt_core
@@ -149,25 +131,18 @@ set(
 	zt_go_native
 	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)
 if(WIN32)
 	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 else(WIN32)
 else(WIN32)
 	set(libs ${libs} pthread)
 	set(libs ${libs} pthread)
 endif(WIN32)
 endif(WIN32)
 
 
+#if(WIN32)
+#	add_subdirectory("windows/WinUI")
+#	add_subdirectory("windows/copyutil")
+#	add_definitions(-DNOMINMAX)
+#endif(WIN32)
+
 #if(WIN32)
 #if(WIN32)
 #	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 #	set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi)
 #	set(src
 #	set(src
@@ -203,6 +178,6 @@ add_custom_command(
 )
 )
 add_custom_target(build_zerotier ALL DEPENDS zerotier)
 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)
 void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender)
 {
 {
 	char tmp[64];
 	char tmp[64];
+
 	_signingId = signingId;
 	_signingId = signingId;
 	_sender = sender;
 	_sender = sender;
 	_signingIdAddressString = signingId.address().toString(tmp);
 	_signingIdAddressString = signingId.address().toString(tmp);
@@ -1445,6 +1446,7 @@ void EmbeddedNetworkController::_request(
 
 
 	const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false);
 	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 ((v6AssignMode.is_object())&&(!noAutoAssignIps)) {
 		if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
 		if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
 			nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt());
 			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,
 		// 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.
 		// 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];
 		char maskingKey [128];
-		Utils::hex(sha512pk,32,maskingKey);
+		Utils::hex(sha384pk,32,maskingKey);
 
 
 		httplib::Client htcli(_lfNodeHost.c_str(),_lfNodePort,600);
 		httplib::Client htcli(_lfNodeHost.c_str(),_lfNodePort,600);
 		int64_t timeRangeStart = 0;
 		int64_t timeRangeStart = 0;

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

@@ -13,53 +13,6 @@
 
 
 package cli
 package cli
 
 
-import (
-	"encoding/base64"
-	"fmt"
-	"io/ioutil"
-	"net/url"
-	"os"
-	"strings"
-
-	"zerotier/pkg/zerotier"
-)
-
 // AddRoot CLI command
 // AddRoot CLI command
 func AddRoot(basePath, authToken string, args []string) {
 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
   status                               Show ZeroTier service status and config
   peers                                Show VL1 peers
   peers                                Show VL1 peers
   roots                                Show configured VL1 root servers
   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
   identity <command> [args]            Identity management commands
     new [c25519|p384]                  Create new identity (including secret)
     new [c25519|p384]                  Create new identity (including secret)
     getpublic <identity>               Extract only public part of identity
     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
 package cli
 
 
-import (
-	"fmt"
-	"os"
-
-	"zerotier/pkg/zerotier"
-)
-
 // Roots CLI command
 // Roots CLI command
 func Roots(basePath, authToken string, args []string, jsonOutput bool) {
 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"
 	"os/signal"
 	"os/signal"
 	"syscall"
 	"syscall"
+
 	"zerotier/pkg/zerotier"
 	"zerotier/pkg/zerotier"
 )
 )
 
 

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

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

+ 1 - 0
go/go.mod

@@ -5,4 +5,5 @@ go 1.13
 require (
 require (
 	github.com/Microsoft/go-winio v0.4.14
 	github.com/Microsoft/go-winio v0.4.14
 	github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
 	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-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 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU=
 golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 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 "GoGlue.h"
 
 
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+
 #include "../../node/Constants.hpp"
 #include "../../node/Constants.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/Address.hpp"
 #include "../../node/Address.hpp"
-#include "../../node/Locator.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/EthernetTap.hpp"
 #include "../../osdep/EthernetTap.hpp"
-#include "../../osdep/ManagedRoute.hpp"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
 
 
 #ifndef __WINDOWS__
 #ifndef __WINDOWS__
-#include <errno.h>
-#include <signal.h>
 #include <unistd.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/socket.h>
 #include <sys/un.h>
 #include <sys/un.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <netinet/tcp.h>
 #ifdef __BSD__
 #ifdef __BSD__
 #include <net/if.h>
 #include <net/if.h>
 #endif
 #endif
@@ -56,7 +46,6 @@
 #include <mutex>
 #include <mutex>
 #include <map>
 #include <map>
 #include <vector>
 #include <vector>
-#include <array>
 #include <set>
 #include <set>
 #include <memory>
 #include <memory>
 #include <atomic>
 #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 */
 /* 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 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" 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" 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 goVirtualNetworkConfigFunc(void *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *);
 extern "C" void goZtEvent(void *,int,const void *);
 extern "C" void goZtEvent(void *,int,const void *);
 extern "C" void goHandleTapAddedMulticastGroup(void *,ZT_GoTap *,uint64_t,uint64_t,uint32_t);
 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 *uptr,
 	void *tptr,
 	void *tptr,
 	uint64_t ztAddress,
 	uint64_t ztAddress,
+	const ZT_Identity *id,
 	int desiredAddressFamily,
 	int desiredAddressFamily,
 	struct sockaddr_storage *sa)
 	struct sockaddr_storage *sa)
 {
 {
@@ -289,6 +278,7 @@ static int ZT_GoNode_PathLookupFunction(
 		reinterpret_cast<ZT_GoNode *>(uptr)->goUserPtr,
 		reinterpret_cast<ZT_GoNode *>(uptr)->goUserPtr,
 		ztAddress,
 		ztAddress,
 		desiredAddressFamily,
 		desiredAddressFamily,
+		id,
 		&family,
 		&family,
 		ip,
 		ip,
 		&port
 		&port
@@ -310,20 +300,6 @@ static int ZT_GoNode_PathLookupFunction(
 	return 0;
 	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)
 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.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction;
 		cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction;
 		cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction;
 		cb.eventCallback = &ZT_GoNode_EventCallback;
 		cb.eventCallback = &ZT_GoNode_EventCallback;
-		cb.dnsResolver = &ZT_GoNode_DNSResolver;
 		cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction;
 		cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction;
 		cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction;
 		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);
 	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
 #ifndef ZT_GONODE_H
 #define ZT_GONODE_H
 #define ZT_GONODE_H
 
 
+#ifdef __cplusplus
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#else
 #include <stdint.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
+#endif
 
 
 #include "../../include/ZeroTierCore.h"
 #include "../../include/ZeroTierCore.h"
 #include "../../node/Constants.hpp"
 #include "../../node/Constants.hpp"
 
 
-/****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 
 /* A pointer to an instance of EthernetTap */
 /* A pointer to an instance of EthernetTap */
 typedef void ZT_GoTap;
 typedef void ZT_GoTap;
@@ -32,18 +40,8 @@ typedef void ZT_GoTap;
 struct ZT_GoNode_Impl;
 struct ZT_GoNode_Impl;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 
 
-/****************************************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/****************************************************************************/
-
 extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH;
 extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH;
 
 
-/****************************************************************************/
-
 ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr);
 ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr);
 
 
 void ZT_GoNode_delete(ZT_GoNode *gn);
 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_GoNode_leave(ZT_GoNode *gn,uint64_t nwid);
 
 
-/****************************************************************************/
-
 void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled);
 void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled);
 
 
 int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
 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);
 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
 #ifdef __cplusplus
 }
 }
 #endif
 #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)
 		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 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 {
 		} 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 {
 		} 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 {
 		} else {
 			out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
 			out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE")
 			_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method})
 			_ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method})

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

@@ -13,7 +13,6 @@
 
 
 package zerotier
 package zerotier
 
 
-//#cgo CFLAGS: -O3
 //#include "../../native/GoGlue.h"
 //#include "../../native/GoGlue.h"
 import "C"
 import "C"
 
 
@@ -47,15 +46,26 @@ type Identity struct {
 	privateKey []byte
 	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
 // NewIdentity generates a new identity of the selected type
 func NewIdentity(identityType int) (*Identity, error) {
 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
 		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.
 // NewIdentityFromString generates a new identity from its string representation.
@@ -159,23 +169,35 @@ func (id *Identity) String() string {
 func (id *Identity) LocallyValidate() bool {
 func (id *Identity) LocallyValidate() bool {
 	idCStr := C.CString(id.String())
 	idCStr := C.CString(id.String())
 	defer C.free(unsafe.Pointer(idCStr))
 	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
 // Sign signs a message with this identity
 func (id *Identity) Sign(msg []byte) ([]byte, error) {
 func (id *Identity) Sign(msg []byte) ([]byte, error) {
 	idCStr := C.CString(id.PrivateKeyString())
 	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
 	var dataP unsafe.Pointer
 	if len(msg) > 0 {
 	if len(msg) > 0 {
 		dataP = unsafe.Pointer(&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 nil, ErrInvalidKey
 	}
 	}
-	return sigbuf[0:int(siglen)], nil
+
+	return sig[0:uint(sigLen)], nil
 }
 }
 
 
 // Verify verifies a signature
 // Verify verifies a signature
@@ -183,13 +205,20 @@ func (id *Identity) Verify(msg, sig []byte) bool {
 	if len(sig) == 0 {
 	if len(sig) == 0 {
 		return false
 		return false
 	}
 	}
+
 	idCStr := C.CString(id.String())
 	idCStr := C.CString(id.String())
 	defer C.free(unsafe.Pointer(idCStr))
 	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
 	var dataP unsafe.Pointer
 	if len(msg) > 0 {
 	if len(msg) > 0 {
 		dataP = unsafe.Pointer(&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)
 // 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
 // LocalConfigVirtualAddressConfiguration contains settings for virtual addresses
 type LocalConfigVirtualAddressConfiguration struct {
 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 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
 // 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
 	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
 // Peers retrieves a list of current peers
 func (n *Node) Peers() []*Peer {
 func (n *Node) Peers() []*Peer {
 	var peers []*Peer
 	var peers []*Peer
@@ -665,59 +609,21 @@ func (n *Node) Peers() []*Peer {
 			p2.Role = int(p.role)
 			p2.Role = int(p.role)
 
 
 			p2.Paths = make([]Path, 0, int(p.pathCount))
 			p2.Paths = make([]Path, 0, int(p.pathCount))
-			usingAllocation := false
 			for j := uintptr(0); j < uintptr(p.pathCount); j++ {
 			for j := uintptr(0); j < uintptr(p.pathCount); j++ {
 				pt := &p.paths[j]
 				pt := &p.paths[j]
 				if pt.alive != 0 {
 				if pt.alive != 0 {
 					a := sockaddrStorageToUDPAddr(&pt.address)
 					a := sockaddrStorageToUDPAddr(&pt.address)
 					if a != nil {
 					if a != nil {
-						alloc := float32(pt.allocation)
-						if alloc > 0.0 {
-							usingAllocation = true
-						}
 						p2.Paths = append(p2.Paths, Path{
 						p2.Paths = append(p2.Paths, Path{
 							IP:                     a.IP,
 							IP:                     a.IP,
 							Port:                   a.Port,
 							Port:                   a.Port,
 							LastSend:               int64(pt.lastSend),
 							LastSend:               int64(pt.lastSend),
 							LastReceive:            int64(pt.lastReceive),
 							LastReceive:            int64(pt.lastReceive),
 							TrustedPathID:          uint64(pt.trustedPathId),
 							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()
 			p2.Clock = TimeMs()
 			peers = append(peers, p2)
 			peers = append(peers, p2)
@@ -754,10 +660,10 @@ func (n *Node) pathCheck(ztAddress Address, af int, ip net.IP, port int) bool {
 	return true
 	return true
 }
 }
 
 
-func (n *Node) pathLookup(ztAddress Address) (net.IP, int) {
+func (n *Node) pathLookup(id *Identity) (net.IP, int) {
 	n.localConfigLock.RLock()
 	n.localConfigLock.RLock()
 	defer n.localConfigLock.RUnlock()
 	defer n.localConfigLock.RUnlock()
-	virt := n.localConfig.Virtual[ztAddress]
+	virt := n.localConfig.Virtual[id.address]
 	if len(virt.Try) > 0 {
 	if len(virt.Try) > 0 {
 		idx := rand.Int() % len(virt.Try)
 		idx := rand.Int() % len(virt.Try)
 		return virt.Try[idx].IP, virt.Try[idx].Port
 		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) 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
 // 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
 //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()
 	nodesByUserPtrLock.RLock()
 	node := nodesByUserPtr[uintptr(gn)]
 	node := nodesByUserPtr[uintptr(gn)]
 	nodesByUserPtrLock.RUnlock()
 	nodesByUserPtrLock.RUnlock()
@@ -868,7 +771,11 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, i
 		return 0
 		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 {
 	if len(ip) > 0 && port > 0 && port <= 65535 {
 		ip4 := ip.To4()
 		ip4 := ip.To4()
 		if len(ip4) == 4 {
 		if len(ip4) == 4 {
@@ -922,38 +829,6 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Poin
 	return -1
 	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
 //export goVirtualNetworkConfigFunc
 func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
 func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) {
 	go func() {
 	go func() {
@@ -1032,9 +907,6 @@ func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) {
 		case C.ZT_EVENT_USER_MESSAGE:
 		case C.ZT_EVENT_USER_MESSAGE:
 			um := (*C.ZT_UserMessage)(data)
 			um := (*C.ZT_UserMessage)(data)
 			node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length)))
 			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"`
 	LastSend               int64   `json:"lastSend"`
 	LastReceive            int64   `json:"lastReceive"`
 	LastReceive            int64   `json:"lastReceive"`
 	TrustedPathID          uint64  `json:"trustedPathID"`
 	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
 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 {
 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"
 	"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.
 // DLLError describes reasons for DLL load failures.
 type DLLError struct {
 type DLLError struct {
 	Err     error
 	Err     error
@@ -20,10 +32,6 @@ type DLLError struct {
 
 
 func (e *DLLError) Error() string { return e.Msg }
 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.
 // A DLL implements access to a single DLL.
 type DLL struct {
 type DLL struct {
 	Name   string
 	Name   string
@@ -40,7 +48,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	h, e := loadlibrary(namep)
+	h, e := syscall_loadlibrary(namep)
 	if e != 0 {
 	if e != 0 {
 		return nil, &DLLError{
 		return nil, &DLLError{
 			Err:     e,
 			Err:     e,
@@ -50,7 +58,7 @@ func LoadDLL(name string) (dll *DLL, err error) {
 	}
 	}
 	d := &DLL{
 	d := &DLL{
 		Name:   name,
 		Name:   name,
-		Handle: Handle(h),
+		Handle: h,
 	}
 	}
 	return d, nil
 	return d, nil
 }
 }
@@ -71,7 +79,7 @@ func (d *DLL) FindProc(name string) (proc *Proc, err error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	a, e := getprocaddress(uintptr(d.Handle), namep)
+	a, e := syscall_getprocaddress(d.Handle, namep)
 	if e != 0 {
 	if e != 0 {
 		return nil, &DLLError{
 		return nil, &DLLError{
 			Err:     e,
 			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
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 // 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
 set -e
 shopt -s nullglob
 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)"
 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; }
 [[ -n $winerror ]] || { echo "Unable to find winerror.h" >&2; exit 1; }
 
 
 declare -A errors
 declare -A errors
 
 
 {
 {
-	echo "// Code generated by 'go generate'; DO NOT EDIT."
+	echo "// Code generated by 'mkerrors.bash'; DO NOT EDIT."
 	echo
 	echo
 	echo "package windows"
 	echo "package windows"
 	echo "import \"syscall\""
 	echo "import \"syscall\""
@@ -61,4 +60,4 @@ declare -A errors
 	done < "$winerror"
 	done < "$winerror"
 
 
 	echo ")"
 	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
 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"
 	"unsafe"
 )
 )
 
 
-const (
-	STANDARD_RIGHTS_REQUIRED = 0xf0000
-	STANDARD_RIGHTS_READ     = 0x20000
-	STANDARD_RIGHTS_WRITE    = 0x20000
-	STANDARD_RIGHTS_EXECUTE  = 0x20000
-	STANDARD_RIGHTS_ALL      = 0x1F0000
-)
-
 const (
 const (
 	NameUnknown          = 0
 	NameUnknown          = 0
 	NameFullyQualifiedDN = 1
 	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
 	var s *uint16
 	e := ConvertSidToStringSid(sid, &s)
 	e := ConvertSidToStringSid(sid, &s)
 	if e != nil {
 	if e != nil {
-		return "", e
+		return ""
 	}
 	}
 	defer LocalFree((Handle)(unsafe.Pointer(s)))
 	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.
 // Len returns the length, in bytes, of a valid security identifier SID.
@@ -603,12 +594,22 @@ type Tokenprimarygroup struct {
 
 
 type Tokengroups struct {
 type Tokengroups struct {
 	GroupCount uint32
 	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 {
 type Tokenprivileges struct {
 	PrivilegeCount uint32
 	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 {
 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	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	GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW
 //sys	getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW
 //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.
 // An access token contains the security information for a logon session.
 // The system creates an access token when a user logs on, and every
 // 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.
 // system-related operations on the local computer.
 type Token Handle
 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) {
 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
 // GetCurrentProcessToken returns the access token associated with
@@ -775,8 +773,8 @@ func (token Token) GetLinkedToken() (Token, error) {
 	return linkedToken, nil
 	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) {
 func GetSystemDirectory() (string, error) {
 	n := uint32(MAX_PATH)
 	n := uint32(MAX_PATH)
 	for {
 	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.
 // IsMember reports whether the access token t is a member of the provided SID.
 func (t Token) IsMember(sid *SID) (bool, error) {
 func (t Token) IsMember(sid *SID) (bool, error) {
 	var b int32
 	var b int32
@@ -842,3 +876,521 @@ type WTS_SESSION_INFO struct {
 //sys WTSQueryUserToken(session uint32, token *Token) (err error) = wtsapi32.WTSQueryUserToken
 //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 WTSEnumerateSessions(handle Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW
 //sys WTSFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory
 //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
 	Description *uint16
 }
 }
 
 
+type SERVICE_DELAYED_AUTO_START_INFO struct {
+	IsDelayedAutoStartUp uint32
+}
+
 type SERVICE_STATUS_PROCESS struct {
 type SERVICE_STATUS_PROCESS struct {
 	ServiceType             uint32
 	ServiceType             uint32
 	CurrentState            uint32
 	CurrentState            uint32
@@ -200,12 +204,19 @@ type SC_ACTION struct {
 	Delay uint32
 	Delay uint32
 }
 }
 
 
+type QUERY_SERVICE_LOCK_STATUS struct {
+	IsLocked     uint32
+	LockOwner    *uint16
+	LockDuration uint32
+}
+
 //sys	CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle
 //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	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	OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW
 //sys	DeleteService(service Handle) (err error) = advapi32.DeleteService
 //sys	DeleteService(service Handle) (err error) = advapi32.DeleteService
 //sys	StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
 //sys	StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW
 //sys	QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus
 //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	ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService
 //sys	StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
 //sys	StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW
 //sys	SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus
 //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"
 	errorspkg "errors"
 	"sync"
 	"sync"
 	"syscall"
 	"syscall"
+	"time"
 	"unicode/utf16"
 	"unicode/utf16"
 	"unsafe"
 	"unsafe"
 )
 )
@@ -56,6 +57,10 @@ const (
 	FILE_VOLUME_IS_COMPRESSED         = 0x00008000
 	FILE_VOLUME_IS_COMPRESSED         = 0x00008000
 	FILE_VOLUME_QUOTAS                = 0x00000020
 	FILE_VOLUME_QUOTAS                = 0x00000020
 
 
+	// Flags for LockFileEx.
+	LOCKFILE_FAIL_IMMEDIATELY = 0x00000001
+	LOCKFILE_EXCLUSIVE_LOCK   = 0x00000002
+
 	// Return values of SleepEx and other APC functions
 	// Return values of SleepEx and other APC functions
 	STATUS_USER_APC    = 0x000000C0
 	STATUS_USER_APC    = 0x000000C0
 	WAIT_IO_COMPLETION = STATUS_USER_APC
 	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	LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, err error) = LoadLibraryExW
 //sys	FreeLibrary(handle Handle) (err error)
 //sys	FreeLibrary(handle Handle) (err error)
 //sys	GetProcAddress(module Handle, procname string) (proc uintptr, 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	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	FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW
 //sys	ExitProcess(exitcode uint32)
 //sys	ExitProcess(exitcode uint32)
@@ -159,6 +166,8 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	DeleteFile(path *uint16) (err error) = DeleteFileW
 //sys	DeleteFile(path *uint16) (err error) = DeleteFileW
 //sys	MoveFile(from *uint16, to *uint16) (err error) = MoveFileW
 //sys	MoveFile(from *uint16, to *uint16) (err error) = MoveFileW
 //sys	MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW
 //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	GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW
 //sys	GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
 //sys	GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW
 //sys	SetEndOfFile(handle Handle) (err error)
 //sys	SetEndOfFile(handle Handle) (err error)
@@ -171,13 +180,12 @@ func NewCallbackCDecl(fn interface{}) uintptr {
 //sys	CancelIo(s Handle) (err error)
 //sys	CancelIo(s Handle) (err error)
 //sys	CancelIoEx(s Handle, o *Overlapped) (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	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	TerminateProcess(handle Handle, exitcode uint32) (err error)
 //sys	GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)
 //sys	GetExitCodeProcess(handle Handle, exitcode *uint32) (err error)
 //sys	GetStartupInfo(startupInfo *StartupInfo) (err error) = GetStartupInfoW
 //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	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	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]
 //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	SetEnvironmentVariable(name *uint16, value *uint16) (err error) = kernel32.SetEnvironmentVariableW
 //sys	CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
 //sys	CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock
 //sys	DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock
 //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	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	GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW
 //sys	SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW
 //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	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	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	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	GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode
 //sys	SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
 //sys	SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode
 //sys	GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo
 //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	CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot
 //sys	Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32FirstW
 //sys	Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32FirstW
 //sys	Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32NextW
 //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)
 //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.
 // 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
 //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	SetEvent(event Handle) (err error) = kernel32.SetEvent
 //sys	ResetEvent(event Handle) (err error) = kernel32.ResetEvent
 //sys	ResetEvent(event Handle) (err error) = kernel32.ResetEvent
 //sys	PulseEvent(event Handle) (err error) = kernel32.PulseEvent
 //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	SleepEx(milliseconds uint32, alertable bool) (ret uint32) = kernel32.SleepEx
 //sys	CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, err error) = kernel32.CreateJobObjectW
 //sys	CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, err error) = kernel32.CreateJobObjectW
 //sys	AssignProcessToJobObject(job Handle, process Handle) (err error) = kernel32.AssignProcessToJobObject
 //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	SetPriorityClass(process Handle, priorityClass uint32) (err error) = kernel32.SetPriorityClass
 //sys	GetPriorityClass(process Handle) (ret uint32, err error) = kernel32.GetPriorityClass
 //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	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
 // Volume Management Functions
 //sys	DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW
 //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	FindNextVolumeMountPoint(findVolumeMountPoint Handle, volumeMountPoint *uint16, bufferLength uint32) (err error) = FindNextVolumeMountPointW
 //sys	FindVolumeClose(findVolume Handle) (err error)
 //sys	FindVolumeClose(findVolume Handle) (err error)
 //sys	FindVolumeMountPointClose(findVolumeMountPoint 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	GetDriveType(rootPathName *uint16) (driveType uint32) = GetDriveTypeW
 //sys	GetLogicalDrives() (drivesBitMask uint32, err error) [failretval==0]
 //sys	GetLogicalDrives() (drivesBitMask uint32, err error) [failretval==0]
 //sys	GetLogicalDriveStrings(bufferLength uint32, buffer *uint16) (n uint32, err error) [failretval==0] = GetLogicalDriveStringsW
 //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	SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) = SetVolumeLabelW
 //sys	SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) = SetVolumeMountPointW
 //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	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
 // 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
 // GetProcAddressByOrdinal retrieves the address of the exported
 // function from module by ordinal.
 // function from module by ordinal.
 func GetProcAddressByOrdinal(module Handle, ordinal uintptr) (proc uintptr, err error) {
 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:
 	default:
 		createmode = OPEN_EXISTING
 		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
 	return h, e
 }
 }
 
 
@@ -497,6 +566,10 @@ func ComputerName() (name string, err error) {
 	return string(utf16.Decode(b[0:n])), nil
 	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) {
 func Ftruncate(fd Handle, length int64) (err error) {
 	curoffset, e := Seek(fd, 0, 1)
 	curoffset, e := Seek(fd, 0, 1)
 	if e != nil {
 	if e != nil {
@@ -628,6 +701,8 @@ const socket_error = uintptr(^uint32(0))
 //sys	WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup
 //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	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	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	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	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
 //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 {
 		for n < len(pp.Path) && pp.Path[n] != 0 {
 			n++
 			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)
 		sa.Name = string(bytes)
 		return sa, nil
 		return sa, nil
 
 
@@ -1056,10 +1131,27 @@ func NsecToTimespec(nsec int64) (ts Timespec) {
 // TODO(brainman): fix all needed for net
 // TODO(brainman): fix all needed for net
 
 
 func Accept(fd Handle) (nfd Handle, sa Sockaddr, err error) { return 0, nil, syscall.EWINDOWS }
 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) {
 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 }
 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.
 // 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
 	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) {
 func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) {
 	// NOTE(rsc): The Win32finddata struct is wrong for the system call:
 	// 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
 	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 (
 const (
-	GENERIC_READ    = 0x80000000
-	GENERIC_WRITE   = 0x40000000
-	GENERIC_EXECUTE = 0x20000000
-	GENERIC_ALL     = 0x10000000
-
 	FILE_LIST_DIRECTORY   = 0x00000001
 	FILE_LIST_DIRECTORY   = 0x00000001
 	FILE_APPEND_DATA      = 0x00000004
 	FILE_APPEND_DATA      = 0x00000004
 	FILE_WRITE_ATTRIBUTES = 0x00000100
 	FILE_WRITE_ATTRIBUTES = 0x00000100
@@ -158,17 +153,43 @@ const (
 	WAIT_OBJECT_0  = 0x00000000
 	WAIT_OBJECT_0  = 0x00000000
 	WAIT_FAILED    = 0xFFFFFFFF
 	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_COPY    = 0x01
 	FILE_MAP_WRITE   = 0x02
 	FILE_MAP_WRITE   = 0x02
 	FILE_MAP_READ    = 0x04
 	FILE_MAP_READ    = 0x04
 	FILE_MAP_EXECUTE = 0x20
 	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.
 	// Windows reserves errors >= 1<<29 for application use.
 	APPLICATION_ERROR = 1 << 29
 	APPLICATION_ERROR = 1 << 29
@@ -450,12 +471,6 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
 	return
 	return
 }
 }
 
 
-type SecurityAttributes struct {
-	Length             uint32
-	SecurityDescriptor uintptr
-	InheritHandle      uint32
-}
-
 type Overlapped struct {
 type Overlapped struct {
 	Internal     uintptr
 	Internal     uintptr
 	InternalHigh uintptr
 	InternalHigh uintptr
@@ -629,6 +644,16 @@ type ProcessEntry32 struct {
 	ExeFile         [MAX_PATH]uint16
 	ExeFile         [MAX_PATH]uint16
 }
 }
 
 
+type ThreadEntry32 struct {
+	Size           uint32
+	Usage          uint32
+	ThreadID       uint32
+	OwnerProcessID uint32
+	BasePri        int32
+	DeltaPri       int32
+	Flags          uint32
+}
+
 type Systemtime struct {
 type Systemtime struct {
 	Year         uint16
 	Year         uint16
 	Month        uint16
 	Month        uint16
@@ -665,6 +690,7 @@ const (
 	SOCK_SEQPACKET = 5
 	SOCK_SEQPACKET = 5
 
 
 	IPPROTO_IP   = 0
 	IPPROTO_IP   = 0
+	IPPROTO_ICMP = 1
 	IPPROTO_IPV6 = 0x29
 	IPPROTO_IPV6 = 0x29
 	IPPROTO_TCP  = 6
 	IPPROTO_TCP  = 6
 	IPPROTO_UDP  = 17
 	IPPROTO_UDP  = 17
@@ -676,6 +702,7 @@ const (
 	SO_BROADCAST              = 32
 	SO_BROADCAST              = 32
 	SO_LINGER                 = 128
 	SO_LINGER                 = 128
 	SO_RCVBUF                 = 0x1002
 	SO_RCVBUF                 = 0x1002
+	SO_RCVTIMEO               = 0x1006
 	SO_SNDBUF                 = 0x1001
 	SO_SNDBUF                 = 0x1001
 	SO_UPDATE_ACCEPT_CONTEXT  = 0x700b
 	SO_UPDATE_ACCEPT_CONTEXT  = 0x700b
 	SO_UPDATE_CONNECT_CONTEXT = 0x7010
 	SO_UPDATE_CONNECT_CONTEXT = 0x7010
@@ -1147,6 +1174,28 @@ const (
 	REG_QWORD = REG_QWORD_LITTLE_ENDIAN
 	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 {
 type AddrinfoW struct {
 	Flags     int32
 	Flags     int32
 	Family    int32
 	Family    int32
@@ -1590,3 +1639,141 @@ const (
 	JobObjectNotificationLimitInformation2      = 34
 	JobObjectNotificationLimitInformation2      = 34
 	JobObjectSecurityLimitInformation           = 5
 	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
 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}}
+)

File diff suppressed because it is too large
+ 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 v0.0.0-20190604041725-da78bae5fc95
 github.com/hectane/go-acl
 github.com/hectane/go-acl
 github.com/hectane/go-acl/api
 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
 golang.org/x/sys/windows

+ 159 - 400
include/ZeroTierCore.h

@@ -19,7 +19,12 @@
 #ifndef ZT_ZEROTIER_API_H
 #ifndef ZT_ZEROTIER_API_H
 #define ZT_ZEROTIER_API_H
 #define ZT_ZEROTIER_API_H
 
 
+#ifdef __cplusplus
+#include <cstdint>
+extern "C" {
+#else
 #include <stdint.h>
 #include <stdint.h>
+#endif
 
 
 /* For struct sockaddr_storage, which is referenced here. */
 /* For struct sockaddr_storage, which is referenced here. */
 #if defined(_WIN32) || defined(_WIN64)
 #if defined(_WIN32) || defined(_WIN64)
@@ -39,10 +44,6 @@
 #define ZT_SDK_API
 #define ZT_SDK_API
 #endif
 #endif
 
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /****************************************************************************/
 /****************************************************************************/
 /* Core constants                                                           */
 /* Core constants                                                           */
 /****************************************************************************/
 /****************************************************************************/
@@ -91,11 +92,6 @@ extern "C" {
  */
  */
 #define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM)
 #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
  * Maximum length of network short name
  */
  */
@@ -258,78 +254,6 @@ extern "C" {
  */
  */
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 #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                                               */
 /* Structures and other types                                               */
 /****************************************************************************/
 /****************************************************************************/
@@ -397,35 +321,6 @@ enum ZT_ResultCode
  */
  */
 #define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
 #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
  * Status codes sent to status update callback when things happen
  */
  */
@@ -499,87 +394,23 @@ enum ZT_Event
 	 *
 	 *
 	 * Meta-data: ZT_UserMessage structure
 	 * 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
  * User message used with ZT_EVENT_USER_MESSAGE
@@ -624,6 +455,11 @@ typedef struct
 	 */
 	 */
 	uint64_t address;
 	uint64_t address;
 
 
+	/**
+	 * Actual identity object for this node
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	/**
 	 * Public identity in string-serialized form (safe to send to others)
 	 * 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
 	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
  * Virtual network configuration
  */
  */
@@ -1180,56 +983,6 @@ typedef struct
 	 */
 	 */
 	uint64_t trustedPathId;
 	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?
 	 * Is path alive?
 	 */
 	 */
@@ -1241,6 +994,15 @@ typedef struct
 	int preferred;
 	int preferred;
 } ZT_PeerPhysicalPath;
 } 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
  * Peer status result buffer
  */
  */
@@ -1251,6 +1013,11 @@ typedef struct
 	 */
 	 */
 	uint64_t address;
 	uint64_t address;
 
 
+	/**
+	 * Peer identity
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	/**
 	 * Remote major version or -1 if not known
 	 * Remote major version or -1 if not known
 	 */
 	 */
@@ -1281,11 +1048,6 @@ typedef struct
 	 */
 	 */
 	unsigned int pathCount;
 	unsigned int pathCount;
 
 
-	/**
-	 * Whether this peer was ever reachable via an aggregate link
-	 */
-	int hadAggregateLink;
-
 	/**
 	/**
 	 * Known network paths to peer
 	 * Known network paths to peer
 	 */
 	 */
@@ -1541,8 +1303,9 @@ typedef int (*ZT_PathCheckFunction)(
  *  (1) Node
  *  (1) Node
  *  (2) User pointer
  *  (2) User pointer
  *  (3) ZeroTier address (least significant 40 bits)
  *  (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
  * If provided this function will be occasionally called to get physical
  * addresses that might be tried to reach a ZeroTier address. It must
  * addresses that might be tried to reach a ZeroTier address. It must
@@ -1554,53 +1317,10 @@ typedef int (*ZT_PathLookupFunction)(
 	void *,                           /* User ptr */
 	void *,                           /* User ptr */
 	void *,                           /* Thread ptr */
 	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address (40 bits) */
 	uint64_t,                         /* ZeroTier address (40 bits) */
+	const ZT_Identity *,              /* Full identity of node */
 	int,                              /* Desired ss_family or -1 for any */
 	int,                              /* Desired ss_family or -1 for any */
 	struct sockaddr_storage *);       /* Result buffer */
 	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                                                               */
 /* C Node API                                                               */
 /****************************************************************************/
 /****************************************************************************/
@@ -1641,17 +1361,12 @@ struct ZT_Node_Callbacks
 	ZT_EventCallback eventCallback;
 	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;
 	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;
 	ZT_PathLookupFunction pathLookupFunction;
 };
 };
@@ -1750,60 +1465,6 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks(
 	int64_t now,
 	int64_t now,
 	volatile int64_t *nextBackgroundTaskDeadline);
 	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
  * 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);
 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 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 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
  * 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);
 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
  * 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

File diff suppressed because it is too large
+ 3 - 29
node/AES.cpp


+ 159 - 59
node/AES.hpp

@@ -18,35 +18,24 @@
 #include "Utils.hpp"
 #include "Utils.hpp"
 #include "SHA512.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 <wmmintrin.h>
 #include <emmintrin.h>
 #include <emmintrin.h>
 #include <smmintrin.h>
 #include <smmintrin.h>
-
 #define ZT_AES_AESNI 1
 #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 {
 namespace ZeroTier {
 
 
 /**
 /**
- * AES-256 and pals
+ * AES-256 and pals including GMAC, CTR, etc.
  */
  */
 class AES
 class AES
 {
 {
 public:
 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() {}
 	ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); }
 	ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); }
 	ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); }
 	ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); }
@@ -57,12 +46,11 @@ public:
 	ZT_ALWAYS_INLINE void init(const uint8_t key[32])
 	ZT_ALWAYS_INLINE void init(const uint8_t key[32])
 	{
 	{
 #ifdef ZT_AES_AESNI
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_init_aesni(key);
 			_init_aesni(key);
 			return;
 			return;
 		}
 		}
 #endif
 #endif
-
 		_initSW(key);
 		_initSW(key);
 	}
 	}
 
 
@@ -75,12 +63,11 @@ public:
 	ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const
 	ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const
 	{
 	{
 #ifdef ZT_AES_AESNI
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_encrypt_aesni(in,out);
 			_encrypt_aesni(in,out);
 			return;
 			return;
 		}
 		}
 #endif
 #endif
-
 		_encryptSW(in,out);
 		_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
 	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
 #ifdef ZT_AES_AESNI
-		if (likely(HW_ACCEL)) {
+		if (likely(Utils::CPUID.aes)) {
 			_gmac_aesni(iv,(const uint8_t *)in,len,out);
 			_gmac_aesni(iv,(const uint8_t *)in,len,out);
 			return;
 			return;
 		}
 		}
 #endif
 #endif
-
 		_gmacSW(iv,(const uint8_t *)in,len,out);
 		_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
 	ZT_ALWAYS_INLINE void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const
 	{
 	{
 #ifdef ZT_AES_AESNI
 #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;
 			return;
 		}
 		}
 #endif
 #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 out Output buffer to receive ciphertext
 	 * @param tag Output buffer to receive 64-bit authentication tag
 	 * @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__
 #ifdef __GNUC__
 		uint8_t __attribute__ ((aligned (16))) miv[12];
 		uint8_t __attribute__ ((aligned (16))) miv[12];
@@ -246,7 +203,7 @@ public:
 	 * @param tag Authentication tag supplied with message
 	 * @param tag Authentication tag supplied with message
 	 * @return True if authentication tags match and message appears authentic
 	 * @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__
 #ifdef __GNUC__
 		uint8_t __attribute__ ((aligned (16))) miv[12];
 		uint8_t __attribute__ ((aligned (16))) miv[12];
@@ -307,7 +264,7 @@ public:
 	 * @param k3 CTR IV keyed hash key
 	 * @param k3 CTR IV keyed hash key
 	 * @param k4 AES-CTR 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];
 		uint8_t k[32];
 		KBKDFHMACSHA384(masterKey,ZT_PROTO_KBKDF_LABEL_KEY_USE_AES_GMAC_SIV_K1,0,0,k);
 		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 _initSW(const uint8_t key[32]);
 	void _encryptSW(const uint8_t in[16],uint8_t out[16]) const;
 	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;
 	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]));
 		_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);
 		y = _mm_shuffle_epi8(y,shuf);
 		__m128i t1 = _mm_clmulepi64_si128(h,y,0x00);
 		__m128i t1 = _mm_clmulepi64_si128(h,y,0x00);
@@ -569,7 +527,7 @@ private:
 		t4 = _mm_xor_si128(t4,t5);
 		t4 = _mm_xor_si128(t4,t5);
 		return _mm_shuffle_epi8(t4,shuf);
 		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
 	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]);
 		t = _mm_aesenclast_si128(t,_k.ni.k[14]);
 		_mm_storeu_si128((__m128i *)out,_mm_xor_si128(y,t));
 		_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 ******************************************************/
 #endif /* ZT_AES_AESNI ******************************************************/
 };
 };
 
 

+ 98 - 17
node/Address.hpp

@@ -14,12 +14,15 @@
 #ifndef ZT_ADDRESS_HPP
 #ifndef ZT_ADDRESS_HPP
 #define 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 <string>
+#include <vector>
+#include <algorithm>
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "Utils.hpp"
 #include "Utils.hpp"
@@ -34,8 +37,7 @@ class Address
 {
 {
 public:
 public:
 	ZT_ALWAYS_INLINE Address() : _a(0) {}
 	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
 	 * @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(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; }
 	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
 	 * @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
 	 * @return Hexadecimal string
 	 */
 	 */
 	ZT_ALWAYS_INLINE char *toString(char buf[11]) const { return Utils::hex10(_a,buf); }
 	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
 	 * Check if this address is reserved
 	 *
 	 *
@@ -126,7 +122,7 @@ public:
 	 *
 	 *
 	 * @return True if address is reserved and may not be used
 	 * @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)
 	 * @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 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; }
 	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); }
 	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:
 private:
 	uint64_t _a;
 	uint64_t _a;
 };
 };

+ 10 - 5
node/AtomicCounter.hpp

@@ -24,25 +24,30 @@ namespace ZeroTier {
 
 
 /**
 /**
  * Simple atomic counter supporting increment and decrement
  * Simple atomic counter supporting increment and decrement
+ *
+ * This is used as the reference counter in reference counted objects that
+ * work with SharedPtr<>.
  */
  */
 class AtomicCounter
 class AtomicCounter
 {
 {
 public:
 public:
-	ZT_ALWAYS_INLINE AtomicCounter() { _v = 0; }
+	ZT_ALWAYS_INLINE AtomicCounter() : _v(0) {}
 
 
 	ZT_ALWAYS_INLINE int load() const
 	ZT_ALWAYS_INLINE int load() const
 	{
 	{
 #ifdef __GNUC__
 #ifdef __GNUC__
-		return __sync_or_and_fetch(const_cast<int *>(&_v),0);
+		return _v;
 #else
 #else
 		return _v.load();
 		return _v.load();
 #endif
 #endif
 	}
 	}
 
 
+	ZT_ALWAYS_INLINE void zero() { _v = 0; }
+
 	ZT_ALWAYS_INLINE int operator++()
 	ZT_ALWAYS_INLINE int operator++()
 	{
 	{
 #ifdef __GNUC__
 #ifdef __GNUC__
-		return __sync_add_and_fetch(&_v,1);
+		return __sync_add_and_fetch((int *)&_v,1);
 #else
 #else
 		return ++_v;
 		return ++_v;
 #endif
 #endif
@@ -51,7 +56,7 @@ public:
 	ZT_ALWAYS_INLINE int operator--()
 	ZT_ALWAYS_INLINE int operator--()
 	{
 	{
 #ifdef __GNUC__
 #ifdef __GNUC__
-		return __sync_sub_and_fetch(&_v,1);
+		return __sync_sub_and_fetch((int *)&_v,1);
 #else
 #else
 		return --_v;
 		return --_v;
 #endif
 #endif
@@ -62,7 +67,7 @@ private:
 	ZT_ALWAYS_INLINE const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
 	ZT_ALWAYS_INLINE const AtomicCounter &operator=(const AtomicCounter &) { return *this; }
 
 
 #ifdef __GNUC__
 #ifdef __GNUC__
-	int _v;
+	volatile int _v;
 #else
 #else
 	std::atomic_int _v;
 	std::atomic_int _v;
 #endif
 #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
 // Modified very slightly for ZeroTier One by Adam Ierymenko
 // This code remains in the public domain.
 // 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 "C25519.hpp"
 #include "SHA512.hpp"
 #include "SHA512.hpp"
-#include "Buffer.hpp"
-#include "Hashtable.hpp"
-#include "Mutex.hpp"
 
 
 #ifdef __WINDOWS__
 #ifdef __WINDOWS__
 #pragma warning(disable: 4146)
 #pragma warning(disable: 4146)
@@ -28,10 +23,7 @@ Derived from public domain code by D. J. Bernstein.
 
 
 namespace {
 namespace {
 
 
-#define crypto_int32 int32_t
 #define crypto_uint32 uint32_t
 #define crypto_uint32 uint32_t
-#define crypto_int64 int64_t
-#define crypto_uint64 uint64_t
 #define crypto_hash_sha512_BYTES 64
 #define crypto_hash_sha512_BYTES 64
 
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////
@@ -41,7 +33,7 @@ typedef uint8_t u8;
 typedef int32_t s32;
 typedef int32_t s32;
 typedef int64_t limb;
 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;
   unsigned i;
   for (i = 0; i < 10; i += 2) {
   for (i = 0; i < 10; i += 2) {
     output[0+i] = output[0+i] + in[0+i];
     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;
   unsigned i;
   for (i = 0; i < 10; ++i) {
   for (i = 0; i < 10; ++i) {
     output[i] = in[i] - output[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;
   unsigned i;
   for (i = 0; i < 10; ++i) {
   for (i = 0; i < 10; ++i) {
     output[i] = in[i] * scalar;
     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[0] =       ((limb) ((s32) in2[0])) * ((s32) in[0]);
   output[1] =       ((limb) ((s32) in2[0])) * ((s32) in[1]) +
   output[1] =       ((limb) ((s32) in2[0])) * ((s32) in[1]) +
                     ((limb) ((s32) in2[1])) * ((s32) in[0]);
                     ((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]);
   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] << 4;
   output[8] += output[18] << 1;
   output[8] += output[18] << 1;
   output[8] += output[18];
   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"
 #error "This code only works on a two's complement system"
 #endif
 #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. */
   /* High word of v; no shift needed. */
   const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
   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;
   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*/
   /* High word of v; no shift needed*/
   const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
   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;
   return (v + roundoff) >> 25;
 }
 }
 
 
-static ZT_ALWAYS_INLINE void freduce_coefficients(limb *output) {
+static inline void freduce_coefficients(limb *output) {
   unsigned i;
   unsigned i;
 
 
   output[10] = 0;
   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);
   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[0] =       ((limb) ((s32) in[0])) * ((s32) in[0]);
   output[1] =  2 *  ((limb) ((s32) in[0])) * ((s32) in[1]);
   output[1] =  2 *  ((limb) ((s32) in[0])) * ((s32) in[1]);
   output[2] =  2 * (((limb) ((s32) in[1])) * ((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);
   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) \
 #define F(n,start,shift,mask) \
   output[n] = ((((limb) input[start + 0]) | \
   output[n] = ((((limb) input[start + 0]) | \
                 ((limb) input[start + 1]) << 8 | \
                 ((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"
 #error "This code only works when >> does sign-extension on negative numbers"
 #endif
 #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 ^ b);
   a &= a << 16;
   a &= a << 16;
   a &= a << 8;
   a &= a << 8;
@@ -380,7 +372,7 @@ static ZT_ALWAYS_INLINE s32 s32_eq(s32 a, s32 b) {
   return a >> 31;
   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 -= b;
   /* a >= 0 iff a >= b. */
   /* a >= 0 iff a >= b. */
   return ~(a >> 31);
   return ~(a >> 31);
@@ -560,7 +552,7 @@ static inline void fmonty(limb *x2, limb *z2,  /* output 2Q */
   /* |z2|i| < 2^26 */
   /* |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;
   unsigned i;
   const s32 swap = (s32) -iswap;
   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 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 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 */
 	crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */
 	x -= 1; /* 4294967295: yes; 0..65534: 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;
 	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;
 	unsigned int x = a;
 	x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */
 	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;
 	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;
 	int i,rep;
 	for(rep=0;rep<4;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;
 	int i,rep;
 	for(rep=0;rep<2;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;
 	int i;
 	crypto_uint32 mm = equal(r->v[31],127);
 	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;
 	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;
 	int i;
 	for(i=0;i<32;i++) r->v[i] = x[i];
 	for(i=0;i<32;i++) r->v[i] = x[i];
 	r->v[31] &= 127;
 	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;
 	int i;
 	fe25519 y = *x;
 	fe25519 y = *x;
@@ -859,7 +851,7 @@ static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y)
 	return 1;
 	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;
 	int i;
 	crypto_uint32 mask = b;
 	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]);
 	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 t = *x;
 	fe25519_freeze(&t);
 	fe25519_freeze(&t);
 	return t.v[0] & 1;
 	return t.v[0] & 1;
 }
 }
 
 
-static ZT_ALWAYS_INLINE void fe25519_setone(fe25519 *r)
+static inline void fe25519_setone(fe25519 *r)
 {
 {
 	int i;
 	int i;
 	r->v[0] = 1;
 	r->v[0] = 1;
 	for(i=1;i<32;i++) r->v[i]=0;
 	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;
 	int i;
 	for(i=0;i<32;i++) r->v[i]=0;
 	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;
 	fe25519 t;
 	int i;
 	int i;
@@ -896,14 +888,14 @@ static ZT_ALWAYS_INLINE void fe25519_neg(fe25519 *r, const fe25519 *x)
 	fe25519_sub(r, r, &t);
 	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;
 	int i;
 	for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
 	for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i];
 	reduce_add_sub(r);
 	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;
 	int i;
 	crypto_uint32 t[32];
 	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);
 	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;
 	int i,j;
 	crypto_uint32 t[63];
 	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);
 	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)
 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 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 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;
 	unsigned int x = a;
 	x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */
 	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;
 	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 pb = 0;
 	crypto_uint32 b;
 	crypto_uint32 b;
@@ -1144,7 +1136,7 @@ static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64])
 	barrett_reduce(r, t);
 	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;
 	int i;
 	for(i=0;i<32;i++) r[i] = x->v[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);
 	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;
 	char carry;
 	int i;
 	int i;
@@ -1218,7 +1210,7 @@ static ZT_ALWAYS_INLINE void sc25519_window3(signed char r[85], const sc25519 *s
 	r[84] += carry;
 	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;
 	int i;
 	for(i=0;i<31;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}}}
  {{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->x, &p->x, &p->t);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->z, &p->z, &p->t);
 	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->x, &p->x, &p->t);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->y, &p->y, &p->z);
 	fe25519_mul(&r->z, &p->z, &p->t);
 	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);
 	p1p1_to_p2_2(r, p);
 	fe25519_mul(&r->t, &p->x, &p->y);
 	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 */
 /* 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->x, &p->x, b);
 	fe25519_cmov(&r->y, &p->y, 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 ub = b;
 	unsigned char uc = c;
 	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;
 	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 */
 	unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */
 	x >>= 63; /* 1: yes; 0: 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;
 	unsigned long long i;
 
 

+ 2 - 2
node/C25519.hpp

@@ -32,7 +32,7 @@ public:
 	/**
 	/**
 	 * Generate a C25519 elliptic curve key pair
 	 * 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);
 		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
 		_calcPubDH(pub,priv);
 		_calcPubDH(pub,priv);
@@ -53,7 +53,7 @@ public:
 	 * @tparam F Type of 'cond'
 	 * @tparam F Type of 'cond'
 	 */
 	 */
 	template<typename F>
 	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);
 		Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN);
 		_calcPubED(pub,priv); // do Ed25519 key -- bytes 32-63 of pub and priv
 		_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
 set(core_headers
 	Address.hpp
 	Address.hpp
-	AES.hpp
 	AtomicCounter.hpp
 	AtomicCounter.hpp
+	Buf.hpp
 	Buffer.hpp
 	Buffer.hpp
 	C25519.hpp
 	C25519.hpp
 	Capability.hpp
 	Capability.hpp
@@ -29,6 +29,7 @@ set(core_headers
 	Network.hpp
 	Network.hpp
 	NetworkConfig.hpp
 	NetworkConfig.hpp
 	Node.hpp
 	Node.hpp
+	OS.hpp
 	Packet.hpp
 	Packet.hpp
 	Path.hpp
 	Path.hpp
 	Peer.hpp
 	Peer.hpp
@@ -50,13 +51,14 @@ set(core_headers
 
 
 set(core_src
 set(core_src
 	AES.cpp
 	AES.cpp
-	AES-aesni.c
+	Buf.cpp
 	C25519.cpp
 	C25519.cpp
 	Credential.cpp
 	Credential.cpp
 	ECC384.cpp
 	ECC384.cpp
 	Identity.cpp
 	Identity.cpp
 	IncomingPacket.cpp
 	IncomingPacket.cpp
 	InetAddress.cpp
 	InetAddress.cpp
+	Locator.cpp
 	Membership.cpp
 	Membership.cpp
 	Network.cpp
 	Network.cpp
 	NetworkConfig.cpp
 	NetworkConfig.cpp
@@ -69,23 +71,10 @@ set(core_src
 	SelfAwareness.cpp
 	SelfAwareness.cpp
 	SHA512.cpp
 	SHA512.cpp
 	Switch.cpp
 	Switch.cpp
-	Trace.cpp
+	Topology.cpp
 	Utils.cpp
 	Utils.cpp
 )
 )
 
 
 add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers})
 add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers})
 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
 target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})
 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
 #ifndef ZT_CAPABILITY_HPP
 #define 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 "Constants.hpp"
 #include "Credential.hpp"
 #include "Credential.hpp"
@@ -61,7 +61,7 @@ class Capability : public Credential
 public:
 public:
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
 	static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; }
 
 
-	ZT_ALWAYS_INLINE Capability() :
+	inline Capability() :
 		_nwid(0),
 		_nwid(0),
 		_ts(0),
 		_ts(0),
 		_id(0),
 		_id(0),
@@ -80,7 +80,7 @@ public:
 	 * @param rules Network flow rules for this capability
 	 * @param rules Network flow rules for this capability
 	 * @param ruleCount Number of flow rules
 	 * @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),
 		_nwid(nwid),
 		_ts(ts),
 		_ts(ts),
 		_id(id),
 		_id(id),
@@ -94,32 +94,32 @@ public:
 	/**
 	/**
 	 * @return Rules -- see ruleCount() for size of array
 	 * @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()
 	 * @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
 	 * @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
 	 * @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
 	 * @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
 	 * @return Last 'to' address in chain of custody
 	 */
 	 */
-	ZT_ALWAYS_INLINE Address issuedTo() const
+	inline Address issuedTo() const
 	{
 	{
 		Address i2;
 		Address i2;
 		for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
 		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.
 	 * @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>
 	template<unsigned int C>
 	static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
 	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
 	// 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:
 private:
 	uint64_t _nwid;
 	uint64_t _nwid;

+ 17 - 17
node/CertificateOfMembership.hpp

@@ -14,8 +14,8 @@
 #ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
 #ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP
 #define ZT_CERTIFICATEOFMEMBERSHIP_HPP
 #define ZT_CERTIFICATEOFMEMBERSHIP_HPP
 
 
-#include <stdint.h>
-#include <string.h>
+#include <cstdint>
+#include <cstring>
 
 
 #include <string>
 #include <string>
 #include <stdexcept>
 #include <stdexcept>
@@ -69,7 +69,7 @@ class CertificateOfMembership : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 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
 	 * Reserved qualifier IDs
@@ -101,7 +101,7 @@ public:
 	/**
 	/**
 	 * Create an empty certificate of membership
 	 * Create an empty certificate of membership
 	 */
 	 */
-	ZT_ALWAYS_INLINE CertificateOfMembership() :
+	inline CertificateOfMembership() :
 		_qualifierCount(0),
 		_qualifierCount(0),
 		_signatureLength(0) {}
 		_signatureLength(0) {}
 
 
@@ -113,7 +113,7 @@ public:
 	 * @param nwid Network ID
 	 * @param nwid Network ID
 	 * @param issuedTo Certificate recipient
 	 * @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].id = COM_RESERVED_ID_TIMESTAMP;
 		_qualifiers[0].value = timestamp;
 		_qualifiers[0].value = timestamp;
@@ -135,22 +135,22 @@ public:
 	 * @param startAt Position to start in buffer
 	 * @param startAt Position to start in buffer
 	 */
 	 */
 	template<unsigned int C>
 	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
 	 * @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
 	 * @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
 	 * @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) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
 			if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
@@ -162,7 +162,7 @@ public:
 	/**
 	/**
 	 * @return Address to which this cert was issued
 	 * @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) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
 			if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO)
@@ -174,7 +174,7 @@ public:
 	/**
 	/**
 	 * @return Network ID for which this cert was issued
 	 * @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) {
 		for(unsigned int i=0;i<_qualifierCount;++i) {
 			if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID)
 			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
 	 * Compare two certificates for parameter agreement
@@ -294,17 +294,17 @@ public:
 	 * @param RR Runtime environment for looking up peers
 	 * @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
 	 * @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
 	 * @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
 	 * @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>
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b) const
 	inline void serialize(Buffer<C> &b) const
@@ -369,7 +369,7 @@ public:
 		return (p - startAt);
 		return (p - startAt);
 	}
 	}
 
 
-	ZT_ALWAYS_INLINE bool operator==(const CertificateOfMembership &c) const
+	inline bool operator==(const CertificateOfMembership &c) const
 	{
 	{
 		if (_signedBy != c._signedBy)
 		if (_signedBy != c._signedBy)
 			return false;
 			return false;
@@ -385,7 +385,7 @@ public:
 		}
 		}
 		return (memcmp(_signature,c._signature,_signatureLength) == 0);
 		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:
 private:
 	struct _Qualifier
 	struct _Qualifier

+ 23 - 23
node/CertificateOfOwnership.hpp

@@ -14,10 +14,10 @@
 #ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
 #ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP
 #define 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 "Constants.hpp"
 #include "Credential.hpp"
 #include "Credential.hpp"
@@ -46,7 +46,7 @@ class CertificateOfOwnership : public Credential
 	friend class Credential;
 	friend class Credential;
 
 
 public:
 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
 	enum Thing
 	{
 	{
@@ -56,12 +56,12 @@ public:
 		THING_IPV6_ADDRESS = 3
 		THING_IPV6_ADDRESS = 3
 	};
 	};
 
 
-	ZT_ALWAYS_INLINE CertificateOfOwnership()
+	inline CertificateOfOwnership()
 	{
 	{
 		memset(reinterpret_cast<void *>(this),0,sizeof(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));
 		memset(reinterpret_cast<void *>(this),0,sizeof(CertificateOfOwnership));
 		_networkId = nwid;
 		_networkId = nwid;
@@ -70,19 +70,19 @@ public:
 		_issuedTo = issuedTo;
 		_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)
 		if (ip.ss_family == AF_INET)
 			return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast<const struct sockaddr_in *>(&ip)->sin_addr.s_addr),4);
 			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;
 		return false;
 	}
 	}
 
 
-	ZT_ALWAYS_INLINE bool owns(const MAC &mac) const
+	inline bool owns(const MAC &mac) const
 	{
 	{
 		uint8_t tmp[6];
 		uint8_t tmp[6];
 		mac.copyTo(tmp,6);
 		mac.copyTo(tmp,6);
@@ -136,7 +136,7 @@ public:
 		return false;
 		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>
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const
@@ -206,10 +206,10 @@ public:
 	}
 	}
 
 
 	// Provides natural sort order by ID
 	// 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:
 private:
 	inline bool _owns(const Thing &t,const void *v,unsigned int l) const
 	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
 #ifndef ZT_CONSTANTS_HPP
 #define ZT_CONSTANTS_HPP
 #define ZT_CONSTANTS_HPP
 
 
-/****************************************************************************/
-/* Core includes and OS/platform setup stuff                                */
-/****************************************************************************/
-
 #include "../include/ZeroTierCore.h"
 #include "../include/ZeroTierCore.h"
+#include "OS.hpp"
 
 
 #if __has_include("version.h")
 #if __has_include("version.h")
 #include "version.h"
 #include "version.h"
@@ -29,187 +26,25 @@
 #define ZEROTIER_ONE_VERSION_BUILD 255
 #define ZEROTIER_ONE_VERSION_BUILD 255
 #endif
 #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
  * Length of a ZeroTier address in bytes
  */
  */
 #define ZT_ADDRESS_LENGTH 5
 #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
  * Addresses beginning with this byte are reserved for the joy of in-band signaling
  */
  */
 #define ZT_ADDRESS_RESERVED_PREFIX 0xff
 #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)
  * Default virtual network MTU (not physical)
@@ -282,246 +117,34 @@
 #define ZT_RELAY_MAX_HOPS 4
 #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
  * See https://conferences.sigcomm.org/imc/2010/papers/p260.pdf for
  * some real world data on NAT UDP timeouts. From the paper: "the
  * some real world data on NAT UDP timeouts. From the paper: "the
  * lowest measured timeout when a binding has seen bidirectional
  * lowest measured timeout when a binding has seen bidirectional
  * traffic is 54 sec." 30 seconds is faster than really necessary.
  * 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
  * Delay between requests for updated network autoconf information
@@ -531,15 +154,6 @@
  */
  */
 #define ZT_NETWORK_AUTOCONF_DELAY 60000
 #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
  * Sanity limit on maximum bridge routes
  *
  *
@@ -566,34 +180,10 @@
  */
  */
 #define ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH 120000
 #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)
  * 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)
  * WHOIS rate limit (we allow these to be pretty fast)
@@ -632,12 +222,7 @@
  */
  */
 #define ZT_SIGNATURE_BUFFER_SIZE 96
 #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_C25519 0
 #define ZT_CRYPTO_ALG_P384 1
 #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_INVALID_CRYPTOGRAPHIC_TOKEN 202
 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203
 #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
 #endif

+ 1 - 1
node/Credential.cpp

@@ -26,7 +26,7 @@
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 template<typename CRED>
 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 Address signedBy(credential.signer());
 	const uint64_t networkId = credential.networkId();
 	const uint64_t networkId = credential.networkId();

+ 4 - 4
node/Credential.hpp

@@ -18,10 +18,10 @@
 #include <memory>
 #include <memory>
 #include <stdexcept>
 #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"
 #include "Constants.hpp"
 
 

+ 23 - 23
node/Dictionary.hpp

@@ -19,7 +19,7 @@
 #include "Buffer.hpp"
 #include "Buffer.hpp"
 #include "Address.hpp"
 #include "Address.hpp"
 
 
-#include <stdint.h>
+#include <cstdint>
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -49,9 +49,9 @@ template<unsigned int C>
 class Dictionary
 class Dictionary
 {
 {
 public:
 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) {
 		for(unsigned int i=0;i<C;++i) {
 			if ((s)&&(i < len)) {
 			if ((s)&&(i < len)) {
@@ -62,15 +62,15 @@ public:
 		}
 		}
 		_d[C - 1] = (char)0;
 		_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);
 		memcpy(_d,d._d,C);
 		return *this;
 		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
 	 * Load a dictionary from a C-string
@@ -78,7 +78,7 @@ public:
 	 * @param s Dictionary in string form
 	 * @param s Dictionary in string form
 	 * @return False if 's' was longer than our capacity
 	 * @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) {
 		for(unsigned int i=0;i<C;++i) {
 			if (s) {
 			if (s) {
@@ -94,12 +94,12 @@ public:
 	/**
 	/**
 	 * Delete all entries
 	 * 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
 	 * @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) {
 		for(unsigned int i=0;i<C;++i) {
 			if (!_d[i])
 			if (!_d[i])
@@ -217,7 +217,7 @@ public:
 	 * @tparam BC Buffer capacity (usually inferred)
 	 * @tparam BC Buffer capacity (usually inferred)
 	 */
 	 */
 	template<unsigned int BC>
 	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);
 		const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
 		if (r >= 0) {
 		if (r >= 0) {
@@ -236,7 +236,7 @@ public:
 	 * @param dfl Default value if not found in dictionary
 	 * @param dfl Default value if not found in dictionary
 	 * @return Boolean value of key or 'dfl' if not found
 	 * @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];
 		char tmp[4];
 		if (this->get(key,tmp,sizeof(tmp)) >= 0)
 		if (this->get(key,tmp,sizeof(tmp)) >= 0)
@@ -251,7 +251,7 @@ public:
 	 * @param dfl Default value or 0 if unspecified
 	 * @param dfl Default value or 0 if unspecified
 	 * @return Decoded hex UInt value or 'dfl' if not found
 	 * @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];
 		char tmp[128];
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
@@ -266,7 +266,7 @@ public:
 	 * @param dfl Default value or 0 if unspecified
 	 * @param dfl Default value or 0 if unspecified
 	 * @return Decoded hex UInt value or 'dfl' if not found
 	 * @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];
 		char tmp[128];
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
 		if (this->get(key,tmp,sizeof(tmp)) >= 1)
@@ -366,7 +366,7 @@ public:
 	/**
 	/**
 	 * Add a boolean as a '1' or a '0'
 	 * 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);
 		return this->add(key,(value) ? "1" : "0",1);
 	}
 	}
@@ -374,7 +374,7 @@ public:
 	/**
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 * 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];
 		char tmp[32];
 		return this->add(key,Utils::hex(value,tmp),-1);
 		return this->add(key,Utils::hex(value,tmp),-1);
@@ -383,7 +383,7 @@ public:
 	/**
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 * 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];
 		char tmp[32];
 		if (value >= 0) {
 		if (value >= 0) {
@@ -397,7 +397,7 @@ public:
 	/**
 	/**
 	 * Add a 64-bit integer (unsigned) as a hex value
 	 * 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];
 		char tmp[32];
 		return this->add(key,Utils::hex(a.toInt(),tmp),-1);
 		return this->add(key,Utils::hex(a.toInt(),tmp),-1);
@@ -409,7 +409,7 @@ public:
 	 * @tparam BC Buffer capacity (usually inferred)
 	 * @tparam BC Buffer capacity (usually inferred)
 	 */
 	 */
 	template<unsigned int BC>
 	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());
 		return this->add(key,(const char *)value.data(),(int)value.size());
 	}
 	}
@@ -418,7 +418,7 @@ public:
 	 * @param key Key to check
 	 * @param key Key to check
 	 * @return True if key is present
 	 * @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];
 		char tmp[2];
 		return (this->get(key,tmp,2) >= 0);
 		return (this->get(key,tmp,2) >= 0);
@@ -427,10 +427,10 @@ public:
 	/**
 	/**
 	 * @return Value of C template parameter
 	 * @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:
 private:
 	char _d[C];
 	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
 // 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 "Constants.hpp"
 #include "ECC384.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. */
 /* 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;
 	int i;
 	for(i = NUM_ECC_DIGITS-1; i >= 0; --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.
 /* Computes p_result = (p_left + p_right) % p_mod.
    Assumes that p_left < p_mod and p_right < p_mod, p_result != 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);
 	uint64_t l_carry = vli_add(p_result, p_left, p_right);
 	if(l_carry || vli_cmp(p_result, p_mod) >= 0)
 	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.
 /* Computes p_result = (p_left - p_right) % p_mod.
    Assumes that p_left < p_mod and p_right < p_mod, p_result != 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);
 	uint64_t l_borrow = vli_sub(p_result, p_left, p_right);
 	if(l_borrow)
 	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)
 	 * @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))),
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
 		_bc(bc),
 		_bc(bc),
 		_s(0)
 		_s(0)
@@ -105,7 +105,7 @@ public:
 			_t[i] = (_Bucket *)0;
 			_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))),
 		_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))),
 		_bc(ht._bc),
 		_bc(ht._bc),
 		_s(ht._s)
 		_s(ht._s)
@@ -125,13 +125,13 @@ public:
 		}
 		}
 	}
 	}
 
 
-	ZT_ALWAYS_INLINE ~Hashtable()
+	inline ~Hashtable()
 	{
 	{
 		this->clear();
 		this->clear();
 		::free(_t);
 		::free(_t);
 	}
 	}
 
 
-	ZT_ALWAYS_INLINE Hashtable &operator=(const Hashtable<K,V> &ht)
+	inline Hashtable &operator=(const Hashtable<K,V> &ht)
 	{
 	{
 		this->clear();
 		this->clear();
 		if (ht._s) {
 		if (ht._s) {
@@ -149,7 +149,7 @@ public:
 	/**
 	/**
 	 * Erase all entries
 	 * Erase all entries
 	 */
 	 */
-	ZT_ALWAYS_INLINE void clear()
+	inline void clear()
 	{
 	{
 		if (_s) {
 		if (_s) {
 			for(unsigned long i=0;i<_bc;++i) {
 			for(unsigned long i=0;i<_bc;++i) {
@@ -168,7 +168,7 @@ public:
 	/**
 	/**
 	 * @return Vector of all keys
 	 * @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;
 		typename std::vector<K> k;
 		if (_s) {
 		if (_s) {
@@ -191,7 +191,7 @@ public:
 	 * @tparam Type of V (generally inferred)
 	 * @tparam Type of V (generally inferred)
 	 */
 	 */
 	template<typename C>
 	template<typename C>
-	ZT_ALWAYS_INLINE void appendKeys(C &v) const
+	inline void appendKeys(C &v) const
 	{
 	{
 		if (_s) {
 		if (_s) {
 			for(unsigned long i=0;i<_bc;++i) {
 			for(unsigned long i=0;i<_bc;++i) {
@@ -207,7 +207,7 @@ public:
 	/**
 	/**
 	 * @return Vector of all entries (pairs of K,V)
 	 * @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;
 		typename std::vector< std::pair<K,V> > k;
 		if (_s) {
 		if (_s) {
@@ -227,7 +227,7 @@ public:
 	 * @param k Key
 	 * @param k Key
 	 * @return Pointer to value or NULL if not found
 	 * @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];
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
 		while (b) {
@@ -237,14 +237,14 @@ public:
 		}
 		}
 		return (V *)0;
 		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 k Key
 	 * @param v Value to fill with result
 	 * @param v Value to fill with result
 	 * @return True if value was found and set (if false, v is not modified)
 	 * @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];
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
 		while (b) {
@@ -261,7 +261,7 @@ public:
 	 * @param k Key to check
 	 * @param k Key to check
 	 * @return True if key is present
 	 * @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];
 		_Bucket *b = _t[_hc(k) % _bc];
 		while (b) {
 		while (b) {
@@ -276,7 +276,7 @@ public:
 	 * @param k Key
 	 * @param k Key
 	 * @return True if value was present
 	 * @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;
 		const unsigned long bidx = _hc(k) % _bc;
 		_Bucket *lastb = (_Bucket *)0;
 		_Bucket *lastb = (_Bucket *)0;
@@ -301,7 +301,7 @@ public:
 	 * @param v Value
 	 * @param v Value
 	 * @return Reference to value in table
 	 * @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);
 		const unsigned long h = _hc(k);
 		unsigned long bidx = h % _bc;
 		unsigned long bidx = h % _bc;
@@ -331,7 +331,7 @@ public:
 	 * @param k Key
 	 * @param k Key
 	 * @return Value, possibly newly created
 	 * @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);
 		const unsigned long h = _hc(k);
 		unsigned long bidx = h % _bc;
 		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(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); }
 	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;
 		const unsigned long nc = _bc * 2;
 		_Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc));
 		_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 "Constants.hpp"
 #include "Identity.hpp"
 #include "Identity.hpp"
@@ -100,8 +98,13 @@ void Identity::generate(const Type t)
 	delete [] genmem;
 	delete [] genmem;
 
 
 	if (t == P384) {
 	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);
 		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:
 		case C25519:
 			break;
 			break;
 		case P384:
 		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;
 				return false;
 		default:
 		default:
 			return false;
 			return false;
@@ -135,6 +141,108 @@ bool Identity::locallyValidate() const
 	return false;
 	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
 char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
 {
 {
 	switch(_type) {
 	switch(_type) {
@@ -224,7 +332,7 @@ bool Identity::fromString(const char *str)
 				switch(_type) {
 				switch(_type) {
 
 
 					case C25519:
 					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();
 							_address.zero();
 							return false;
 							return false;
 						}
 						}
@@ -245,7 +353,7 @@ bool Identity::fromString(const char *str)
 					switch(_type) {
 					switch(_type) {
 
 
 						case C25519:
 						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();
 								_address.zero();
 								return false;
 								return false;
 							} else {
 							} else {
@@ -278,3 +386,99 @@ bool Identity::fromString(const char *str)
 }
 }
 
 
 } // namespace ZeroTier
 } // 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
 #ifndef ZT_IDENTITY_HPP
 #define ZT_IDENTITY_HPP
 #define ZT_IDENTITY_HPP
 
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <cstdio>
+#include <cstdlib>
 
 
 #include "Constants.hpp"
 #include "Constants.hpp"
 #include "Utils.hpp"
 #include "Utils.hpp"
@@ -27,6 +27,11 @@
 
 
 #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024
 #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 {
 namespace ZeroTier {
 
 
 /**
 /**
@@ -52,15 +57,17 @@ public:
 	};
 	};
 
 
 	ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast<void *>(this),0,sizeof(Identity)); }
 	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)
 	 * 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)); }
 	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; }
 	ZT_ALWAYS_INLINE Type type() const { return _type; }
 
 
 	/**
 	/**
 	 * Generate a new identity (address, key pair)
 	 * 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
 	 * @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
 	 * 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; }
 	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 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)
 	 * Sign a message with this identity (private key required)
@@ -142,31 +120,7 @@ public:
 	 * @param siglen Length of buffer
 	 * @param siglen Length of buffer
 	 * @return Number of bytes actually written to sig or 0 on error
 	 * @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
 	 * Verify a message signature against this identity
@@ -177,21 +131,7 @@ public:
 	 * @param siglen Length of signature in bytes
 	 * @param siglen Length of signature in bytes
 	 * @return True if signature validates and data integrity checks
 	 * @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
 	 * Shortcut method to perform key agreement with another identity
@@ -202,78 +142,21 @@ public:
 	 * @param key Result parameter to fill with key bytes
 	 * @param key Result parameter to fill with key bytes
 	 * @return Was agreement successful?
 	 * @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
 	 * @return This identity's address
 	 */
 	 */
 	ZT_ALWAYS_INLINE const Address &address() const { return _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)
 	 * Serialize this identity (binary)
 	 *
 	 *
 	 * @param b Destination buffer to append to
 	 * @param b Destination buffer to append to
 	 * @param includePrivate If true, include private key component (if present) (default: false)
 	 * @param includePrivate If true, include private key component (if present) (default: false)
-	 * @throws std::out_of_range Buffer too small
 	 */
 	 */
 	template<unsigned int C>
 	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);
 		_address.appendTo(b);
 		switch(_type) {
 		switch(_type) {
@@ -291,9 +174,7 @@ public:
 
 
 			case P384:
 			case P384:
 				b.append((uint8_t)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)) {
 				if ((_hasPrivate)&&(includePrivate)) {
 					b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE));
 					b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE));
 					b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
 					b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
@@ -301,7 +182,7 @@ public:
 				} else {
 				} else {
 					b.append((uint8_t)0);
 					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;
 				break;
 
 
 		}
 		}
@@ -316,11 +197,9 @@ public:
 	 * @param b Buffer containing serialized data
 	 * @param b Buffer containing serialized data
 	 * @param startAt Index within buffer of serialized data (default: 0)
 	 * @param startAt Index within buffer of serialized data (default: 0)
 	 * @return Length of serialized data read from buffer
 	 * @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>
 	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;
 		_hasPrivate = false;
 		unsigned int p = startAt;
 		unsigned int p = startAt;
@@ -347,12 +226,8 @@ public:
 				break;
 				break;
 
 
 			case P384:
 			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++];
 				pkl = (unsigned int)b[p++];
 				if (pkl) {
 				if (pkl) {
 					if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE))
 					if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE))
@@ -365,7 +240,7 @@ public:
 				} else {
 				} else {
 					_hasPrivate = false;
 					_hasPrivate = false;
 				}
 				}
-				p += b.template at<uint16_t>(p) + 2;
+				p += b.template at<uint8_t>(p) + 2;
 				break;
 				break;
 
 
 			default:
 			default:
@@ -381,7 +256,7 @@ public:
 	 *
 	 *
 	 * @param includePrivate If true, include private key (if it exists)
 	 * @param includePrivate If true, include private key (if it exists)
 	 * @param buf Buffer to store string
 	 * @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;
 	char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const;
 
 
@@ -399,18 +274,15 @@ public:
 	/**
 	/**
 	 * @return True if this identity contains something
 	 * @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
 	ZT_ALWAYS_INLINE bool operator==(const Identity &id) const
 	{
 	{
 		if ((_address == id._address)&&(_type == id._type)) {
 		if ((_address == id._address)&&(_type == id._type)) {
 			switch(_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;
 		return false;
@@ -424,10 +296,9 @@ public:
 				return true;
 				return true;
 			if (_type == id._type) {
 			if (_type == id._type) {
 				switch(_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]); }
 	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:
 private:
 	Address _address;
 	Address _address;
-	Type _type;
+	Type _type; // _type determines which fields in _priv and _pub are used
 	bool _hasPrivate;
 	bool _hasPrivate;
 	ZT_PACKED_STRUCT(struct { // don't re-order these
 	ZT_PACKED_STRUCT(struct { // don't re-order these
 		uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
 		uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN];
 		uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
 		uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE];
 	}) _priv;
 	}) _priv;
 	ZT_PACKED_STRUCT(struct { // don't re-order these
 	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;
 	}) _pub;
 };
 };
 
 

+ 332 - 594
node/IncomingPacket.cpp

@@ -29,238 +29,49 @@
 #include "Revocation.hpp"
 #include "Revocation.hpp"
 #include "Trace.hpp"
 #include "Trace.hpp"
 
 
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
+#include <cstring>
+#include <cstdlib>
 
 
 #include <list>
 #include <list>
 
 
 namespace ZeroTier {
 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 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;
 	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) {
 	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;
 		return true;
 	}
 	}
 	if (fromAddress != id.address()) {
 	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;
 		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
 				// Identity is different from the one we already have -- address collision
 
 
 				// Check rate limits
 				// Check rate limits
-				if (!RR->node->rateGateIdentityVerification(now,_path->address()))
+				if (!RR->node->rateGateIdentityVerification(now,path->address()))
 					return true;
 					return true;
 
 
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 				uint8_t key[ZT_PEER_SECRET_KEY_LENGTH];
 				if (RR->identity.agree(id,key)) {
 				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);
 						Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR);
 						outp.append((uint8_t)Packet::VERB_HELLO);
 						outp.append((uint8_t)Packet::VERB_HELLO);
 						outp.append((uint64_t)pid);
 						outp.append((uint64_t)pid);
 						outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
 						outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
 						outp.armor(key,true);
 						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 {
 					} else {
-						RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC");
+						RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC");
 					}
 					}
 				} else {
 				} else {
-					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity");
+					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid identity");
 				}
 				}
 
 
 				return true;
 				return true;
 			} else {
 			} else {
 				// Identity is the same as the one we already have -- check packet integrity
 				// 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;
 					return true;
 				}
 				}
 
 
@@ -309,26 +120,26 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 
 
 		// Sanity check: this basically can't happen
 		// Sanity check: this basically can't happen
 		if (alreadyAuthenticated) {
 		if (alreadyAuthenticated) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state");
+			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"illegal alreadyAuthenticated state");
 			return true;
 			return true;
 		}
 		}
 
 
 		// Check rate limits
 		// 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;
 			return true;
 		}
 		}
 
 
 		// Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap)
 		// 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));
 		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;
 			return true;
 		}
 		}
 
 
 		// Check that identity's address is valid as per the derivation function
 		// Check that identity's address is valid as per the derivation function
 		if (!id.locallyValidate()) {
 		if (!id.locallyValidate()) {
-			RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity");
+			RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"invalid identity");
 			return true;
 			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!
 	// 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.
 	// 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;
 		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_MAJOR);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
 	outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
 	outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
-	_path->address().serialize(outp);
+	path->address().serialize(outp);
 	outp.armor(peer->key(),true);
 	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->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;
 	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;
 	uint64_t networkId = 0;
 
 
 	if (!RR->node->expectingReplyTo(inRePacketId))
 	if (!RR->node->expectingReplyTo(inRePacketId))
@@ -382,34 +255,34 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 	switch(inReVerb) {
 	switch(inReVerb) {
 
 
 		case Packet::VERB_HELLO: {
 		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)
 			if (vProto < ZT_PROTO_VERSION_MIN)
 				return true;
 				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;
 					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)
 					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);
 			peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 		}	break;
 		}	break;
 
 
 		case Packet::VERB_WHOIS:
 		case Packet::VERB_WHOIS:
 			if (RR->topology->isRoot(peer->identity())) {
 			if (RR->topology->isRoot(peer->identity())) {
 				unsigned int p = ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY;
 				unsigned int p = ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY;
-				while (p < size()) {
+				while (p < pkt.size()) {
 					try {
 					try {
 						Identity id;
 						Identity id;
-						p += id.deserialize(*this,p);
+						p += id.deserialize(pkt,p);
 						if (id)
 						if (id)
 							RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
 							RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
 					} catch ( ... ) {
 					} catch ( ... ) {
@@ -420,47 +293,36 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 			break;
 			break;
 
 
 		case Packet::VERB_NETWORK_CONFIG_REQUEST: {
 		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));
 			const SharedPtr<Network> network(RR->node->network(networkId));
 			if (network)
 			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;
 		}	break;
 
 
 		case Packet::VERB_MULTICAST_GATHER: {
 		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;
 		}	break;
 
 
 		default: 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;
 	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()))
 	if (!peer->rateGateInboundWhoisRequest(RR->node->now()))
 		return true;
 		return true;
 
 
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	outp.append((unsigned char)Packet::VERB_WHOIS);
 	outp.append((unsigned char)Packet::VERB_WHOIS);
-	outp.append(packetId());
+	outp.append(pkt.packetId());
 
 
 	unsigned int count = 0;
 	unsigned int count = 0;
 	unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
 	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;
 		ptr += ZT_ADDRESS_LENGTH;
 
 
 		const Identity id(RR->topology->getIdentity(tPtr,addr));
 		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) {
 	if (count > 0) {
 		outp.armor(peer->key(),true);
 		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;
 	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())) {
 	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));
 		const SharedPtr<Peer> rendezvousWith(RR->topology->get(with));
 		if (rendezvousWith) {
 		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))) {
 			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;
 	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));
 	const SharedPtr<Network> network(RR->node->network(nwid));
 	if (network) {
 	if (network) {
 		if (network->gate(tPtr,peer)) {
 		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 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)
 				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);
 					RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 			}
 			}
 		} else {
 		} else {
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 			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;
 	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));
 	const SharedPtr<Network> network(RR->node->network(nwid));
 	if (network) {
 	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;
 		unsigned int comLen = 0;
 		if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
 		if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers
 			CertificateOfMembership com;
 			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)
 			if (com)
 				network->addCredential(tPtr,com);
 				network->addCredential(tPtr,com);
 		}
 		}
 
 
 		if (!network->gate(tPtr,peer)) {
 		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;
 			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())) {
 			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;
 				return true;
 			}
 			}
 
 
@@ -567,20 +430,20 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 						if (network->config().permitsBridging(peer->address())) {
 						if (network->config().permitsBridging(peer->address())) {
 							network->learnBridgeRoute(from,peer->address());
 							network->learnBridgeRoute(from,peer->address());
 						} else {
 						} 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;
 							return true;
 						}
 						}
 					} else if (to != network->mac()) {
 					} else if (to != network->mac()) {
 						if (to.isMulticast()) {
 						if (to.isMulticast()) {
 							if (network->config().multicastLimit == 0) {
 							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;
 								return true;
 							}
 							}
 						} else if (!network->config().permitsBridging(RR->identity.address())) {
 						} 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;
 							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);
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((uint8_t)Packet::VERB_EXT_FRAME);
 			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.append((uint64_t)nwid);
 			outp.armor(peer->key(),true);
 			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;
 	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()))
 	if (!peer->rateGateEchoRequest(RR->node->now()))
 		return true;
 		return true;
 
 
-	const uint64_t pid = packetId();
+	const uint64_t pid = pkt.packetId();
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 	outp.append((unsigned char)Packet::VERB_ECHO);
 	outp.append((unsigned char)Packet::VERB_ECHO);
 	outp.append((uint64_t)pid);
 	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);
 	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;
 	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;
 	CertificateOfMembership com;
 	Capability cap;
 	Capability cap;
 	Tag tag;
 	Tag tag;
@@ -666,8 +498,8 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 	SharedPtr<Network> network;
 	SharedPtr<Network> network;
 
 
 	unsigned int p = ZT_PACKET_IDX_PAYLOAD;
 	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) {
 		if (com) {
 			network = RR->node->network(com.networkId());
 			network = RR->node->network(com.networkId());
 			if (network) {
 			if (network) {
@@ -678,10 +510,10 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 	}
 	}
 	++p; // skip trailing 0 after COMs if present
 	++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) {
 		for(unsigned int i=0;i<numCapabilities;++i) {
-			p += cap.deserialize(*this,p);
+			p += cap.deserialize(pkt,p);
 			if ((!network)||(network->id() != cap.networkId()))
 			if ((!network)||(network->id() != cap.networkId()))
 				network = RR->node->network(cap.networkId());
 				network = RR->node->network(cap.networkId());
 			if (network) {
 			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) {
 		for(unsigned int i=0;i<numTags;++i) {
-			p += tag.deserialize(*this,p);
+			p += tag.deserialize(pkt,p);
 			if ((!network)||(network->id() != tag.networkId()))
 			if ((!network)||(network->id() != tag.networkId()))
 				network = RR->node->network(tag.networkId());
 				network = RR->node->network(tag.networkId());
 			if (network) {
 			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) {
 		for(unsigned int i=0;i<numRevocations;++i) {
-			p += revocation.deserialize(*this,p);
+			p += revocation.deserialize(pkt,p);
 			if ((!network)||(network->id() != revocation.networkId()))
 			if ((!network)||(network->id() != revocation.networkId()))
 				network = RR->node->network(revocation.networkId());
 				network = RR->node->network(revocation.networkId());
 			if (network) {
 			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) {
 		for(unsigned int i=0;i<numCoos;++i) {
-			p += coo.deserialize(*this,p);
+			p += coo.deserialize(pkt,p);
 			if ((!network)||(network->id() != coo.networkId()))
 			if ((!network)||(network->id() != coo.networkId()))
 				network = RR->node->network(coo.networkId());
 				network = RR->node->network(coo.networkId());
 			if (network) {
 			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;
 	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) {
 	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);
 		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 {
 	} else {
 		Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
 		Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
 		outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
 		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((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
 		outp.append(nwid);
 		outp.append(nwid);
 		outp.armor(peer->key(),true);
 		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;
 	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) {
 	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) {
 		if (configUpdateId) {
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((uint8_t)Packet::VERB_ECHO);
 			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)network->id());
 			outp.append((uint64_t)configUpdateId);
 			outp.append((uint64_t)configUpdateId);
 			outp.armor(peer->key(),true);
 			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;
 	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));
 	const SharedPtr<Network> network(RR->node->network(nwid));
 
 
 	if ((flags & 0x01) != 0) {
 	if ((flags & 0x01) != 0) {
 		try {
 		try {
 			CertificateOfMembership com;
 			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))
 			if ((com)&&(network))
 				network->addCredential(tPtr,com);
 				network->addCredential(tPtr,com);
 		} catch ( ... ) {} // discard invalid COMs
 		} catch ( ... ) {} // discard invalid COMs
@@ -800,7 +632,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 
 
 	if (network) {
 	if (network) {
 		if (!network->gate(tPtr,peer)) {
 		if (!network->gate(tPtr,peer)) {
-			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			_sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path);
 			return false;
 			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;
 	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();
 	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;
 	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;
 		ZT_UserMessage um;
 		um.origin = peer->address().toInt();
 		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));
 		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;
 	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
 } // namespace ZeroTier

+ 1 - 34
node/IncomingPacket.hpp

@@ -14,8 +14,6 @@
 #ifndef ZT_INCOMINGPACKET_HPP
 #ifndef ZT_INCOMINGPACKET_HPP
 #define ZT_INCOMINGPACKET_HPP
 #define ZT_INCOMINGPACKET_HPP
 
 
-#include <stdexcept>
-
 #include "Packet.hpp"
 #include "Packet.hpp"
 #include "Path.hpp"
 #include "Path.hpp"
 #include "Utils.hpp"
 #include "Utils.hpp"
@@ -43,17 +41,10 @@ namespace ZeroTier {
 class RuntimeEnvironment;
 class RuntimeEnvironment;
 class Network;
 class Network;
 
 
-/**
- * Subclass of packet that handles the decoding of it
- */
 class IncomingPacket : public Packet
 class IncomingPacket : public Packet
 {
 {
 public:
 public:
-	ZT_ALWAYS_INLINE IncomingPacket() :
-		Packet(),
-		_receiveTime(0)
-	{
-	}
+	ZT_ALWAYS_INLINE IncomingPacket() : Packet(),_receiveTime(0),_path() {}
 
 
 	/**
 	/**
 	 * Create a new packet-in-decode
 	 * Create a new packet-in-decode
@@ -108,30 +99,6 @@ public:
 	ZT_ALWAYS_INLINE uint64_t receiveTime() const { return _receiveTime; }
 	ZT_ALWAYS_INLINE uint64_t receiveTime() const { return _receiveTime; }
 
 
 private:
 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;
 	uint64_t _receiveTime;
 	SharedPtr<Path> _path;
 	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 "Constants.hpp"
 #include "InetAddress.hpp"
 #include "InetAddress.hpp"
@@ -33,20 +30,20 @@ InetAddress::IpScope InetAddress::ipScope() const
 
 
 		case AF_INET: {
 		case AF_INET: {
 			const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
 			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 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 0x06: return IP_SCOPE_PSEUDOPRIVATE;                          // 6.0.0.0/8 (US Army)
 				case 0x0a: return IP_SCOPE_PRIVATE;                                // 10.0.0.0/8
 				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 0x38: return IP_SCOPE_PSEUDOPRIVATE;                          // 56.0.0.0/8 (US Postal Service)
 				case 0x64:
 				case 0x64:
 					if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE;    // 100.64.0.0/10
 					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;
 	unsigned int port = 0;
 	if (*portAt) {
 	if (*portAt) {
 		*(portAt++) = (char)0;
 		*(portAt++) = (char)0;
-		port = Utils::strToUInt(portAt) & 0xffff;
+		port = Utils::strToUInt(portAt) & 0xffffU;
 	}
 	}
 
 
 	if (strchr(buf,':')) {
 	if (strchr(buf,':')) {
@@ -189,7 +186,7 @@ InetAddress InetAddress::netmask() const
 	InetAddress r(*this);
 	InetAddress r(*this);
 	switch(r.ss_family) {
 	switch(r.ss_family) {
 		case AF_INET:
 		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;
 			break;
 		case AF_INET6: {
 		case AF_INET6: {
 			uint64_t nm[2];
 			uint64_t nm[2];
@@ -211,7 +208,7 @@ InetAddress InetAddress::broadcast() const
 {
 {
 	if (ss_family == AF_INET) {
 	if (ss_family == AF_INET) {
 		InetAddress r(*this);
 		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 r;
 	}
 	}
 	return InetAddress();
 	return InetAddress();
@@ -222,7 +219,7 @@ InetAddress InetAddress::network() const
 	InetAddress r(*this);
 	InetAddress r(*this);
 	switch(r.ss_family) {
 	switch(r.ss_family) {
 		case AF_INET:
 		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;
 			break;
 		case AF_INET6: {
 		case AF_INET6: {
 			uint64_t nm[2];
 			uint64_t nm[2];
@@ -294,7 +291,7 @@ bool InetAddress::isNetwork() const
 			if (bits >= 32)
 			if (bits >= 32)
 				return false;
 				return false;
 			uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
 			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: {
 		case AF_INET6: {
 			unsigned int bits = netmaskBits();
 			unsigned int bits = netmaskBits();
@@ -304,7 +301,7 @@ bool InetAddress::isNetwork() const
 				return false;
 				return false;
 			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
 			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;
 			unsigned int p = bits / 8;
-			if ((ip[p++] & (0xff >> (bits % 8))) != 0)
+			if ((ip[p++] & (0xffU >> (bits % 8))) != 0)
 				return false;
 				return false;
 			while (p < 16) {
 			while (p < 16) {
 				if (ip[p++])
 				if (ip[p++])
@@ -378,48 +375,49 @@ bool InetAddress::operator<(const InetAddress &a) const
 
 
 InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
 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 InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
 {
 {
 	InetAddress r;
 	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_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfd;
 	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[8] = (uint8_t)nwid;
 	sin6->sin6_addr.s6_addr[9] = 0x99;
 	sin6->sin6_addr.s6_addr[9] = 0x99;
 	sin6->sin6_addr.s6_addr[10] = 0x93;
 	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_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
 	sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
 	return r;
 	return r;
@@ -427,19 +425,19 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
 
 
 InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
 InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
 {
 {
-	nwid ^= (nwid >> 32);
+	nwid ^= (nwid >> 32U);
 	InetAddress r;
 	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_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfc;
 	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[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[9] = (uint8_t)zeroTierAddress;
 	sin6->sin6_addr.s6_addr[15] = 0x01;
 	sin6->sin6_addr.s6_addr[15] = 0x01;
 	sin6->sin6_port = Utils::hton((uint16_t)40);
 	sin6->sin6_port = Utils::hton((uint16_t)40);

+ 145 - 89
node/InetAddress.hpp

@@ -14,12 +14,13 @@
 #ifndef ZT_INETADDRESS_HPP
 #ifndef ZT_INETADDRESS_HPP
 #define 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 "../include/ZeroTierOne.h"
+
+#include "Constants.hpp"
 #include "Utils.hpp"
 #include "Utils.hpp"
 #include "MAC.hpp"
 #include "MAC.hpp"
 #include "Buffer.hpp"
 #include "Buffer.hpp"
@@ -41,6 +42,16 @@ namespace ZeroTier {
  */
  */
 struct InetAddress : public sockaddr_storage
 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)
 	 * 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.
 		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 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 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)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage *ss)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in &sa)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in *sa)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 &sa)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 *sa)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr &sa)
 	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;
 		return *this;
 	}
 	}
 
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr *sa)
 	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;
 		return *this;
 	}
 	}
 
 
@@ -468,16 +442,16 @@ struct InetAddress : public sockaddr_storage
 		unsigned long h = 0;
 		unsigned long h = 0;
 		switch(ss_family) {
 		switch(ss_family) {
 			case AF_INET:
 			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;
 				break;
 			case AF_INET6: {
 			case AF_INET6: {
 				const uint8_t *ip = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
 				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]);
 				h += ((unsigned long)ip[5]);
 			}	break;
 			}	break;
 		}
 		}
@@ -487,10 +461,85 @@ struct InetAddress : public sockaddr_storage
 	/**
 	/**
 	 * @return True if address family is non-zero
 	 * @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>
 	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
 		// This is used in the protocol and must be the same as describe in places
 		// like VERB_HELLO in Packet.hpp.
 		// like VERB_HELLO in Packet.hpp.
@@ -512,7 +561,7 @@ struct InetAddress : public sockaddr_storage
 	}
 	}
 
 
 	template<unsigned int C>
 	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));
 		memset(this,0,sizeof(InetAddress));
 		unsigned int p = startAt;
 		unsigned int p = startAt;
@@ -607,6 +656,13 @@ struct InetAddress : public sockaddr_storage
 	static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
 	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
 } // namespace ZeroTier
 
 
 #endif
 #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

Some files were not shown because too many files changed in this diff