diff --git a/.gitignore b/.gitignore index bbd212f2b..d9620f10d 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ -build/ +/build +/cmake-build-debug +/cmake-build-release /version.h +/.idea +/go/.idea .DS_Store .Trashes *.swp diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 0e40fe8f5..000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ - -# Default ignored files -/workspace.xml \ No newline at end of file diff --git a/.idea/ZeroTierOne.iml b/.idea/ZeroTierOne.iml deleted file mode 100644 index 5e764c4f0..000000000 --- a/.idea/ZeroTierOne.iml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a179..000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/dictionaries/api.xml b/.idea/dictionaries/api.xml deleted file mode 100644 index 53167764f..000000000 --- a/.idea/dictionaries/api.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - apisocket - nwid - secrand - sockaddr - unmarshals - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 146ab09b7..000000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 28a804d89..000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 6b5db6854..000000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4..000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml deleted file mode 100644 index 97ad6d2d9..000000000 --- a/.idea/watcherTasks.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7487351ec..2f1e839a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required (VERSION 3.8) +project(zerotier DESCRIPTION "ZeroTier Network Hypervisor" LANGUAGES CXX C) if(${CMAKE_VERSION} VERSION_LESS 3.15) cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) @@ -7,33 +8,32 @@ else() endif() if(WIN32) - # If building on Windows, set minimum target to Windows 7 set(CMAKE_SYSTEM_VERSION "7" CACHE STRING INTERNAL FORCE) endif(WIN32) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version") set(ZEROTIER_ONE_VERSION_MAJOR 2 CACHE INTERNAL "") set(ZEROTIER_ONE_VERSION_MINOR 0 CACHE INTERNAL "") set(ZEROTIER_ONE_VERSION_REVISION 0 CACHE INTERNAL "") set(ZEROTIER_ONE_VERSION_BUILD 0 CACHE INTERNAL "") +configure_file( + ${CMAKE_SOURCE_DIR}/version.h.in + ${CMAKE_BINARY_DIR}/version.h +) + set(default_build_type "Release") -if(EXISTS "${CMAKE_SOURCE_DIR}/.git") - set(default_build_type "Debug") -endif() +#if(EXISTS "${CMAKE_SOURCE_DIR}/.git") +# set(default_build_type "Debug") +#endif() if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to '${default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Choose the type of build." FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF) -option(ZT_TRACE "Trace Messages" OFF) -option(ZT_DEBUG_TRACE "Debug Trace Messages" OFF) - if (BUILD_CENTRAL_CONTROLLER) find_package(PostgreSQL REQUIRED) set(ENABLE_SSL_SUPPORT OFF) @@ -45,25 +45,21 @@ if (BUILD_CENTRAL_CONTROLLER) add_subdirectory("ext/librabbitmq") endif(BUILD_CENTRAL_CONTROLLER) -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X Deployment Version") - if(CMAKE_BUILD_TYPE STREQUAL "Debug") add_definitions(-DZT_TRACE) endif(CMAKE_BUILD_TYPE STREQUAL "Debug") -project(zerotier - DESCRIPTION "ZeroTier Network Hypervisor" - LANGUAGES CXX C) - if(WIN32) + message("++ Setting Windows Compiler Flags ${CMAKE_BUILD_TYPE}") add_definitions(-DNOMINMAX) else(WIN32) if(APPLE) - message("Setting macOS Compiler Flags ${CMAKE_BUILD_TYPE}") + message("++ Setting MacOS Compiler Flags ${CMAKE_BUILD_TYPE}") add_compile_options( -Wall -Wno-deprecated + -Wno-unused-function -mmacosx-version-min=10.9 $<$:-g> $<$:-O0> @@ -79,17 +75,13 @@ else(WIN32) $<$:-flto> ) - elseif ( - CMAKE_SYSTEM_NAME MATCHES "Linux" OR - CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR - CMAKE_SYSTEM_NAME MATCHES "OpenBSD" OR - CMAKE_SYSTEM_NAME MATCHES "NetBSD" - ) + else(APPLE) - message("Setting Linux/BSD Compiler Flags (${CMAKE_BUILD_TYPE})") + message("++ Setting Linux/BSD/Posix Compiler Flags (${CMAKE_BUILD_TYPE})") add_compile_options( -Wall -Wno-deprecated + -Wno-unused-function $<$:-g> $<$:-O0> $<$:-O3> @@ -110,10 +102,9 @@ if ( CMAKE_SYSTEM_PROCESSOR MATCHES "i586" OR CMAKE_SYSTEM_PROCESSOR MATCHES "i686" ) - message("Adding SSE and AES-NI flags for processor ${CMAKE_SYSTEM_PROCESSOR}") + message("++ Adding SSE and AES-NI flags for processor ${CMAKE_SYSTEM_PROCESSOR}") add_compile_options( -maes - -mmmx -mrdrnd -mpclmul -msse @@ -126,9 +117,6 @@ endif() if(ZT_TRACE) add_definitions(-DZT_TRACE) endif() -if(ZT_DEBUG_TRACE) - add_definitions(-DZT_DEBUG_TRACE) -endif() add_subdirectory(node) add_subdirectory(controller) @@ -136,12 +124,6 @@ add_subdirectory(osdep) add_subdirectory(root) add_subdirectory(go/native) -#if(WIN32) -# add_subdirectory("windows/WinUI") -# add_subdirectory("windows/copyutil") -# add_definitions(-DNOMINMAX) -#endif(WIN32) - set( zt_osdep zt_core @@ -149,25 +131,18 @@ set( zt_go_native ) -configure_file( - ${CMAKE_SOURCE_DIR}/version.h.in - ${CMAKE_BINARY_DIR}/version.h -) - -#set(src -# one.cpp -# "ext/http-parser/http_parser.c" -#) -#set(headers -# "ext/http-parser/http_parser.h" -#) - if(WIN32) set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi) else(WIN32) set(libs ${libs} pthread) endif(WIN32) +#if(WIN32) +# add_subdirectory("windows/WinUI") +# add_subdirectory("windows/copyutil") +# add_definitions(-DNOMINMAX) +#endif(WIN32) + #if(WIN32) # set(libs ${libs} wsock32 ws2_32 rpcrt4 iphlpapi) # set(src @@ -203,6 +178,6 @@ add_custom_command( ) add_custom_target(build_zerotier ALL DEPENDS zerotier) -add_executable(zerotier-selftest selftest.cpp) -target_link_libraries(zerotier-selftest ${libs} zt_core zt_osdep) -target_compile_features(zerotier-selftest PUBLIC cxx_std_11) +#add_executable(zerotier-selftest selftest.cpp) +#target_link_libraries(zerotier-selftest ${libs} zt_core zt_osdep) +#target_compile_features(zerotier-selftest PUBLIC cxx_std_11) diff --git a/attic/Binder.hpp b/attic/Binder.hpp deleted file mode 100644 index 67debc80b..000000000 --- a/attic/Binder.hpp +++ /dev/null @@ -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 -#include -#include -#include - -#ifdef __WINDOWS__ -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#ifdef __LINUX__ -#include -#include -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include - -#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 - void closeAll(Phy &phy) - { - Mutex::Lock _l(_lock); - for(unsigned int b=0,c=_bindingCount;b template - * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method - */ - template - void refresh(Phy &phy,unsigned int *ports,unsigned int portCount,const std::vector explicitBind,INTERFACE_CHECKER &ifChecker) - { - std::map 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(aabuf),&aalen) == NO_ERROR) { - PIP_ADAPTER_ADDRESSES a = reinterpret_cast(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(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 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(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(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(ip,std::string(ifa->ifa_name))); - } - break; - } - } - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifatbl); - } - else { - interfacesEnumerated = false; - } - } -#endif - -#endif - } else { - for(std::vector::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i) - localIfAddrs.insert(std::pair(*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((uint32_t)0,ports[x]),std::string())); - localIfAddrs.insert(std::pair(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::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(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); - tcps = phy.tcpListen(reinterpret_cast(&(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::getDescriptor(udps); - if (fd >= 0) - setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); - fd = (int)Phy::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 allBoundLocalInterfaceAddresses() const - { - std::vector aa; - Mutex::Lock _l(_lock); - for(unsigned int b=0,c=_bindingCount;b - inline bool udpSendAll(Phy &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 _bindingCount; - Mutex _lock; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Http.cpp b/attic/Http.cpp deleted file mode 100644 index 9ff1a0689..000000000 --- a/attic/Http.cpp +++ /dev/null @@ -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 -#include -#include - -#include "Http.hpp" -#include "Phy.hpp" -#include "OSUtils.hpp" -#include "../node/Constants.hpp" -#include "../node/Utils.hpp" - -#ifdef ZT_USE_SYSTEM_HTTP_PARSER -#include -#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 *responseHeaders; - std::string *responseBody; - bool error; - bool done; - - Phy *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(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(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;icurrentHeaderField.push_back(OSUtils::toLower(ptr[i])); - return 0; -} -static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length) -{ - HttpPhyHandler *hh = reinterpret_cast(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(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(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(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 &requestHeaders, - const void *requestBody, - unsigned long requestBodyLength, - std::map &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::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 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 diff --git a/attic/Http.hpp b/attic/Http.hpp deleted file mode 100644 index d9cad4dc1..000000000 --- a/attic/Http.hpp +++ /dev/null @@ -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 -#include -#include - -#if defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#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 &requestHeaders, - std::map &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 &requestHeaders, - std::map &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 &requestHeaders, - const void *postData, - unsigned long postDataLength, - std::map &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 &requestHeaders, - const void *postData, - unsigned long postDataLength, - std::map &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 &requestHeaders, - const void *requestBody, - unsigned long requestBodyLength, - std::map &responseHeaders, - std::string &responseBody); -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Phy.hpp b/attic/Phy.hpp deleted file mode 100644 index d4934edf3..000000000 --- a/attic/Phy.hpp +++ /dev/null @@ -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 -#include -#include - -#include -#include - -#if defined(_WIN32) || defined(_WIN64) - -#include -#include -#include - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 -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 _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::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(s)->sock; } - - /** - * @param s Socket object - * @return Pointer to user object - */ - static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast(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(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(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(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(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(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(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(sock)); -#if defined(_WIN32) || defined(_WIN64) - return ((long)::sendto(sws.sock,reinterpret_cast(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(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(sock)); -#if defined(_WIN32) || defined(_WIN64) - long n = (long)::send(sws.sock,reinterpret_cast(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(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(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(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::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(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::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 diff --git a/attic/PortMapper-libnatpmp.c b/attic/PortMapper-libnatpmp.c deleted file mode 100644 index 5da85cba9..000000000 --- a/attic/PortMapper-libnatpmp.c +++ /dev/null @@ -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" diff --git a/attic/PortMapper-miniupnpc.c b/attic/PortMapper-miniupnpc.c deleted file mode 100644 index 8d28da109..000000000 --- a/attic/PortMapper-miniupnpc.c +++ /dev/null @@ -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" diff --git a/attic/PortMapper.cpp b/attic/PortMapper.cpp deleted file mode 100644 index d0ed87c37..000000000 --- a/attic/PortMapper.cpp +++ /dev/null @@ -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 -#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__)) -#else -#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__) -#endif - -#include -#include -#include - -#include - -#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 -#include -#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 -#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 surface; -}; - -PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName) -{ - _impl = new PortMapperImpl(localUdpPortToMap,uniqueName); - Thread::start(_impl); -} - -PortMapper::~PortMapper() -{ - _impl->run = false; -} - -std::vector PortMapper::get() const -{ - Mutex::Lock _l(_impl->surface_l); - return _impl->surface; -} - -} // namespace ZeroTier diff --git a/attic/PortMapper.hpp b/attic/PortMapper.hpp deleted file mode 100644 index 54b04de1b..000000000 --- a/attic/PortMapper.hpp +++ /dev/null @@ -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 - -#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 get() const; - -private: - PortMapperImpl *_impl; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Root.hpp b/attic/Root.hpp deleted file mode 100644 index c526007da..000000000 --- a/attic/Root.hpp +++ /dev/null @@ -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 - ZT_ALWAYS_INLINE Root(S dn,const uint8_t *const dnspk,const unsigned int dnspksize,const Identity &dflId,const std::vector &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 - 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 pickList; - const std::vector *const av = (_lastFetchedLocator) ? &(_lastFetchedLocator.phy()) : &_defaultAddresses; - for(std::vector::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 _defaultAddresses; - Str _dnsName; - Locator _lastFetchedLocator; - unsigned int _dnsPublicKeySize; - uint8_t _dnsPublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/linux-old-glibc-compat.c b/attic/linux-old-glibc-compat.c deleted file mode 100644 index 6d793a2d0..000000000 --- a/attic/linux-old-glibc-compat.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -__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 diff --git a/attic/listaddrinfo.go b/attic/listaddrinfo.go deleted file mode 100644 index 3db54bf00..000000000 --- a/attic/listaddrinfo.go +++ /dev/null @@ -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") - } -} diff --git a/attic/one.cpp b/attic/one.cpp deleted file mode 100644 index 2a3be098e..000000000 --- a/attic/one.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#include "node/Constants.hpp" - -#ifdef __WINDOWS__ -#include -#include -#include -#include -#include -#include -#include -#include "osdep/WindowsEthernetTap.hpp" -#include "windows/ZeroTierOne/ServiceInstaller.h" -#include "windows/ZeroTierOne/ServiceBase.h" -#include "windows/ZeroTierOne/ZeroTierOneService.h" -#else -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __LINUX__ -#include -#include -#include -#ifndef ZT_NO_CAPABILITIES -#include -#include -#endif -#endif -#endif - -#include -#include -#include -#include -#include - -#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] []" 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 - ZeroTier home path for parameter auto-detect" ZT_EOL_S); - fprintf(out," -p - HTTP port (default: auto)" ZT_EOL_S); - fprintf(out," -T - 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 - Join a network" ZT_EOL_S); - fprintf(out," leave - Leave a network" ZT_EOL_S); - fprintf(out," set - Set a network setting" ZT_EOL_S); - fprintf(out," get - 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 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 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 requestHeaders; - std::map 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 " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long k=0;k= 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 " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long k=0;k= 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 " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long i=0;i 0) aa.push_back(','); - aa.append(addr.get()); - } - } - } - 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().find(".") != std::string::npos) - || ((arg2.find("ip6") == 0) && addr.get().find(":") != std::string::npos) - || (arg2 == "ip") - ) { - matchingIdxs[addressCountOfType++] = k; - } - } - for (int k=0; k().find("fc") == 0) { - aa.append(addr.get().substr(0,addr.get().find("/"))); - if (k < addressCountOfType-1) aa.append("\n"); - } - } - if (arg2 == "ip6prefix") { - if (addr.get().find("fc") == 0) { - aa.append(addr.get().substr(0,addr.get().find("/")).substr(0,24)); - if (k < addressCountOfType-1) aa.append("\n"); - } - } - } - else { - aa.append(addr.get().substr(0,addr.get().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 []" ZT_EOL_S"" ZT_EOL_S"Commands:" ZT_EOL_S,pn); - fprintf(out," generate [] [] []" ZT_EOL_S); - fprintf(out," validate " ZT_EOL_S); - fprintf(out," getpublic " ZT_EOL_S); - fprintf(out," sign " ZT_EOL_S); - fprintf(out," verify " 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 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 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 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::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; -} diff --git a/attic/webview/.clang-format b/attic/webview/.clang-format new file mode 100644 index 000000000..5dad5a60a --- /dev/null +++ b/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 +... + diff --git a/attic/webview/.clang-tidy b/attic/webview/.clang-tidy new file mode 100644 index 000000000..cc8f6eb3d --- /dev/null +++ b/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' +... + diff --git a/attic/webview/.gitattributes b/attic/webview/.gitattributes new file mode 100644 index 000000000..5170675f3 --- /dev/null +++ b/attic/webview/.gitattributes @@ -0,0 +1 @@ +*.h linguist-language=c diff --git a/attic/webview/.gitignore b/attic/webview/.gitignore new file mode 100644 index 000000000..6307e1aab --- /dev/null +++ b/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 diff --git a/attic/webview/.travis.yml b/attic/webview/.travis.yml new file mode 100644 index 000000000..e94a7765e --- /dev/null +++ b/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 diff --git a/attic/webview/LICENSE b/attic/webview/LICENSE new file mode 100644 index 000000000..b18604bf4 --- /dev/null +++ b/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. diff --git a/attic/webview/Makefile b/attic/webview/Makefile new file mode 100644 index 000000000..71cdf57db --- /dev/null +++ b/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 diff --git a/attic/webview/example.cc b/attic/webview/example.cc new file mode 100644 index 000000000..7afcc5794 --- /dev/null +++ b/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, + + + hello + + + )"); + w.run(); + return 0; +} diff --git a/attic/webview/example/example.go b/attic/webview/example/example.go new file mode 100644 index 000000000..d536a07cb --- /dev/null +++ b/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() +} diff --git a/attic/webview/go.mod b/attic/webview/go.mod new file mode 100644 index 000000000..487f240b9 --- /dev/null +++ b/attic/webview/go.mod @@ -0,0 +1,3 @@ +module github.com/zserge/webview + +go 1.13 diff --git a/attic/webview/webview.cc b/attic/webview/webview.cc new file mode 100644 index 000000000..0d861df50 --- /dev/null +++ b/attic/webview/webview.cc @@ -0,0 +1 @@ +#include "webview.h" diff --git a/attic/webview/webview.go b/attic/webview/webview.go new file mode 100644 index 000000000..32595f26a --- /dev/null +++ b/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 +#include + +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() +} diff --git a/attic/webview/webview.h b/attic/webview/webview.h new file mode 100644 index 000000000..7a20b9541 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +#include + +namespace webview { +using dispatch_fn_t = std::function; +using msg_cb_t = std::function; + +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(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 +#include +#include + +namespace webview { + +class browser_engine { +public: + browser_engine(msg_cb_t cb, bool debug, void *window) + : m_cb(cb), m_window(static_cast(window)) { + gtk_init_check(0, NULL); + m_window = static_cast(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(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(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 f) { + g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)([](void *f) -> int { + (*static_cast(f))(); + return G_SOURCE_REMOVE; + }), + new std::function(f), + [](void *f) { delete static_cast(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 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 +#include + +#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 f) { + dispatch_async_f(dispatch_get_main_queue(), new dispatch_fn_t(f), + (dispatch_function_t)([](void *arg) { + auto f = static_cast(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 + +#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(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 +#include +#include +#include +#include +#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(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(this); + return S_OK; + } + if (riid == IID_IOleInPlaceSite) { + *obj = static_cast(this); + return S_OK; + } + if (riid == IID_IDocHostUIHandler) { + *obj = static_cast(this); + return S_OK; + } + if (riid == IID_IDispatch || riid == DIID_DWebBrowserEvents2) { + *obj = static_cast(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(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(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 +#include +#include + +#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(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({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; + + 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 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(w); +} + +WEBVIEW_API void webview_run(webview_t w) { + static_cast(w)->run(); +} + +WEBVIEW_API void webview_terminate(webview_t w) { + static_cast(w)->terminate(); +} + +WEBVIEW_API void +webview_dispatch(webview_t w, void (*fn)(webview_t w, void *arg), void *arg) { + static_cast(w)->dispatch([=]() { fn(w, arg); }); +} + +WEBVIEW_API void *webview_get_window(webview_t w) { + return static_cast(w)->window(); +} + +WEBVIEW_API void webview_set_title(webview_t w, const char *title) { + static_cast(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(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(w)->navigate(url); +} + +WEBVIEW_API void webview_init(webview_t w, const char *js) { + static_cast(w)->init(js); +} + +WEBVIEW_API void webview_eval(webview_t w, const char *js) { + static_cast(w)->eval(js); +} + +#endif /* WEBVIEW_HEADER */ + +#endif /* WEBVIEW_H */ diff --git a/attic/webview/webview_test.cc b/attic/webview/webview_test.cc new file mode 100644 index 000000000..ebf995b1e --- /dev/null +++ b/attic/webview/webview_test.cc @@ -0,0 +1,38 @@ +// +build ignore + +#include "webview.h" + +#include +#include + +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; +} diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index f5c48228d..47ce6a3ab 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -479,6 +479,7 @@ EmbeddedNetworkController::~EmbeddedNetworkController() void EmbeddedNetworkController::init(const Identity &signingId,Sender *sender) { char tmp[64]; + _signingId = signingId; _sender = sender; _signingIdAddressString = signingId.address().toString(tmp); @@ -1445,6 +1446,7 @@ void EmbeddedNetworkController::_request( const bool noAutoAssignIps = OSUtils::jsonBool(member["noAutoAssignIps"],false); + // Set IPv6 static IPs based on NDP emulated schemes if enabled. if ((v6AssignMode.is_object())&&(!noAutoAssignIps)) { if ((OSUtils::jsonBool(v6AssignMode["rfc4193"],false))&&(nc->staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { nc->staticIps[nc->staticIpCount++] = InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()); diff --git a/controller/LFDB.cpp b/controller/LFDB.cpp index d11b77a07..d16c1fd29 100644 --- a/controller/LFDB.cpp +++ b/controller/LFDB.cpp @@ -43,10 +43,10 @@ LFDB::LFDB(const Identity &myId,const char *path,const char *lfOwnerPrivate,cons // LF record masking key is the first 32 bytes of SHA512(controller private key) in hex, // hiding record values from anything but the controller or someone who has its key. - uint8_t sha512pk[64]; - _myId.sha512PrivateKey(sha512pk); + uint8_t sha384pk[48]; + _myId.hash(sha384pk,true); char maskingKey [128]; - Utils::hex(sha512pk,32,maskingKey); + Utils::hex(sha384pk,32,maskingKey); httplib::Client htcli(_lfNodeHost.c_str(),_lfNodePort,600); int64_t timeRangeStart = 0; diff --git a/go/cmd/zerotier/cli/addroot.go b/go/cmd/zerotier/cli/addroot.go index 098d800fc..317c2693a 100644 --- a/go/cmd/zerotier/cli/addroot.go +++ b/go/cmd/zerotier/cli/addroot.go @@ -13,53 +13,6 @@ package cli -import ( - "encoding/base64" - "fmt" - "io/ioutil" - "net/url" - "os" - "strings" - - "zerotier/pkg/zerotier" -) - // AddRoot CLI command func AddRoot(basePath, authToken string, args []string) { - if len(args) == 0 { - Help() - os.Exit(0) - } - - locData, err := ioutil.ReadFile(args[0]) - if err != nil { - locData, err2 := base64.StdEncoding.DecodeString(strings.TrimSpace(args[0])) - if err2 != nil || len(locData) == 0 { - fmt.Printf("ERROR: unable to read locator: %s\n", err.Error()) - os.Exit(1) - } - } - loc, err := zerotier.NewLocatorFromBytes(locData) - if err != nil { - fmt.Printf("ERROR: invalid locator '%s' (tried as file and base64 literal): %s\n", args[0], err.Error()) - os.Exit(1) - } - - var name string - if len(args) > 1 { - if len(args) > 2 { - Help() - os.Exit(1) - } - name = strings.TrimSpace(args[1]) - } - - var result zerotier.Root - apiPost(basePath, authToken, "/root/"+url.PathEscape(name), &zerotier.Root{ - Name: name, - Locator: loc, - }, &result) - - fmt.Println(jsonDump(&result)) - os.Exit(0) } diff --git a/go/cmd/zerotier/cli/help.go b/go/cmd/zerotier/cli/help.go index f9197d215..fabd65b50 100644 --- a/go/cmd/zerotier/cli/help.go +++ b/go/cmd/zerotier/cli/help.go @@ -41,12 +41,8 @@ Commands: status Show ZeroTier service status and config peers Show VL1 peers roots Show configured VL1 root servers - addroot [name] Add a VL1 root - removeroot Remove a VL1 root - locator [args] Locator management commands - new
[...] Create and sign locator for identity - newdnskey Create a secure DNS name and secret - getdns Create secure DNS TXT records + addroot Add VL1 root server + removeroot Remove VL1 root server identity [args] Identity management commands new [c25519|p384] Create new identity (including secret) getpublic Extract only public part of identity diff --git a/go/cmd/zerotier/cli/locator.go b/go/cmd/zerotier/cli/locator.go deleted file mode 100644 index 08064d265..000000000 --- a/go/cmd/zerotier/cli/locator.go +++ /dev/null @@ -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) -} diff --git a/go/cmd/zerotier/cli/roots.go b/go/cmd/zerotier/cli/roots.go index 97c4f4280..a53fbb6b1 100644 --- a/go/cmd/zerotier/cli/roots.go +++ b/go/cmd/zerotier/cli/roots.go @@ -13,51 +13,6 @@ package cli -import ( - "fmt" - "os" - - "zerotier/pkg/zerotier" -) - // Roots CLI command func Roots(basePath, authToken string, args []string, jsonOutput bool) { - var roots []zerotier.Root - apiGet(basePath, authToken, "/root", &roots) - - if jsonOutput { - fmt.Println(jsonDump(roots)) - } else { - fmt.Printf("%32s
\n", "") - 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) } diff --git a/go/cmd/zerotier/cli/service.go b/go/cmd/zerotier/cli/service.go index 898232f71..c6d44be8a 100644 --- a/go/cmd/zerotier/cli/service.go +++ b/go/cmd/zerotier/cli/service.go @@ -18,6 +18,7 @@ import ( "os" "os/signal" "syscall" + "zerotier/pkg/zerotier" ) diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index 770ca4767..0ed63a9df 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -127,8 +127,6 @@ func main() { case "removeroot": authTokenRequired(authToken) cli.RemoveRoot(basePath, authToken, cmdArgs) - case "locator": - cli.Locator(cmdArgs) case "identity": cli.Identity(cmdArgs) case "networks", "listnetworks": diff --git a/go/go.mod b/go/go.mod index 216d01963..5629b10cb 100644 --- a/go/go.mod +++ b/go/go.mod @@ -5,4 +5,5 @@ go 1.13 require ( github.com/Microsoft/go-winio v0.4.14 github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 + golang.org/x/sys v0.0.0-20200107162124-548cf772de50 // indirect ) diff --git a/go/go.sum b/go/go.sum index c925d783b..89556cb2e 100644 --- a/go/go.sum +++ b/go/go.sum @@ -13,3 +13,5 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 h1:so6Hr/LodwSZ5UQDu/7PmQiDeS112WwtLvU3lpSPZTU= golang.org/x/sys v0.0.0-20190529164535-6a60838ec259/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50 h1:YvQ10rzcqWXLlJZ3XCUoO25savxmscf4+SC+ZqiCHhA= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index c903c8e7a..8eaabd9d1 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -13,35 +13,25 @@ #include "GoGlue.h" +#include +#include +#include + #include "../../node/Constants.hpp" #include "../../node/InetAddress.hpp" #include "../../node/Node.hpp" #include "../../node/Utils.hpp" #include "../../node/MAC.hpp" #include "../../node/Address.hpp" -#include "../../node/Locator.hpp" #include "../../osdep/OSUtils.hpp" #include "../../osdep/EthernetTap.hpp" -#include "../../osdep/ManagedRoute.hpp" - -#include -#include -#include #ifndef __WINDOWS__ -#include -#include #include -#include -#include -#include -#include #include #include #include #include -#include -#include #ifdef __BSD__ #include #endif @@ -56,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -111,10 +100,9 @@ const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str(); /* These functions are implemented in Go in pkg/ztnode/node-callbacks.go */ extern "C" int goPathCheckFunc(void *,uint64_t,int,const void *,int); -extern "C" int goPathLookupFunc(void *,uint64_t,int,int *,uint8_t [16],int *); +extern "C" int goPathLookupFunc(void *,uint64_t,int,const ZT_Identity *,int *,uint8_t [16],int *); extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int); extern "C" int goStateObjectGetFunc(void *,int,const uint64_t [2],void *,unsigned int); -extern "C" void goDNSResolverFunc(void *,const uint8_t *,int,const char *,uintptr_t); extern "C" void goVirtualNetworkConfigFunc(void *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *); extern "C" void goZtEvent(void *,int,const void *); extern "C" void goHandleTapAddedMulticastGroup(void *,ZT_GoTap *,uint64_t,uint64_t,uint32_t); @@ -279,6 +267,7 @@ static int ZT_GoNode_PathLookupFunction( void *uptr, void *tptr, uint64_t ztAddress, + const ZT_Identity *id, int desiredAddressFamily, struct sockaddr_storage *sa) { @@ -289,6 +278,7 @@ static int ZT_GoNode_PathLookupFunction( reinterpret_cast(uptr)->goUserPtr, ztAddress, desiredAddressFamily, + id, &family, ip, &port @@ -310,20 +300,6 @@ static int ZT_GoNode_PathLookupFunction( return 0; } -static void ZT_GoNode_DNSResolver( - ZT_Node *node, - void *uptr, - void *tptr, - const enum ZT_DNSRecordType *types, - unsigned int numTypes, - const char *name, - uintptr_t requestId) -{ - uint8_t t[256]; - for(unsigned int i=0;(i(uptr)->goUserPtr,t,(int)numTypes,name,requestId); -} - /****************************************************************************/ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr) @@ -336,7 +312,6 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr) cb.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction; cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction; cb.eventCallback = &ZT_GoNode_EventCallback; - cb.dnsResolver = &ZT_GoNode_DNSResolver; cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction; cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction; @@ -727,133 +702,3 @@ extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targe } return reinterpret_cast(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(physicalAddresses + i)); - } - for(unsigned int i=0;i *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 r(loc.makeTxtRecords(privateKey)); - if (r.size() > 256) - return -1; - for(unsigned long i=0;i +#include +#include +#else #include #include #include +#endif #include "../../include/ZeroTierCore.h" #include "../../node/Constants.hpp" -/****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif /* A pointer to an instance of EthernetTap */ typedef void ZT_GoTap; @@ -32,18 +40,8 @@ typedef void ZT_GoTap; struct ZT_GoNode_Impl; typedef struct ZT_GoNode_Impl ZT_GoNode; -/****************************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -/****************************************************************************/ - extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH; -/****************************************************************************/ - ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr); void ZT_GoNode_delete(ZT_GoNode *gn); @@ -60,8 +58,6 @@ ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid); void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid); -/****************************************************************************/ - void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled); int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits); @@ -88,67 +84,6 @@ int ZT_GoTap_addRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int target int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targetIp,int targetNetmaskBits,int viaAf,const void *viaIp,unsigned int metric); -/****************************************************************************/ - -const char *ZT_GoIdentity_generate(int type); - -int ZT_GoIdentity_validate(const char *idStr); - -int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen); - -int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen); - -/****************************************************************************/ - -struct ZT_GoLocator_Info { - char id[1024]; - unsigned int phyCount; - unsigned int virtCount; - struct sockaddr_storage phy[256]; - char virt[256][1024]; -}; - -/* Returns length of private key stored in private key buffer on success, -1 on fail */ -int ZT_GoLocator_makeSecureDNSName(char name[256],unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize); - -/* - * The id is the full identity described by the locator. It must include - * its secret key to permit the locator to be signed. - * - * Physical addresses must be IPv4 or IPv6 IP/port pairs. Virtual addresses - * must be full ZeroTier identities in string format. - * - * On success this returns the actual number of bytes stored in the buffer. - * On failure -1 is returned. - */ -int ZT_GoLocator_makeLocator( - uint8_t *buf, - unsigned int bufSize, - int64_t ts, - const char *id, - const struct sockaddr_storage *physicalAddresses, - unsigned int physicalAddressCount, - const char **virtualAddresses, - unsigned int virtualAddressCount); - -/* Returns >0 on success, fills info structure */ -int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info); - -/* - * The privateKey and privateKeySize are those created by makeSecureDNSName. - * Results is filled and the number of lines of TXT are returned. The value - * -1 is returned on error. - */ -int ZT_GoLocator_makeSignedTxtRecords( - const uint8_t *locator, - unsigned int locatorSize, - const char *name, - const uint8_t *privateKey, - unsigned int privateKeySize, - char results[256][256]); - -/****************************************************************************/ - #ifdef __cplusplus } #endif diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index acc22827c..f3eef1f91 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -460,57 +460,14 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } apiSetStandardHeaders(out) - var queriedName string - if len(req.URL.Path) > 6 { - queriedName = req.URL.Path[6:] - } + //var queriedName string + //if len(req.URL.Path) > 6 { + // queriedName = req.URL.Path[6:] + //} if req.Method == http.MethodDelete { - if len(queriedName) > 0 { - roots := node.Roots() - for _, r := range roots { - if r.Name == queriedName { - node.RemoveRoot(queriedName) - _ = apiSendObj(out, req, http.StatusOK, r) - return - } - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"}) } else if req.Method == http.MethodPost || req.Method == http.MethodPut { - if len(queriedName) == 0 { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual roots can be added or modified with POST/PUT"}) - return - } - var r Root - if apiReadObj(out, req, &r) == nil { - if r.Name != queriedName { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"root name does not match name in path"}) - return - } - err := node.SetRoot(r.Name, r.Locator) - if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"set/update root failed: " + err.Error()}) - } else { - roots := node.Roots() - for _, r := range roots { - if r.Name == queriedName { - _ = apiSendObj(out, req, http.StatusOK, r) - return - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"set/update root failed: root set but not subsequently found in list"}) - } - } } else if req.Method == http.MethodGet || req.Method == http.MethodHead { - roots := node.Roots() - for _, r := range roots { - if r.Name == queriedName { - _ = apiSendObj(out, req, http.StatusOK, r) - return - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"root not found"}) } else { out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method}) diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index 24d8f6c4c..d5099fc57 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -13,7 +13,6 @@ package zerotier -//#cgo CFLAGS: -O3 //#include "../../native/GoGlue.h" import "C" @@ -47,15 +46,26 @@ type Identity struct { privateKey []byte } -// NewIdentity generates a new identity of the selected type -func NewIdentity(identityType int) (*Identity, error) { - cIdStr := C.ZT_GoIdentity_generate(C.int(identityType)) - if uintptr(unsafe.Pointer(cIdStr)) == 0 { +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 } - id, err := NewIdentityFromString(C.GoString(cIdStr)) - C.free(unsafe.Pointer(cIdStr)) - return id, err + return NewIdentityFromString(C.GoString(idStr)) +} + +// NewIdentity generates a new identity of the selected type +func NewIdentity(identityType int) (*Identity, error) { + cid := C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType)) + if uintptr(unsafe.Pointer(cid)) == 0 { + return nil, ErrInternal + } + defer C.ZT_Identity_delete(cid) + return newIdentityFromCIdentity(cid) } // NewIdentityFromString generates a new identity from its string representation. @@ -159,23 +169,35 @@ func (id *Identity) String() string { func (id *Identity) LocallyValidate() bool { idCStr := C.CString(id.String()) defer C.free(unsafe.Pointer(idCStr)) - return C.ZT_GoIdentity_validate(idCStr) != 0 + cid := C.ZT_Identity_fromString(idCStr) + if uintptr(cid) == 0 { + return false + } + defer C.ZT_Identity_delete(cid) + return C.ZT_Identity_validate(cid) != 0 } // Sign signs a message with this identity func (id *Identity) Sign(msg []byte) ([]byte, error) { idCStr := C.CString(id.PrivateKeyString()) - var sigbuf [96]byte + defer C.free(unsafe.Pointer(idCStr)) + cid := C.ZT_Identity_fromString(idCStr) + if uintptr(cid) == 0 { + return nil, ErrInvalidKey + } + defer C.ZT_Identity_delete(cid) + var dataP unsafe.Pointer if len(msg) > 0 { dataP = unsafe.Pointer(&msg[0]) } - siglen := C.ZT_GoIdentity_sign(idCStr, dataP, C.uint(len(msg)), unsafe.Pointer(&sigbuf[0]), C.uint(len(sigbuf))) - C.free(unsafe.Pointer(idCStr)) - if siglen <= 0 { + var sig [96]byte + sigLen := C.ZT_Identity_sign(cid,dataP,C.uint(len(msg)),unsafe.Pointer(&sig[0]),96) + if sigLen <= 0 { return nil, ErrInvalidKey } - return sigbuf[0:int(siglen)], nil + + return sig[0:uint(sigLen)], nil } // Verify verifies a signature @@ -183,13 +205,20 @@ func (id *Identity) Verify(msg, sig []byte) bool { if len(sig) == 0 { return false } + idCStr := C.CString(id.String()) defer C.free(unsafe.Pointer(idCStr)) + cid := C.ZT_Identity_fromString(idCStr) + if uintptr(cid) == 0 { + return false + } + defer C.ZT_Identity_delete(cid) + var dataP unsafe.Pointer if len(msg) > 0 { dataP = unsafe.Pointer(&msg[0]) } - return C.ZT_GoIdentity_verify(idCStr, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 + return C.ZT_Identity_verify(cid,dataP,C.uint(len(msg)),unsafe.Pointer(&sig[0]),C.uint(len(sig))) != 0 } // MarshalJSON marshals this Identity in its string format (private key is never included) diff --git a/go/pkg/zerotier/localconfig.go b/go/pkg/zerotier/localconfig.go index 7fca5a463..abdfd4226 100644 --- a/go/pkg/zerotier/localconfig.go +++ b/go/pkg/zerotier/localconfig.go @@ -33,7 +33,7 @@ type LocalConfigPhysicalPathConfiguration struct { // LocalConfigVirtualAddressConfiguration contains settings for virtual addresses type LocalConfigVirtualAddressConfiguration struct { // Try is a list of IPs/ports to try for this peer in addition to anything learned from roots or direct path push - Try []InetAddress `json:",omitempty"` + Try []InetAddress `json:"try,omitempty"` } // ExternalAddress is an externally visible address diff --git a/go/pkg/zerotier/locator.go b/go/pkg/zerotier/locator.go deleted file mode 100644 index ae9298362..000000000 --- a/go/pkg/zerotier/locator.go +++ /dev/null @@ -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 -} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 922ee3b48..dd86e85c2 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -595,62 +595,6 @@ func (n *Node) Networks() []*Network { return nws } -// Roots retrieves a list of root servers on this node and their preferred and online status. -func (n *Node) Roots() []*Root { - var roots []*Root - rl := C.ZT_Node_listRoots(unsafe.Pointer(n.zn), C.int64_t(TimeMs())) - if rl != nil { - for i := 0; i < int(rl.count); i++ { - root := (*C.ZT_Root)(unsafe.Pointer(uintptr(unsafe.Pointer(rl)) + C.sizeof_ZT_RootList)) - loc, _ := NewLocatorFromBytes(C.GoBytes(root.locator, C.int(root.locatorSize))) - if loc != nil { - roots = append(roots, &Root{ - Name: C.GoString(root.name), - Locator: loc, - }) - } - } - C.ZT_Node_freeQueryResult(unsafe.Pointer(n.zn), unsafe.Pointer(rl)) - } - return roots -} - -// SetRoot sets or updates a root. -// Name can be a DNS name (preferably secure) for DNS fetched locators or can be -// the empty string for static roots. If the name is empty then the locator must -// be non-nil. -func (n *Node) SetRoot(name string, locator *Locator) error { - if len(name) == 0 { - if locator == nil { - return ErrInvalidParameter - } - name = locator.Identity.address.String() - } - var lb []byte - if locator != nil { - lb = locator.Bytes - } - var lbp unsafe.Pointer - if len(lb) > 0 { - lbp = unsafe.Pointer(&lb[0]) - } - cn := C.CString(name) - defer C.free(unsafe.Pointer(cn)) - if C.ZT_Node_setRoot(unsafe.Pointer(n.zn), cn, lbp, C.uint(len(lb))) != 0 { - return ErrInternal - } - return nil -} - -// RemoveRoot removes a root. -// For static roots the name should be the ZeroTier address. -func (n *Node) RemoveRoot(name string) { - cn := C.CString(name) - defer C.free(unsafe.Pointer(cn)) - C.ZT_Node_removeRoot(unsafe.Pointer(n.zn), cn) - return -} - // Peers retrieves a list of current peers func (n *Node) Peers() []*Peer { var peers []*Peer @@ -665,59 +609,21 @@ func (n *Node) Peers() []*Peer { p2.Role = int(p.role) p2.Paths = make([]Path, 0, int(p.pathCount)) - usingAllocation := false for j := uintptr(0); j < uintptr(p.pathCount); j++ { pt := &p.paths[j] if pt.alive != 0 { a := sockaddrStorageToUDPAddr(&pt.address) if a != nil { - alloc := float32(pt.allocation) - if alloc > 0.0 { - usingAllocation = true - } p2.Paths = append(p2.Paths, Path{ IP: a.IP, Port: a.Port, LastSend: int64(pt.lastSend), LastReceive: int64(pt.lastReceive), TrustedPathID: uint64(pt.trustedPathId), - Latency: float32(pt.latency), - PacketDelayVariance: float32(pt.packetDelayVariance), - ThroughputDisturbCoeff: float32(pt.throughputDisturbCoeff), - PacketErrorRatio: float32(pt.packetErrorRatio), - PacketLossRatio: float32(pt.packetLossRatio), - Stability: float32(pt.stability), - Throughput: uint64(pt.throughput), - MaxThroughput: uint64(pt.maxThroughput), - Allocation: alloc, }) } } } - if !usingAllocation { // if all allocations are zero fall back to single path mode that uses the preferred flag - for i, j := 0, uintptr(0); j < uintptr(p.pathCount); j++ { - pt := &p.paths[j] - if pt.alive != 0 { - if pt.preferred == 0 { - p2.Paths[i].Allocation = 0.0 - } else { - p2.Paths[i].Allocation = 1.0 - } - i++ - } - } - } - sort.Slice(p2.Paths, func(a, b int) bool { - pa := &p2.Paths[a] - pb := &p2.Paths[b] - if pb.Allocation < pa.Allocation { // invert order, put highest allocation paths first - return true - } - if pa.Allocation == pb.Allocation { - return pa.LastReceive < pb.LastReceive // then sort by most recent activity - } - return false - }) p2.Clock = TimeMs() peers = append(peers, p2) @@ -754,10 +660,10 @@ func (n *Node) pathCheck(ztAddress Address, af int, ip net.IP, port int) bool { return true } -func (n *Node) pathLookup(ztAddress Address) (net.IP, int) { +func (n *Node) pathLookup(id *Identity) (net.IP, int) { n.localConfigLock.RLock() defer n.localConfigLock.RUnlock() - virt := n.localConfig.Virtual[ztAddress] + virt := n.localConfig.Virtual[id.address] if len(virt.Try) > 0 { idx := rand.Int() % len(virt.Try) return virt.Try[idx].IP, virt.Try[idx].Port @@ -831,9 +737,6 @@ func (n *Node) handleTrace(traceMessage string) { func (n *Node) handleUserMessage(originAddress, messageTypeID uint64, data []byte) { } -func (n *Node) handleRemoteTrace(originAddress uint64, dictData []byte) { -} - ////////////////////////////////////////////////////////////////////////////// // These are callbacks called by the core and GoGlue stuff to talk to the @@ -860,7 +763,7 @@ func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsaf } //export goPathLookupFunc -func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, ipP, portP unsafe.Pointer) C.int { +func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredFamily int, identity, familyP, ipP, portP unsafe.Pointer) C.int { nodesByUserPtrLock.RLock() node := nodesByUserPtr[uintptr(gn)] nodesByUserPtrLock.RUnlock() @@ -868,7 +771,11 @@ func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, _ int, familyP, i return 0 } - ip, port := node.pathLookup(Address(ztAddress)) + id, err := newIdentityFromCIdentity(identity) + if err != nil { + return 0 + } + ip, port := node.pathLookup(id) if len(ip) > 0 && port > 0 && port <= 65535 { ip4 := ip.To4() if len(ip4) == 4 { @@ -922,38 +829,6 @@ func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Poin return -1 } -//export goDNSResolverFunc -func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) { - go func() { - nodesByUserPtrLock.RLock() - node := nodesByUserPtr[uintptr(gn)] - nodesByUserPtrLock.RUnlock() - if node == nil { - return - } - - recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes) - recordName := C.GoString((*C.char)(name)) - - recordNameCStrCopy := C.CString(recordName) - for _, rt := range recordTypes { - switch rt { - case C.ZT_DNS_RECORD_TXT: - recs, _ := net.LookupTXT(recordName) - for _, rec := range recs { - if len(rec) > 0 { - rnCS := C.CString(rec) - C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0) - C.free(unsafe.Pointer(rnCS)) - } - } - } - } - C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0) - C.free(unsafe.Pointer(recordNameCStrCopy)) - }() -} - //export goVirtualNetworkConfigFunc func goVirtualNetworkConfigFunc(gn, _ unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) { go func() { @@ -1032,9 +907,6 @@ func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { case C.ZT_EVENT_USER_MESSAGE: um := (*C.ZT_UserMessage)(data) node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) - case C.ZT_EVENT_REMOTE_TRACE: - rt := (*C.ZT_RemoteTrace)(data) - node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) } }() } diff --git a/go/pkg/zerotier/path.go b/go/pkg/zerotier/path.go index d4d2672f6..116a7cfe9 100644 --- a/go/pkg/zerotier/path.go +++ b/go/pkg/zerotier/path.go @@ -22,13 +22,4 @@ type Path struct { LastSend int64 `json:"lastSend"` LastReceive int64 `json:"lastReceive"` TrustedPathID uint64 `json:"trustedPathID"` - Latency float32 `json:"latency"` - PacketDelayVariance float32 `json:"packetDelayVariance"` - ThroughputDisturbCoeff float32 `json:"throughputDisturbCoeff"` - PacketErrorRatio float32 `json:"packetErrorRatio"` - PacketLossRatio float32 `json:"packetLossRatio"` - Stability float32 `json:"stability"` - Throughput uint64 `json:"throughput"` - MaxThroughput uint64 `json:"maxThroughput"` - Allocation float32 `json:"allocation"` } diff --git a/go/pkg/zerotier/root.go b/go/pkg/zerotier/root.go index 73affb178..b9196b686 100644 --- a/go/pkg/zerotier/root.go +++ b/go/pkg/zerotier/root.go @@ -1,20 +1,8 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - package zerotier -// Root describes a root server used to find and establish communication with other nodes. +// Root is a root server with one or more permanent IPs. type Root struct { - Name string `json:"name"` - Locator *Locator `json:"locator,omitempty"` + Identity Identity + DNSName string + PhysicalAddresses []InetAddress } diff --git a/go/vendor/golang.org/x/sys/windows/asm_windows_386.s b/go/vendor/golang.org/x/sys/windows/asm_windows_386.s deleted file mode 100644 index 21d994d31..000000000 --- a/go/vendor/golang.org/x/sys/windows/asm_windows_386.s +++ /dev/null @@ -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) diff --git a/go/vendor/golang.org/x/sys/windows/asm_windows_amd64.s b/go/vendor/golang.org/x/sys/windows/asm_windows_amd64.s deleted file mode 100644 index 5bfdf7974..000000000 --- a/go/vendor/golang.org/x/sys/windows/asm_windows_amd64.s +++ /dev/null @@ -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) diff --git a/go/vendor/golang.org/x/sys/windows/asm_windows_arm.s b/go/vendor/golang.org/x/sys/windows/asm_windows_arm.s deleted file mode 100644 index 55d8b91a2..000000000 --- a/go/vendor/golang.org/x/sys/windows/asm_windows_arm.s +++ /dev/null @@ -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) diff --git a/go/vendor/golang.org/x/sys/windows/dll_windows.go b/go/vendor/golang.org/x/sys/windows/dll_windows.go index ba67658db..d77711341 100644 --- a/go/vendor/golang.org/x/sys/windows/dll_windows.go +++ b/go/vendor/golang.org/x/sys/windows/dll_windows.go @@ -11,6 +11,18 @@ import ( "unsafe" ) +// We need to use LoadLibrary and GetProcAddress from the Go runtime, because +// the these symbols are loaded by the system linker and are required to +// dynamically load additional symbols. Note that in the Go runtime, these +// return syscall.Handle and syscall.Errno, but these are the same, in fact, +// as windows.Handle and windows.Errno, and we intend to keep these the same. + +//go:linkname syscall_loadlibrary syscall.loadlibrary +func syscall_loadlibrary(filename *uint16) (handle Handle, err Errno) + +//go:linkname syscall_getprocaddress syscall.getprocaddress +func syscall_getprocaddress(handle Handle, procname *uint8) (proc uintptr, err Errno) + // DLLError describes reasons for DLL load failures. type DLLError struct { Err error @@ -20,10 +32,6 @@ type DLLError struct { func (e *DLLError) Error() string { return e.Msg } -// Implemented in runtime/syscall_windows.goc; we provide jumps to them in our assembly file. -func loadlibrary(filename *uint16) (handle uintptr, err syscall.Errno) -func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err syscall.Errno) - // A DLL implements access to a single DLL. type DLL struct { Name string @@ -40,7 +48,7 @@ func LoadDLL(name string) (dll *DLL, err error) { if err != nil { return nil, err } - h, e := loadlibrary(namep) + h, e := syscall_loadlibrary(namep) if e != 0 { return nil, &DLLError{ Err: e, @@ -50,7 +58,7 @@ func LoadDLL(name string) (dll *DLL, err error) { } d := &DLL{ Name: name, - Handle: Handle(h), + Handle: h, } return d, nil } @@ -71,7 +79,7 @@ func (d *DLL) FindProc(name string) (proc *Proc, err error) { if err != nil { return nil, err } - a, e := getprocaddress(uintptr(d.Handle), namep) + a, e := syscall_getprocaddress(d.Handle, namep) if e != 0 { return nil, &DLLError{ Err: e, diff --git a/go/vendor/golang.org/x/sys/windows/mkerrors.go b/go/vendor/golang.org/x/sys/windows/empty.s similarity index 51% rename from go/vendor/golang.org/x/sys/windows/mkerrors.go rename to go/vendor/golang.org/x/sys/windows/empty.s index a204e6caa..69309e4da 100644 --- a/go/vendor/golang.org/x/sys/windows/mkerrors.go +++ b/go/vendor/golang.org/x/sys/windows/empty.s @@ -2,8 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build generate +// +build !go1.12 -package windows - -//go:generate ./mkerrors.bash zerrors_windows.go +// This file is here to allow bodyless functions with go:linkname for Go 1.11 +// and earlier (see https://golang.org/issue/23311). diff --git a/go/vendor/golang.org/x/sys/windows/mkerrors.bash b/go/vendor/golang.org/x/sys/windows/mkerrors.bash index a70b24f39..2163843a1 100644 --- a/go/vendor/golang.org/x/sys/windows/mkerrors.bash +++ b/go/vendor/golang.org/x/sys/windows/mkerrors.bash @@ -7,14 +7,13 @@ set -e shopt -s nullglob -[[ $# -eq 1 ]] || { echo "Usage: $0 OUTPUT_FILE.go" >&2; exit 1; } winerror="$(printf '%s\n' "/mnt/c/Program Files (x86)/Windows Kits/"/*/Include/*/shared/winerror.h | sort -Vr | head -n 1)" [[ -n $winerror ]] || { echo "Unable to find winerror.h" >&2; exit 1; } declare -A errors { - echo "// Code generated by 'go generate'; DO NOT EDIT." + echo "// Code generated by 'mkerrors.bash'; DO NOT EDIT." echo echo "package windows" echo "import \"syscall\"" @@ -61,4 +60,4 @@ declare -A errors done < "$winerror" echo ")" -} | gofmt > "$1" +} | gofmt > "zerrors_windows.go" diff --git a/go/vendor/golang.org/x/sys/windows/mkknownfolderids.bash b/go/vendor/golang.org/x/sys/windows/mkknownfolderids.bash new file mode 100644 index 000000000..ab8924e93 --- /dev/null +++ b/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" diff --git a/go/vendor/golang.org/x/sys/windows/mksyscall.go b/go/vendor/golang.org/x/sys/windows/mksyscall.go index 627705727..328e3b2ac 100644 --- a/go/vendor/golang.org/x/sys/windows/mksyscall.go +++ b/go/vendor/golang.org/x/sys/windows/mksyscall.go @@ -6,4 +6,4 @@ package windows -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go eventlog.go service.go syscall_windows.go security_windows.go diff --git a/go/vendor/golang.org/x/sys/windows/security_windows.go b/go/vendor/golang.org/x/sys/windows/security_windows.go index b84840feb..4b6eff186 100644 --- a/go/vendor/golang.org/x/sys/windows/security_windows.go +++ b/go/vendor/golang.org/x/sys/windows/security_windows.go @@ -9,14 +9,6 @@ import ( "unsafe" ) -const ( - STANDARD_RIGHTS_REQUIRED = 0xf0000 - STANDARD_RIGHTS_READ = 0x20000 - STANDARD_RIGHTS_WRITE = 0x20000 - STANDARD_RIGHTS_EXECUTE = 0x20000 - STANDARD_RIGHTS_ALL = 0x1F0000 -) - const ( NameUnknown = 0 NameFullyQualifiedDN = 1 @@ -235,16 +227,15 @@ func LookupSID(system, account string) (sid *SID, domain string, accType uint32, } } -// String converts SID to a string format -// suitable for display, storage, or transmission. -func (sid *SID) String() (string, error) { +// String converts SID to a string format suitable for display, storage, or transmission. +func (sid *SID) String() string { var s *uint16 e := ConvertSidToStringSid(sid, &s) if e != nil { - return "", e + return "" } defer LocalFree((Handle)(unsafe.Pointer(s))) - return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]), nil + return UTF16ToString((*[256]uint16)(unsafe.Pointer(s))[:]) } // Len returns the length, in bytes, of a valid security identifier SID. @@ -603,12 +594,22 @@ type Tokenprimarygroup struct { type Tokengroups struct { GroupCount uint32 - Groups [1]SIDAndAttributes + Groups [1]SIDAndAttributes // Use AllGroups() for iterating. +} + +// AllGroups returns a slice that can be used to iterate over the groups in g. +func (g *Tokengroups) AllGroups() []SIDAndAttributes { + return (*[(1 << 28) - 1]SIDAndAttributes)(unsafe.Pointer(&g.Groups[0]))[:g.GroupCount:g.GroupCount] } type Tokenprivileges struct { PrivilegeCount uint32 - Privileges [1]LUIDAndAttributes + Privileges [1]LUIDAndAttributes // Use AllPrivileges() for iterating. +} + +// AllPrivileges returns a slice that can be used to iterate over the privileges in p. +func (p *Tokenprivileges) AllPrivileges() []LUIDAndAttributes { + return (*[(1 << 27) - 1]LUIDAndAttributes)(unsafe.Pointer(&p.Privileges[0]))[:p.PrivilegeCount:p.PrivilegeCount] } type Tokenmandatorylabel struct { @@ -634,6 +635,8 @@ func (tml *Tokenmandatorylabel) Size() uint32 { //sys DuplicateTokenEx(existingToken Token, desiredAccess uint32, tokenAttributes *SecurityAttributes, impersonationLevel uint32, tokenType uint32, newToken *Token) (err error) = advapi32.DuplicateTokenEx //sys GetUserProfileDirectory(t Token, dir *uint16, dirLen *uint32) (err error) = userenv.GetUserProfileDirectoryW //sys getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemDirectoryW +//sys getWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetWindowsDirectoryW +//sys getSystemWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) = kernel32.GetSystemWindowsDirectoryW // An access token contains the security information for a logon session. // The system creates an access token when a user logs on, and every @@ -644,21 +647,16 @@ func (tml *Tokenmandatorylabel) Size() uint32 { // system-related operations on the local computer. type Token Handle -// OpenCurrentProcessToken opens the access token -// associated with current process. It is a real -// token that needs to be closed, unlike -// GetCurrentProcessToken. +// OpenCurrentProcessToken opens an access token associated with current +// process with TOKEN_QUERY access. It is a real token that needs to be closed. +// +// Deprecated: Explicitly call OpenProcessToken(CurrentProcess(), ...) +// with the desired access instead, or use GetCurrentProcessToken for a +// TOKEN_QUERY token. func OpenCurrentProcessToken() (Token, error) { - p, e := GetCurrentProcess() - if e != nil { - return 0, e - } - var t Token - e = OpenProcessToken(p, TOKEN_QUERY, &t) - if e != nil { - return 0, e - } - return t, nil + var token Token + err := OpenProcessToken(CurrentProcess(), TOKEN_QUERY, &token) + return token, err } // GetCurrentProcessToken returns the access token associated with @@ -775,8 +773,8 @@ func (token Token) GetLinkedToken() (Token, error) { return linkedToken, nil } -// GetSystemDirectory retrieves path to current location of the system -// directory, which is typically, though not always, C:\Windows\System32. +// GetSystemDirectory retrieves the path to current location of the system +// directory, which is typically, though not always, `C:\Windows\System32`. func GetSystemDirectory() (string, error) { n := uint32(MAX_PATH) for { @@ -792,6 +790,42 @@ func GetSystemDirectory() (string, error) { } } +// GetWindowsDirectory retrieves the path to current location of the Windows +// directory, which is typically, though not always, `C:\Windows`. This may +// be a private user directory in the case that the application is running +// under a terminal server. +func GetWindowsDirectory() (string, error) { + n := uint32(MAX_PATH) + for { + b := make([]uint16, n) + l, e := getWindowsDirectory(&b[0], n) + if e != nil { + return "", e + } + if l <= n { + return UTF16ToString(b[:l]), nil + } + n = l + } +} + +// GetSystemWindowsDirectory retrieves the path to current location of the +// Windows directory, which is typically, though not always, `C:\Windows`. +func GetSystemWindowsDirectory() (string, error) { + n := uint32(MAX_PATH) + for { + b := make([]uint16, n) + l, e := getSystemWindowsDirectory(&b[0], n) + if e != nil { + return "", e + } + if l <= n { + return UTF16ToString(b[:l]), nil + } + n = l + } +} + // IsMember reports whether the access token t is a member of the provided SID. func (t Token) IsMember(sid *SID) (bool, error) { var b int32 @@ -842,3 +876,521 @@ type WTS_SESSION_INFO struct { //sys WTSQueryUserToken(session uint32, token *Token) (err error) = wtsapi32.WTSQueryUserToken //sys WTSEnumerateSessions(handle Handle, reserved uint32, version uint32, sessions **WTS_SESSION_INFO, count *uint32) (err error) = wtsapi32.WTSEnumerateSessionsW //sys WTSFreeMemory(ptr uintptr) = wtsapi32.WTSFreeMemory + +type ACL struct { + aclRevision byte + sbz1 byte + aclSize uint16 + aceCount uint16 + sbz2 uint16 +} + +type SECURITY_DESCRIPTOR struct { + revision byte + sbz1 byte + control SECURITY_DESCRIPTOR_CONTROL + owner *SID + group *SID + sacl *ACL + dacl *ACL +} + +type SecurityAttributes struct { + Length uint32 + SecurityDescriptor *SECURITY_DESCRIPTOR + InheritHandle uint32 +} + +type SE_OBJECT_TYPE uint32 + +// Constants for type SE_OBJECT_TYPE +const ( + SE_UNKNOWN_OBJECT_TYPE = 0 + SE_FILE_OBJECT = 1 + SE_SERVICE = 2 + SE_PRINTER = 3 + SE_REGISTRY_KEY = 4 + SE_LMSHARE = 5 + SE_KERNEL_OBJECT = 6 + SE_WINDOW_OBJECT = 7 + SE_DS_OBJECT = 8 + SE_DS_OBJECT_ALL = 9 + SE_PROVIDER_DEFINED_OBJECT = 10 + SE_WMIGUID_OBJECT = 11 + SE_REGISTRY_WOW64_32KEY = 12 + SE_REGISTRY_WOW64_64KEY = 13 +) + +type SECURITY_INFORMATION uint32 + +// Constants for type SECURITY_INFORMATION +const ( + OWNER_SECURITY_INFORMATION = 0x00000001 + GROUP_SECURITY_INFORMATION = 0x00000002 + DACL_SECURITY_INFORMATION = 0x00000004 + SACL_SECURITY_INFORMATION = 0x00000008 + LABEL_SECURITY_INFORMATION = 0x00000010 + ATTRIBUTE_SECURITY_INFORMATION = 0x00000020 + SCOPE_SECURITY_INFORMATION = 0x00000040 + BACKUP_SECURITY_INFORMATION = 0x00010000 + PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 + PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000 + UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 + UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000 +) + +type SECURITY_DESCRIPTOR_CONTROL uint16 + +// Constants for type SECURITY_DESCRIPTOR_CONTROL +const ( + SE_OWNER_DEFAULTED = 0x0001 + SE_GROUP_DEFAULTED = 0x0002 + SE_DACL_PRESENT = 0x0004 + SE_DACL_DEFAULTED = 0x0008 + SE_SACL_PRESENT = 0x0010 + SE_SACL_DEFAULTED = 0x0020 + SE_DACL_AUTO_INHERIT_REQ = 0x0100 + SE_SACL_AUTO_INHERIT_REQ = 0x0200 + SE_DACL_AUTO_INHERITED = 0x0400 + SE_SACL_AUTO_INHERITED = 0x0800 + SE_DACL_PROTECTED = 0x1000 + SE_SACL_PROTECTED = 0x2000 + SE_RM_CONTROL_VALID = 0x4000 + SE_SELF_RELATIVE = 0x8000 +) + +type ACCESS_MASK uint32 + +// Constants for type ACCESS_MASK +const ( + DELETE = 0x00010000 + READ_CONTROL = 0x00020000 + WRITE_DAC = 0x00040000 + WRITE_OWNER = 0x00080000 + SYNCHRONIZE = 0x00100000 + STANDARD_RIGHTS_REQUIRED = 0x000F0000 + STANDARD_RIGHTS_READ = READ_CONTROL + STANDARD_RIGHTS_WRITE = READ_CONTROL + STANDARD_RIGHTS_EXECUTE = READ_CONTROL + STANDARD_RIGHTS_ALL = 0x001F0000 + SPECIFIC_RIGHTS_ALL = 0x0000FFFF + ACCESS_SYSTEM_SECURITY = 0x01000000 + MAXIMUM_ALLOWED = 0x02000000 + GENERIC_READ = 0x80000000 + GENERIC_WRITE = 0x40000000 + GENERIC_EXECUTE = 0x20000000 + GENERIC_ALL = 0x10000000 +) + +type ACCESS_MODE uint32 + +// Constants for type ACCESS_MODE +const ( + NOT_USED_ACCESS = 0 + GRANT_ACCESS = 1 + SET_ACCESS = 2 + DENY_ACCESS = 3 + REVOKE_ACCESS = 4 + SET_AUDIT_SUCCESS = 5 + SET_AUDIT_FAILURE = 6 +) + +// Constants for AceFlags and Inheritance fields +const ( + NO_INHERITANCE = 0x0 + SUB_OBJECTS_ONLY_INHERIT = 0x1 + SUB_CONTAINERS_ONLY_INHERIT = 0x2 + SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3 + INHERIT_NO_PROPAGATE = 0x4 + INHERIT_ONLY = 0x8 + INHERITED_ACCESS_ENTRY = 0x10 + INHERITED_PARENT = 0x10000000 + INHERITED_GRANDPARENT = 0x20000000 + OBJECT_INHERIT_ACE = 0x1 + CONTAINER_INHERIT_ACE = 0x2 + NO_PROPAGATE_INHERIT_ACE = 0x4 + INHERIT_ONLY_ACE = 0x8 + INHERITED_ACE = 0x10 + VALID_INHERIT_FLAGS = 0x1F +) + +type MULTIPLE_TRUSTEE_OPERATION uint32 + +// Constants for MULTIPLE_TRUSTEE_OPERATION +const ( + NO_MULTIPLE_TRUSTEE = 0 + TRUSTEE_IS_IMPERSONATE = 1 +) + +type TRUSTEE_FORM uint32 + +// Constants for TRUSTEE_FORM +const ( + TRUSTEE_IS_SID = 0 + TRUSTEE_IS_NAME = 1 + TRUSTEE_BAD_FORM = 2 + TRUSTEE_IS_OBJECTS_AND_SID = 3 + TRUSTEE_IS_OBJECTS_AND_NAME = 4 +) + +type TRUSTEE_TYPE uint32 + +// Constants for TRUSTEE_TYPE +const ( + TRUSTEE_IS_UNKNOWN = 0 + TRUSTEE_IS_USER = 1 + TRUSTEE_IS_GROUP = 2 + TRUSTEE_IS_DOMAIN = 3 + TRUSTEE_IS_ALIAS = 4 + TRUSTEE_IS_WELL_KNOWN_GROUP = 5 + TRUSTEE_IS_DELETED = 6 + TRUSTEE_IS_INVALID = 7 + TRUSTEE_IS_COMPUTER = 8 +) + +// Constants for ObjectsPresent field +const ( + ACE_OBJECT_TYPE_PRESENT = 0x1 + ACE_INHERITED_OBJECT_TYPE_PRESENT = 0x2 +) + +type EXPLICIT_ACCESS struct { + AccessPermissions ACCESS_MASK + AccessMode ACCESS_MODE + Inheritance uint32 + Trustee TRUSTEE +} + +// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions. +type TrusteeValue uintptr + +func TrusteeValueFromString(str string) TrusteeValue { + return TrusteeValue(unsafe.Pointer(StringToUTF16Ptr(str))) +} +func TrusteeValueFromSID(sid *SID) TrusteeValue { + return TrusteeValue(unsafe.Pointer(sid)) +} +func TrusteeValueFromObjectsAndSid(objectsAndSid *OBJECTS_AND_SID) TrusteeValue { + return TrusteeValue(unsafe.Pointer(objectsAndSid)) +} +func TrusteeValueFromObjectsAndName(objectsAndName *OBJECTS_AND_NAME) TrusteeValue { + return TrusteeValue(unsafe.Pointer(objectsAndName)) +} + +type TRUSTEE struct { + MultipleTrustee *TRUSTEE + MultipleTrusteeOperation MULTIPLE_TRUSTEE_OPERATION + TrusteeForm TRUSTEE_FORM + TrusteeType TRUSTEE_TYPE + TrusteeValue TrusteeValue +} + +type OBJECTS_AND_SID struct { + ObjectsPresent uint32 + ObjectTypeGuid GUID + InheritedObjectTypeGuid GUID + Sid *SID +} + +type OBJECTS_AND_NAME struct { + ObjectsPresent uint32 + ObjectType SE_OBJECT_TYPE + ObjectTypeName *uint16 + InheritedObjectTypeName *uint16 + Name *uint16 +} + +//sys getSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) = advapi32.GetSecurityInfo +//sys SetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) = advapi32.SetSecurityInfo +//sys getNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) = advapi32.GetNamedSecurityInfoW +//sys SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) = advapi32.SetNamedSecurityInfoW + +//sys buildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, countAccessEntries uint32, accessEntries *EXPLICIT_ACCESS, countAuditEntries uint32, auditEntries *EXPLICIT_ACCESS, oldSecurityDescriptor *SECURITY_DESCRIPTOR, sizeNewSecurityDescriptor *uint32, newSecurityDescriptor **SECURITY_DESCRIPTOR) (ret error) = advapi32.BuildSecurityDescriptorW +//sys initializeSecurityDescriptor(absoluteSD *SECURITY_DESCRIPTOR, revision uint32) (err error) = advapi32.InitializeSecurityDescriptor + +//sys getSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, control *SECURITY_DESCRIPTOR_CONTROL, revision *uint32) (err error) = advapi32.GetSecurityDescriptorControl +//sys getSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent *bool, dacl **ACL, daclDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorDacl +//sys getSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent *bool, sacl **ACL, saclDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorSacl +//sys getSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner **SID, ownerDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorOwner +//sys getSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group **SID, groupDefaulted *bool) (err error) = advapi32.GetSecurityDescriptorGroup +//sys getSecurityDescriptorLength(sd *SECURITY_DESCRIPTOR) (len uint32) = advapi32.GetSecurityDescriptorLength +//sys getSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) (ret error) [failretval!=0] = advapi32.GetSecurityDescriptorRMControl +//sys isValidSecurityDescriptor(sd *SECURITY_DESCRIPTOR) (isValid bool) = advapi32.IsValidSecurityDescriptor + +//sys setSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) (err error) = advapi32.SetSecurityDescriptorControl +//sys setSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent bool, dacl *ACL, daclDefaulted bool) (err error) = advapi32.SetSecurityDescriptorDacl +//sys setSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent bool, sacl *ACL, saclDefaulted bool) (err error) = advapi32.SetSecurityDescriptorSacl +//sys setSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner *SID, ownerDefaulted bool) (err error) = advapi32.SetSecurityDescriptorOwner +//sys setSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group *SID, groupDefaulted bool) (err error) = advapi32.SetSecurityDescriptorGroup +//sys setSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) = advapi32.SetSecurityDescriptorRMControl + +//sys convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd **SECURITY_DESCRIPTOR, size *uint32) (err error) = advapi32.ConvertStringSecurityDescriptorToSecurityDescriptorW +//sys convertSecurityDescriptorToStringSecurityDescriptor(sd *SECURITY_DESCRIPTOR, revision uint32, securityInformation SECURITY_INFORMATION, str **uint16, strLen *uint32) (err error) = advapi32.ConvertSecurityDescriptorToStringSecurityDescriptorW + +//sys makeAbsoluteSD(selfRelativeSD *SECURITY_DESCRIPTOR, absoluteSD *SECURITY_DESCRIPTOR, absoluteSDSize *uint32, dacl *ACL, daclSize *uint32, sacl *ACL, saclSize *uint32, owner *SID, ownerSize *uint32, group *SID, groupSize *uint32) (err error) = advapi32.MakeAbsoluteSD +//sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD + +//sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW + +// Control returns the security descriptor control bits. +func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) { + err = getSecurityDescriptorControl(sd, &control, &revision) + return +} + +// SetControl sets the security descriptor control bits. +func (sd *SECURITY_DESCRIPTOR) SetControl(controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) error { + return setSecurityDescriptorControl(sd, controlBitsOfInterest, controlBitsToSet) +} + +// RMControl returns the security descriptor resource manager control bits. +func (sd *SECURITY_DESCRIPTOR) RMControl() (control uint8, err error) { + err = getSecurityDescriptorRMControl(sd, &control) + return +} + +// SetRMControl sets the security descriptor resource manager control bits. +func (sd *SECURITY_DESCRIPTOR) SetRMControl(rmControl uint8) { + setSecurityDescriptorRMControl(sd, &rmControl) +} + +// DACL returns the security descriptor DACL and whether it was defaulted. The dacl return value may be nil +// if a DACL exists but is an "empty DACL", meaning fully permissive. If the DACL does not exist, err returns +// ERROR_OBJECT_NOT_FOUND. +func (sd *SECURITY_DESCRIPTOR) DACL() (dacl *ACL, defaulted bool, err error) { + var present bool + err = getSecurityDescriptorDacl(sd, &present, &dacl, &defaulted) + if !present { + err = ERROR_OBJECT_NOT_FOUND + } + return +} + +// SetDACL sets the absolute security descriptor DACL. +func (absoluteSD *SECURITY_DESCRIPTOR) SetDACL(dacl *ACL, present, defaulted bool) error { + return setSecurityDescriptorDacl(absoluteSD, present, dacl, defaulted) +} + +// SACL returns the security descriptor SACL and whether it was defaulted. The sacl return value may be nil +// if a SACL exists but is an "empty SACL", meaning fully permissive. If the SACL does not exist, err returns +// ERROR_OBJECT_NOT_FOUND. +func (sd *SECURITY_DESCRIPTOR) SACL() (sacl *ACL, defaulted bool, err error) { + var present bool + err = getSecurityDescriptorSacl(sd, &present, &sacl, &defaulted) + if !present { + err = ERROR_OBJECT_NOT_FOUND + } + return +} + +// SetSACL sets the absolute security descriptor SACL. +func (absoluteSD *SECURITY_DESCRIPTOR) SetSACL(sacl *ACL, present, defaulted bool) error { + return setSecurityDescriptorSacl(absoluteSD, present, sacl, defaulted) +} + +// Owner returns the security descriptor owner and whether it was defaulted. +func (sd *SECURITY_DESCRIPTOR) Owner() (owner *SID, defaulted bool, err error) { + err = getSecurityDescriptorOwner(sd, &owner, &defaulted) + return +} + +// SetOwner sets the absolute security descriptor owner. +func (absoluteSD *SECURITY_DESCRIPTOR) SetOwner(owner *SID, defaulted bool) error { + return setSecurityDescriptorOwner(absoluteSD, owner, defaulted) +} + +// Group returns the security descriptor group and whether it was defaulted. +func (sd *SECURITY_DESCRIPTOR) Group() (group *SID, defaulted bool, err error) { + err = getSecurityDescriptorGroup(sd, &group, &defaulted) + return +} + +// SetGroup sets the absolute security descriptor owner. +func (absoluteSD *SECURITY_DESCRIPTOR) SetGroup(group *SID, defaulted bool) error { + return setSecurityDescriptorGroup(absoluteSD, group, defaulted) +} + +// Length returns the length of the security descriptor. +func (sd *SECURITY_DESCRIPTOR) Length() uint32 { + return getSecurityDescriptorLength(sd) +} + +// IsValid returns whether the security descriptor is valid. +func (sd *SECURITY_DESCRIPTOR) IsValid() bool { + return isValidSecurityDescriptor(sd) +} + +// String returns the SDDL form of the security descriptor, with a function signature that can be +// used with %v formatting directives. +func (sd *SECURITY_DESCRIPTOR) String() string { + var sddl *uint16 + err := convertSecurityDescriptorToStringSecurityDescriptor(sd, 1, 0xff, &sddl, nil) + if err != nil { + return "" + } + defer LocalFree(Handle(unsafe.Pointer(sddl))) + return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(sddl))[:]) +} + +// ToAbsolute converts a self-relative security descriptor into an absolute one. +func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DESCRIPTOR, err error) { + control, _, err := selfRelativeSD.Control() + if err != nil { + return + } + if control&SE_SELF_RELATIVE == 0 { + err = ERROR_INVALID_PARAMETER + return + } + var absoluteSDSize, daclSize, saclSize, ownerSize, groupSize uint32 + err = makeAbsoluteSD(selfRelativeSD, nil, &absoluteSDSize, + nil, &daclSize, nil, &saclSize, nil, &ownerSize, nil, &groupSize) + switch err { + case ERROR_INSUFFICIENT_BUFFER: + case nil: + // makeAbsoluteSD is expected to fail, but it succeeds. + return nil, ERROR_INTERNAL_ERROR + default: + return nil, err + } + if absoluteSDSize > 0 { + absoluteSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, absoluteSDSize)[0])) + } + var ( + dacl *ACL + sacl *ACL + owner *SID + group *SID + ) + if daclSize > 0 { + dacl = (*ACL)(unsafe.Pointer(&make([]byte, daclSize)[0])) + } + if saclSize > 0 { + sacl = (*ACL)(unsafe.Pointer(&make([]byte, saclSize)[0])) + } + if ownerSize > 0 { + owner = (*SID)(unsafe.Pointer(&make([]byte, ownerSize)[0])) + } + if groupSize > 0 { + group = (*SID)(unsafe.Pointer(&make([]byte, groupSize)[0])) + } + err = makeAbsoluteSD(selfRelativeSD, absoluteSD, &absoluteSDSize, + dacl, &daclSize, sacl, &saclSize, owner, &ownerSize, group, &groupSize) + return +} + +// ToSelfRelative converts an absolute security descriptor into a self-relative one. +func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURITY_DESCRIPTOR, err error) { + control, _, err := absoluteSD.Control() + if err != nil { + return + } + if control&SE_SELF_RELATIVE != 0 { + err = ERROR_INVALID_PARAMETER + return + } + var selfRelativeSDSize uint32 + err = makeSelfRelativeSD(absoluteSD, nil, &selfRelativeSDSize) + switch err { + case ERROR_INSUFFICIENT_BUFFER: + case nil: + // makeSelfRelativeSD is expected to fail, but it succeeds. + return nil, ERROR_INTERNAL_ERROR + default: + return nil, err + } + if selfRelativeSDSize > 0 { + selfRelativeSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, selfRelativeSDSize)[0])) + } + err = makeSelfRelativeSD(absoluteSD, selfRelativeSD, &selfRelativeSDSize) + return +} + +func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR { + sdBytes := make([]byte, selfRelativeSD.Length()) + copy(sdBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(selfRelativeSD))[:len(sdBytes)]) + return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&sdBytes[0])) +} + +// SecurityDescriptorFromString converts an SDDL string describing a security descriptor into a +// self-relative security descriptor object allocated on the Go heap. +func SecurityDescriptorFromString(sddl string) (sd *SECURITY_DESCRIPTOR, err error) { + var winHeapSD *SECURITY_DESCRIPTOR + err = convertStringSecurityDescriptorToSecurityDescriptor(sddl, 1, &winHeapSD, nil) + if err != nil { + return + } + defer LocalFree(Handle(unsafe.Pointer(winHeapSD))) + return winHeapSD.copySelfRelativeSecurityDescriptor(), nil +} + +// GetSecurityInfo queries the security information for a given handle and returns the self-relative security +// descriptor result on the Go heap. +func GetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION) (sd *SECURITY_DESCRIPTOR, err error) { + var winHeapSD *SECURITY_DESCRIPTOR + err = getSecurityInfo(handle, objectType, securityInformation, nil, nil, nil, nil, &winHeapSD) + if err != nil { + return + } + defer LocalFree(Handle(unsafe.Pointer(winHeapSD))) + return winHeapSD.copySelfRelativeSecurityDescriptor(), nil +} + +// GetNamedSecurityInfo queries the security information for a given named object and returns the self-relative security +// descriptor result on the Go heap. +func GetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION) (sd *SECURITY_DESCRIPTOR, err error) { + var winHeapSD *SECURITY_DESCRIPTOR + err = getNamedSecurityInfo(objectName, objectType, securityInformation, nil, nil, nil, nil, &winHeapSD) + if err != nil { + return + } + defer LocalFree(Handle(unsafe.Pointer(winHeapSD))) + return winHeapSD.copySelfRelativeSecurityDescriptor(), nil +} + +// BuildSecurityDescriptor makes a new security descriptor using the input trustees, explicit access lists, and +// prior security descriptor to be merged, any of which can be nil, returning the self-relative security descriptor +// result on the Go heap. +func BuildSecurityDescriptor(owner *TRUSTEE, group *TRUSTEE, accessEntries []EXPLICIT_ACCESS, auditEntries []EXPLICIT_ACCESS, mergedSecurityDescriptor *SECURITY_DESCRIPTOR) (sd *SECURITY_DESCRIPTOR, err error) { + var winHeapSD *SECURITY_DESCRIPTOR + var winHeapSDSize uint32 + var firstAccessEntry *EXPLICIT_ACCESS + if len(accessEntries) > 0 { + firstAccessEntry = &accessEntries[0] + } + var firstAuditEntry *EXPLICIT_ACCESS + if len(auditEntries) > 0 { + firstAuditEntry = &auditEntries[0] + } + err = buildSecurityDescriptor(owner, group, uint32(len(accessEntries)), firstAccessEntry, uint32(len(auditEntries)), firstAuditEntry, mergedSecurityDescriptor, &winHeapSDSize, &winHeapSD) + if err != nil { + return + } + defer LocalFree(Handle(unsafe.Pointer(winHeapSD))) + return winHeapSD.copySelfRelativeSecurityDescriptor(), nil +} + +// NewSecurityDescriptor creates and initializes a new absolute security descriptor. +func NewSecurityDescriptor() (absoluteSD *SECURITY_DESCRIPTOR, err error) { + absoluteSD = &SECURITY_DESCRIPTOR{} + err = initializeSecurityDescriptor(absoluteSD, 1) + return +} + +// ACLFromEntries returns a new ACL on the Go heap containing a list of explicit entries as well as those of another ACL. +// Both explicitEntries and mergedACL are optional and can be nil. +func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL, err error) { + var firstExplicitEntry *EXPLICIT_ACCESS + if len(explicitEntries) > 0 { + firstExplicitEntry = &explicitEntries[0] + } + var winHeapACL *ACL + err = setEntriesInAcl(uint32(len(explicitEntries)), firstExplicitEntry, mergedACL, &winHeapACL) + if err != nil { + return + } + defer LocalFree(Handle(unsafe.Pointer(winHeapACL))) + aclBytes := make([]byte, winHeapACL.aclSize) + copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes)]) + return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil +} diff --git a/go/vendor/golang.org/x/sys/windows/service.go b/go/vendor/golang.org/x/sys/windows/service.go index 9a59b42f6..847e00bc9 100644 --- a/go/vendor/golang.org/x/sys/windows/service.go +++ b/go/vendor/golang.org/x/sys/windows/service.go @@ -159,6 +159,10 @@ type SERVICE_DESCRIPTION struct { Description *uint16 } +type SERVICE_DELAYED_AUTO_START_INFO struct { + IsDelayedAutoStartUp uint32 +} + type SERVICE_STATUS_PROCESS struct { ServiceType uint32 CurrentState uint32 @@ -200,12 +204,19 @@ type SC_ACTION struct { Delay uint32 } +type QUERY_SERVICE_LOCK_STATUS struct { + IsLocked uint32 + LockOwner *uint16 + LockDuration uint32 +} + //sys CloseServiceHandle(handle Handle) (err error) = advapi32.CloseServiceHandle //sys CreateService(mgr Handle, serviceName *uint16, displayName *uint16, access uint32, srvType uint32, startType uint32, errCtl uint32, pathName *uint16, loadOrderGroup *uint16, tagId *uint32, dependencies *uint16, serviceStartName *uint16, password *uint16) (handle Handle, err error) [failretval==0] = advapi32.CreateServiceW //sys OpenService(mgr Handle, serviceName *uint16, access uint32) (handle Handle, err error) [failretval==0] = advapi32.OpenServiceW //sys DeleteService(service Handle) (err error) = advapi32.DeleteService //sys StartService(service Handle, numArgs uint32, argVectors **uint16) (err error) = advapi32.StartServiceW //sys QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) = advapi32.QueryServiceStatus +//sys QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) = advapi32.QueryServiceLockStatusW //sys ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) = advapi32.ControlService //sys StartServiceCtrlDispatcher(serviceTable *SERVICE_TABLE_ENTRY) (err error) = advapi32.StartServiceCtrlDispatcherW //sys SetServiceStatus(service Handle, serviceStatus *SERVICE_STATUS) (err error) = advapi32.SetServiceStatus diff --git a/go/vendor/golang.org/x/sys/windows/syscall_windows.go b/go/vendor/golang.org/x/sys/windows/syscall_windows.go index b98b94545..053d664d0 100644 --- a/go/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/go/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -10,6 +10,7 @@ import ( errorspkg "errors" "sync" "syscall" + "time" "unicode/utf16" "unsafe" ) @@ -56,6 +57,10 @@ const ( FILE_VOLUME_IS_COMPRESSED = 0x00008000 FILE_VOLUME_QUOTAS = 0x00000020 + // Flags for LockFileEx. + LOCKFILE_FAIL_IMMEDIATELY = 0x00000001 + LOCKFILE_EXCLUSIVE_LOCK = 0x00000002 + // Return values of SleepEx and other APC functions STATUS_USER_APC = 0x000000C0 WAIT_IO_COMPLETION = STATUS_USER_APC @@ -135,6 +140,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys LoadLibraryEx(libname string, zero Handle, flags uintptr) (handle Handle, err error) = LoadLibraryExW //sys FreeLibrary(handle Handle) (err error) //sys GetProcAddress(module Handle, procname string) (proc uintptr, err error) +//sys GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) = kernel32.GetModuleFileNameW +//sys GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) = kernel32.GetModuleHandleExW //sys GetVersion() (ver uint32, err error) //sys FormatMessage(flags uint32, msgsrc uintptr, msgid uint32, langid uint32, buf []uint16, args *byte) (n uint32, err error) = FormatMessageW //sys ExitProcess(exitcode uint32) @@ -159,6 +166,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys DeleteFile(path *uint16) (err error) = DeleteFileW //sys MoveFile(from *uint16, to *uint16) (err error) = MoveFileW //sys MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) = MoveFileExW +//sys LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) +//sys UnlockFileEx(file Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) //sys GetComputerName(buf *uint16, n *uint32) (err error) = GetComputerNameW //sys GetComputerNameEx(nametype uint32, buf *uint16, n *uint32) (err error) = GetComputerNameExW //sys SetEndOfFile(handle Handle) (err error) @@ -171,13 +180,12 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CancelIo(s Handle) (err error) //sys CancelIoEx(s Handle, o *Overlapped) (err error) //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (err error) = CreateProcessW -//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error) -//sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) = shell32.ShellExecuteW +//sys OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error) +//sys ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) [failretval<=32] = shell32.ShellExecuteW +//sys shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) = shell32.SHGetKnownFolderPath //sys TerminateProcess(handle Handle, exitcode uint32) (err error) //sys GetExitCodeProcess(handle Handle, exitcode *uint32) (err error) //sys GetStartupInfo(startupInfo *StartupInfo) (err error) = GetStartupInfoW -//sys GetCurrentProcess() (pseudoHandle Handle, err error) -//sys GetCurrentThread() (pseudoHandle Handle, err error) //sys GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) //sys DuplicateHandle(hSourceProcessHandle Handle, hSourceHandle Handle, hTargetProcessHandle Handle, lpTargetHandle *Handle, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (err error) //sys WaitForSingleObject(handle Handle, waitMilliseconds uint32) (event uint32, err error) [failretval==0xffffffff] @@ -194,6 +202,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetEnvironmentVariable(name *uint16, value *uint16) (err error) = kernel32.SetEnvironmentVariableW //sys CreateEnvironmentBlock(block **uint16, token Token, inheritExisting bool) (err error) = userenv.CreateEnvironmentBlock //sys DestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock +//sys getTickCount64() (ms uint64) = kernel32.GetTickCount64 //sys SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) //sys GetFileAttributes(name *uint16) (attrs uint32, err error) [failretval==INVALID_FILE_ATTRIBUTES] = kernel32.GetFileAttributesW //sys SetFileAttributes(name *uint16, attrs uint32) (err error) = kernel32.SetFileAttributesW @@ -232,7 +241,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys RegQueryInfoKey(key Handle, class *uint16, classLen *uint32, reserved *uint32, subkeysLen *uint32, maxSubkeyLen *uint32, maxClassLen *uint32, valuesLen *uint32, maxValueNameLen *uint32, maxValueLen *uint32, saLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegQueryInfoKeyW //sys RegEnumKeyEx(key Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, classLen *uint32, lastWriteTime *Filetime) (regerrno error) = advapi32.RegEnumKeyExW //sys RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegQueryValueExW -//sys getCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId +//sys GetCurrentProcessId() (pid uint32) = kernel32.GetCurrentProcessId //sys GetConsoleMode(console Handle, mode *uint32) (err error) = kernel32.GetConsoleMode //sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode //sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo @@ -241,6 +250,8 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys CreateToolhelp32Snapshot(flags uint32, processId uint32) (handle Handle, err error) [failretval==InvalidHandle] = kernel32.CreateToolhelp32Snapshot //sys Process32First(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32FirstW //sys Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) = kernel32.Process32NextW +//sys Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error) +//sys Thread32Next(snapshot Handle, threadEntry *ThreadEntry32) (err error) //sys DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBufferSize uint32, outBuffer *byte, outBufferSize uint32, bytesReturned *uint32, overlapped *Overlapped) (err error) // This function returns 1 byte BOOLEAN rather than the 4 byte BOOL. //sys CreateSymbolicLink(symlinkfilename *uint16, targetfilename *uint16, flags uint32) (err error) [failretval&0xff==0] = CreateSymbolicLinkW @@ -252,6 +263,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetEvent(event Handle) (err error) = kernel32.SetEvent //sys ResetEvent(event Handle) (err error) = kernel32.ResetEvent //sys PulseEvent(event Handle) (err error) = kernel32.PulseEvent +//sys CreateMutex(mutexAttrs *SecurityAttributes, initialOwner bool, name *uint16) (handle Handle, err error) = kernel32.CreateMutexW +//sys CreateMutexEx(mutexAttrs *SecurityAttributes, name *uint16, flags uint32, desiredAccess uint32) (handle Handle, err error) = kernel32.CreateMutexExW +//sys OpenMutex(desiredAccess uint32, inheritHandle bool, name *uint16) (handle Handle, err error) = kernel32.OpenMutexW +//sys ReleaseMutex(mutex Handle) (err error) = kernel32.ReleaseMutex //sys SleepEx(milliseconds uint32, alertable bool) (ret uint32) = kernel32.SleepEx //sys CreateJobObject(jobAttr *SecurityAttributes, name *uint16) (handle Handle, err error) = kernel32.CreateJobObjectW //sys AssignProcessToJobObject(job Handle, process Handle) (err error) = kernel32.AssignProcessToJobObject @@ -261,6 +276,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetPriorityClass(process Handle, priorityClass uint32) (err error) = kernel32.SetPriorityClass //sys GetPriorityClass(process Handle) (ret uint32, err error) = kernel32.GetPriorityClass //sys SetInformationJobObject(job Handle, JobObjectInformationClass uint32, JobObjectInformation uintptr, JobObjectInformationLength uint32) (ret int, err error) +//sys GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) +//sys GetProcessId(process Handle) (id uint32, err error) +//sys OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (handle Handle, err error) +//sys SetProcessPriorityBoost(process Handle, disable bool) (err error) = kernel32.SetProcessPriorityBoost // Volume Management Functions //sys DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) = DefineDosDeviceW @@ -271,6 +290,7 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys FindNextVolumeMountPoint(findVolumeMountPoint Handle, volumeMountPoint *uint16, bufferLength uint32) (err error) = FindNextVolumeMountPointW //sys FindVolumeClose(findVolume Handle) (err error) //sys FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error) +//sys GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) = GetDiskFreeSpaceExW //sys GetDriveType(rootPathName *uint16) (driveType uint32) = GetDriveTypeW //sys GetLogicalDrives() (drivesBitMask uint32, err error) [failretval==0] //sys GetLogicalDriveStrings(bufferLength uint32, buffer *uint16) (n uint32, err error) [failretval==0] = GetLogicalDriveStringsW @@ -283,9 +303,54 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetVolumeLabel(rootPathName *uint16, volumeName *uint16) (err error) = SetVolumeLabelW //sys SetVolumeMountPoint(volumeMountPoint *uint16, volumeName *uint16) (err error) = SetVolumeMountPointW //sys MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret int32, err error) [failretval==0] = user32.MessageBoxW +//sys ExitWindowsEx(flags uint32, reason uint32) (err error) = user32.ExitWindowsEx +//sys InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint32, forceAppsClosed bool, rebootAfterShutdown bool, reason uint32) (err error) = advapi32.InitiateSystemShutdownExW +//sys SetProcessShutdownParameters(level uint32, flags uint32) (err error) = kernel32.SetProcessShutdownParameters +//sys GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) = kernel32.GetProcessShutdownParameters +//sys clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) = ole32.CLSIDFromString +//sys stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) = ole32.StringFromGUID2 +//sys coCreateGuid(pguid *GUID) (ret error) = ole32.CoCreateGuid +//sys CoTaskMemFree(address unsafe.Pointer) = ole32.CoTaskMemFree +//sys rtlGetVersion(info *OsVersionInfoEx) (ret error) = ntdll.RtlGetVersion +//sys rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) = ntdll.RtlGetNtVersionNumbers +//sys getProcessPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetProcessPreferredUILanguages +//sys getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetThreadPreferredUILanguages +//sys getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetUserPreferredUILanguages +//sys getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) = kernel32.GetSystemPreferredUILanguages + +// Process Status API (PSAPI) +//sys EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) = psapi.EnumProcesses // syscall interface implementation for other packages +// GetCurrentProcess returns the handle for the current process. +// It is a pseudo handle that does not need to be closed. +// The returned error is always nil. +// +// Deprecated: use CurrentProcess for the same Handle without the nil +// error. +func GetCurrentProcess() (Handle, error) { + return CurrentProcess(), nil +} + +// CurrentProcess returns the handle for the current process. +// It is a pseudo handle that does not need to be closed. +func CurrentProcess() Handle { return Handle(^uintptr(1 - 1)) } + +// GetCurrentThread returns the handle for the current thread. +// It is a pseudo handle that does not need to be closed. +// The returned error is always nil. +// +// Deprecated: use CurrentThread for the same Handle without the nil +// error. +func GetCurrentThread() (Handle, error) { + return CurrentThread(), nil +} + +// CurrentThread returns the handle for the current thread. +// It is a pseudo handle that does not need to be closed. +func CurrentThread() Handle { return Handle(^uintptr(2 - 1)) } + // GetProcAddressByOrdinal retrieves the address of the exported // function from module by ordinal. func GetProcAddressByOrdinal(module Handle, ordinal uintptr) (proc uintptr, err error) { @@ -352,7 +417,11 @@ func Open(path string, mode int, perm uint32) (fd Handle, err error) { default: createmode = OPEN_EXISTING } - h, e := CreateFile(pathp, access, sharemode, sa, createmode, FILE_ATTRIBUTE_NORMAL, 0) + var attrs uint32 = FILE_ATTRIBUTE_NORMAL + if perm&S_IWRITE == 0 { + attrs = FILE_ATTRIBUTE_READONLY + } + h, e := CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) return h, e } @@ -497,6 +566,10 @@ func ComputerName() (name string, err error) { return string(utf16.Decode(b[0:n])), nil } +func DurationSinceBoot() time.Duration { + return time.Duration(getTickCount64()) * time.Millisecond +} + func Ftruncate(fd Handle, length int64) (err error) { curoffset, e := Seek(fd, 0, 1) if e != nil { @@ -628,6 +701,8 @@ const socket_error = uintptr(^uint32(0)) //sys WSACleanup() (err error) [failretval==socket_error] = ws2_32.WSACleanup //sys WSAIoctl(s Handle, iocc uint32, inbuf *byte, cbif uint32, outbuf *byte, cbob uint32, cbbr *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSAIoctl //sys socket(af int32, typ int32, protocol int32) (handle Handle, err error) [failretval==InvalidHandle] = ws2_32.socket +//sys sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) (err error) [failretval==socket_error] = ws2_32.sendto +//sys recvfrom(s Handle, buf []byte, flags int32, from *RawSockaddrAny, fromlen *int32) (n int32, err error) [failretval==-1] = ws2_32.recvfrom //sys Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) [failretval==socket_error] = ws2_32.setsockopt //sys Getsockopt(s Handle, level int32, optname int32, optval *byte, optlen *int32) (err error) [failretval==socket_error] = ws2_32.getsockopt //sys bind(s Handle, name unsafe.Pointer, namelen int32) (err error) [failretval==socket_error] = ws2_32.bind @@ -795,7 +870,7 @@ func (rsa *RawSockaddrAny) Sockaddr() (Sockaddr, error) { for n < len(pp.Path) && pp.Path[n] != 0 { n++ } - bytes := (*[10000]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] + bytes := (*[len(pp.Path)]byte)(unsafe.Pointer(&pp.Path[0]))[0:n] sa.Name = string(bytes) return sa, nil @@ -1056,10 +1131,27 @@ func NsecToTimespec(nsec int64) (ts Timespec) { // TODO(brainman): fix all needed for net func Accept(fd Handle) (nfd Handle, sa Sockaddr, err error) { return 0, nil, syscall.EWINDOWS } + func Recvfrom(fd Handle, p []byte, flags int) (n int, from Sockaddr, err error) { - return 0, nil, syscall.EWINDOWS + var rsa RawSockaddrAny + l := int32(unsafe.Sizeof(rsa)) + n32, err := recvfrom(fd, p, int32(flags), &rsa, &l) + n = int(n32) + if err != nil { + return + } + from, err = rsa.Sockaddr() + return } -func Sendto(fd Handle, p []byte, flags int, to Sockaddr) (err error) { return syscall.EWINDOWS } + +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 SetsockoptTimeval(fd Handle, level, opt int, tv *Timeval) (err error) { return syscall.EWINDOWS } // The Linger struct is wrong but we only noticed after Go 1. @@ -1106,7 +1198,7 @@ func SetsockoptIPv6Mreq(fd Handle, level, opt int, mreq *IPv6Mreq) (err error) { return syscall.EWINDOWS } -func Getpid() (pid int) { return int(getCurrentProcessId()) } +func Getpid() (pid int) { return int(GetCurrentProcessId()) } func FindFirstFile(name *uint16, data *Win32finddata) (handle Handle, err error) { // NOTE(rsc): The Win32finddata struct is wrong for the system call: @@ -1234,3 +1326,129 @@ func Readlink(path string, buf []byte) (n int, err error) { return n, nil } + +// GUIDFromString parses a string in the form of +// "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" into a GUID. +func GUIDFromString(str string) (GUID, error) { + guid := GUID{} + str16, err := syscall.UTF16PtrFromString(str) + if err != nil { + return guid, err + } + err = clsidFromString(str16, &guid) + if err != nil { + return guid, err + } + return guid, nil +} + +// GenerateGUID creates a new random GUID. +func GenerateGUID() (GUID, error) { + guid := GUID{} + err := coCreateGuid(&guid) + if err != nil { + return guid, err + } + return guid, nil +} + +// String returns the canonical string form of the GUID, +// in the form of "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}". +func (guid GUID) String() string { + var str [100]uint16 + chars := stringFromGUID2(&guid, &str[0], int32(len(str))) + if chars <= 1 { + return "" + } + return string(utf16.Decode(str[:chars-1])) +} + +// KnownFolderPath returns a well-known folder path for the current user, specified by one of +// the FOLDERID_ constants, and chosen and optionally created based on a KF_ flag. +func KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, error) { + return Token(0).KnownFolderPath(folderID, flags) +} + +// KnownFolderPath returns a well-known folder path for the user token, specified by one of +// the FOLDERID_ constants, and chosen and optionally created based on a KF_ flag. +func (t Token) KnownFolderPath(folderID *KNOWNFOLDERID, flags uint32) (string, error) { + var p *uint16 + err := shGetKnownFolderPath(folderID, flags, t, &p) + if err != nil { + return "", err + } + defer CoTaskMemFree(unsafe.Pointer(p)) + return UTF16ToString((*[(1 << 30) - 1]uint16)(unsafe.Pointer(p))[:]), nil +} + +// RtlGetVersion returns the version of the underlying operating system, ignoring +// manifest semantics but is affected by the application compatibility layer. +func RtlGetVersion() *OsVersionInfoEx { + info := &OsVersionInfoEx{} + info.osVersionInfoSize = uint32(unsafe.Sizeof(*info)) + // According to documentation, this function always succeeds. + // The function doesn't even check the validity of the + // osVersionInfoSize member. Disassembling ntdll.dll indicates + // that the documentation is indeed correct about that. + _ = rtlGetVersion(info) + return info +} + +// RtlGetNtVersionNumbers returns the version of the underlying operating system, +// ignoring manifest semantics and the application compatibility layer. +func RtlGetNtVersionNumbers() (majorVersion, minorVersion, buildNumber uint32) { + rtlGetNtVersionNumbers(&majorVersion, &minorVersion, &buildNumber) + buildNumber &= 0xffff + return +} + +// GetProcessPreferredUILanguages retrieves the process preferred UI languages. +func GetProcessPreferredUILanguages(flags uint32) ([]string, error) { + return getUILanguages(flags, getProcessPreferredUILanguages) +} + +// GetThreadPreferredUILanguages retrieves the thread preferred UI languages for the current thread. +func GetThreadPreferredUILanguages(flags uint32) ([]string, error) { + return getUILanguages(flags, getThreadPreferredUILanguages) +} + +// GetUserPreferredUILanguages retrieves information about the user preferred UI languages. +func GetUserPreferredUILanguages(flags uint32) ([]string, error) { + return getUILanguages(flags, getUserPreferredUILanguages) +} + +// GetSystemPreferredUILanguages retrieves the system preferred UI languages. +func GetSystemPreferredUILanguages(flags uint32) ([]string, error) { + return getUILanguages(flags, getSystemPreferredUILanguages) +} + +func getUILanguages(flags uint32, f func(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) error) ([]string, error) { + size := uint32(128) + for { + var numLanguages uint32 + buf := make([]uint16, size) + err := f(flags, &numLanguages, &buf[0], &size) + if err == ERROR_INSUFFICIENT_BUFFER { + continue + } + if err != nil { + return nil, err + } + buf = buf[:size] + if numLanguages == 0 || len(buf) == 0 { // GetProcessPreferredUILanguages may return numLanguages==0 with "\0\0" + return []string{}, nil + } + if buf[len(buf)-1] == 0 { + buf = buf[:len(buf)-1] // remove terminating null + } + languages := make([]string, 0, numLanguages) + from := 0 + for i, c := range buf { + if c == 0 { + languages = append(languages, string(utf16.Decode(buf[from:i]))) + from = i + 1 + } + } + return languages, nil + } +} diff --git a/go/vendor/golang.org/x/sys/windows/types_windows.go b/go/vendor/golang.org/x/sys/windows/types_windows.go index 99b85f6dd..aefb7fc39 100644 --- a/go/vendor/golang.org/x/sys/windows/types_windows.go +++ b/go/vendor/golang.org/x/sys/windows/types_windows.go @@ -62,11 +62,6 @@ var signals = [...]string{ } const ( - GENERIC_READ = 0x80000000 - GENERIC_WRITE = 0x40000000 - GENERIC_EXECUTE = 0x20000000 - GENERIC_ALL = 0x10000000 - FILE_LIST_DIRECTORY = 0x00000001 FILE_APPEND_DATA = 0x00000004 FILE_WRITE_ATTRIBUTES = 0x00000100 @@ -158,17 +153,43 @@ const ( WAIT_OBJECT_0 = 0x00000000 WAIT_FAILED = 0xFFFFFFFF - PROCESS_TERMINATE = 1 - PROCESS_QUERY_INFORMATION = 0x00000400 - SYNCHRONIZE = 0x00100000 + // Access rights for process. + PROCESS_CREATE_PROCESS = 0x0080 + PROCESS_CREATE_THREAD = 0x0002 + PROCESS_DUP_HANDLE = 0x0040 + PROCESS_QUERY_INFORMATION = 0x0400 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + PROCESS_SET_INFORMATION = 0x0200 + PROCESS_SET_QUOTA = 0x0100 + PROCESS_SUSPEND_RESUME = 0x0800 + PROCESS_TERMINATE = 0x0001 + PROCESS_VM_OPERATION = 0x0008 + PROCESS_VM_READ = 0x0010 + PROCESS_VM_WRITE = 0x0020 + + // Access rights for thread. + THREAD_DIRECT_IMPERSONATION = 0x0200 + THREAD_GET_CONTEXT = 0x0008 + THREAD_IMPERSONATE = 0x0100 + THREAD_QUERY_INFORMATION = 0x0040 + THREAD_QUERY_LIMITED_INFORMATION = 0x0800 + THREAD_SET_CONTEXT = 0x0010 + THREAD_SET_INFORMATION = 0x0020 + THREAD_SET_LIMITED_INFORMATION = 0x0400 + THREAD_SET_THREAD_TOKEN = 0x0080 + THREAD_SUSPEND_RESUME = 0x0002 + THREAD_TERMINATE = 0x0001 FILE_MAP_COPY = 0x01 FILE_MAP_WRITE = 0x02 FILE_MAP_READ = 0x04 FILE_MAP_EXECUTE = 0x20 - CTRL_C_EVENT = 0 - CTRL_BREAK_EVENT = 1 + CTRL_C_EVENT = 0 + CTRL_BREAK_EVENT = 1 + CTRL_CLOSE_EVENT = 2 + CTRL_LOGOFF_EVENT = 5 + CTRL_SHUTDOWN_EVENT = 6 // Windows reserves errors >= 1<<29 for application use. APPLICATION_ERROR = 1 << 29 @@ -450,12 +471,6 @@ func NsecToTimeval(nsec int64) (tv Timeval) { return } -type SecurityAttributes struct { - Length uint32 - SecurityDescriptor uintptr - InheritHandle uint32 -} - type Overlapped struct { Internal uintptr InternalHigh uintptr @@ -629,6 +644,16 @@ type ProcessEntry32 struct { ExeFile [MAX_PATH]uint16 } +type ThreadEntry32 struct { + Size uint32 + Usage uint32 + ThreadID uint32 + OwnerProcessID uint32 + BasePri int32 + DeltaPri int32 + Flags uint32 +} + type Systemtime struct { Year uint16 Month uint16 @@ -665,6 +690,7 @@ const ( SOCK_SEQPACKET = 5 IPPROTO_IP = 0 + IPPROTO_ICMP = 1 IPPROTO_IPV6 = 0x29 IPPROTO_TCP = 6 IPPROTO_UDP = 17 @@ -676,6 +702,7 @@ const ( SO_BROADCAST = 32 SO_LINGER = 128 SO_RCVBUF = 0x1002 + SO_RCVTIMEO = 0x1006 SO_SNDBUF = 0x1001 SO_UPDATE_ACCEPT_CONTEXT = 0x700b SO_UPDATE_CONNECT_CONTEXT = 0x7010 @@ -1147,6 +1174,28 @@ const ( REG_QWORD = REG_QWORD_LITTLE_ENDIAN ) +const ( + EVENT_MODIFY_STATE = 0x0002 + EVENT_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3 + + MUTANT_QUERY_STATE = 0x0001 + MUTANT_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | MUTANT_QUERY_STATE + + SEMAPHORE_MODIFY_STATE = 0x0002 + SEMAPHORE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3 + + TIMER_QUERY_STATE = 0x0001 + TIMER_MODIFY_STATE = 0x0002 + TIMER_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE | TIMER_MODIFY_STATE + + MUTEX_MODIFY_STATE = MUTANT_QUERY_STATE + MUTEX_ALL_ACCESS = MUTANT_ALL_ACCESS + + CREATE_EVENT_MANUAL_RESET = 0x1 + CREATE_EVENT_INITIAL_SET = 0x2 + CREATE_MUTEX_INITIAL_OWNER = 0x1 +) + type AddrinfoW struct { Flags int32 Family int32 @@ -1590,3 +1639,141 @@ const ( JobObjectNotificationLimitInformation2 = 34 JobObjectSecurityLimitInformation = 5 ) + +const ( + KF_FLAG_DEFAULT = 0x00000000 + KF_FLAG_FORCE_APP_DATA_REDIRECTION = 0x00080000 + KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET = 0x00040000 + KF_FLAG_FORCE_PACKAGE_REDIRECTION = 0x00020000 + KF_FLAG_NO_PACKAGE_REDIRECTION = 0x00010000 + KF_FLAG_FORCE_APPCONTAINER_REDIRECTION = 0x00020000 + KF_FLAG_NO_APPCONTAINER_REDIRECTION = 0x00010000 + KF_FLAG_CREATE = 0x00008000 + KF_FLAG_DONT_VERIFY = 0x00004000 + KF_FLAG_DONT_UNEXPAND = 0x00002000 + KF_FLAG_NO_ALIAS = 0x00001000 + KF_FLAG_INIT = 0x00000800 + KF_FLAG_DEFAULT_PATH = 0x00000400 + KF_FLAG_NOT_PARENT_RELATIVE = 0x00000200 + KF_FLAG_SIMPLE_IDLIST = 0x00000100 + KF_FLAG_ALIAS_ONLY = 0x80000000 +) + +type OsVersionInfoEx struct { + osVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformId uint32 + CsdVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + _ byte +} + +const ( + EWX_LOGOFF = 0x00000000 + EWX_SHUTDOWN = 0x00000001 + EWX_REBOOT = 0x00000002 + EWX_FORCE = 0x00000004 + EWX_POWEROFF = 0x00000008 + EWX_FORCEIFHUNG = 0x00000010 + EWX_QUICKRESOLVE = 0x00000020 + EWX_RESTARTAPPS = 0x00000040 + EWX_HYBRID_SHUTDOWN = 0x00400000 + EWX_BOOTOPTIONS = 0x01000000 + + SHTDN_REASON_FLAG_COMMENT_REQUIRED = 0x01000000 + SHTDN_REASON_FLAG_DIRTY_PROBLEM_ID_REQUIRED = 0x02000000 + SHTDN_REASON_FLAG_CLEAN_UI = 0x04000000 + SHTDN_REASON_FLAG_DIRTY_UI = 0x08000000 + SHTDN_REASON_FLAG_USER_DEFINED = 0x40000000 + SHTDN_REASON_FLAG_PLANNED = 0x80000000 + SHTDN_REASON_MAJOR_OTHER = 0x00000000 + SHTDN_REASON_MAJOR_NONE = 0x00000000 + SHTDN_REASON_MAJOR_HARDWARE = 0x00010000 + SHTDN_REASON_MAJOR_OPERATINGSYSTEM = 0x00020000 + SHTDN_REASON_MAJOR_SOFTWARE = 0x00030000 + SHTDN_REASON_MAJOR_APPLICATION = 0x00040000 + SHTDN_REASON_MAJOR_SYSTEM = 0x00050000 + SHTDN_REASON_MAJOR_POWER = 0x00060000 + SHTDN_REASON_MAJOR_LEGACY_API = 0x00070000 + SHTDN_REASON_MINOR_OTHER = 0x00000000 + SHTDN_REASON_MINOR_NONE = 0x000000ff + SHTDN_REASON_MINOR_MAINTENANCE = 0x00000001 + SHTDN_REASON_MINOR_INSTALLATION = 0x00000002 + SHTDN_REASON_MINOR_UPGRADE = 0x00000003 + SHTDN_REASON_MINOR_RECONFIG = 0x00000004 + SHTDN_REASON_MINOR_HUNG = 0x00000005 + SHTDN_REASON_MINOR_UNSTABLE = 0x00000006 + SHTDN_REASON_MINOR_DISK = 0x00000007 + SHTDN_REASON_MINOR_PROCESSOR = 0x00000008 + SHTDN_REASON_MINOR_NETWORKCARD = 0x00000009 + SHTDN_REASON_MINOR_POWER_SUPPLY = 0x0000000a + SHTDN_REASON_MINOR_CORDUNPLUGGED = 0x0000000b + SHTDN_REASON_MINOR_ENVIRONMENT = 0x0000000c + SHTDN_REASON_MINOR_HARDWARE_DRIVER = 0x0000000d + SHTDN_REASON_MINOR_OTHERDRIVER = 0x0000000e + SHTDN_REASON_MINOR_BLUESCREEN = 0x0000000F + SHTDN_REASON_MINOR_SERVICEPACK = 0x00000010 + SHTDN_REASON_MINOR_HOTFIX = 0x00000011 + SHTDN_REASON_MINOR_SECURITYFIX = 0x00000012 + SHTDN_REASON_MINOR_SECURITY = 0x00000013 + SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY = 0x00000014 + SHTDN_REASON_MINOR_WMI = 0x00000015 + SHTDN_REASON_MINOR_SERVICEPACK_UNINSTALL = 0x00000016 + SHTDN_REASON_MINOR_HOTFIX_UNINSTALL = 0x00000017 + SHTDN_REASON_MINOR_SECURITYFIX_UNINSTALL = 0x00000018 + SHTDN_REASON_MINOR_MMC = 0x00000019 + SHTDN_REASON_MINOR_SYSTEMRESTORE = 0x0000001a + SHTDN_REASON_MINOR_TERMSRV = 0x00000020 + SHTDN_REASON_MINOR_DC_PROMOTION = 0x00000021 + SHTDN_REASON_MINOR_DC_DEMOTION = 0x00000022 + SHTDN_REASON_UNKNOWN = SHTDN_REASON_MINOR_NONE + SHTDN_REASON_LEGACY_API = SHTDN_REASON_MAJOR_LEGACY_API | SHTDN_REASON_FLAG_PLANNED + SHTDN_REASON_VALID_BIT_MASK = 0xc0ffffff + + SHUTDOWN_NORETRY = 0x1 +) + +// Flags used for GetModuleHandleEx +const ( + GET_MODULE_HANDLE_EX_FLAG_PIN = 1 + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2 + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 4 +) + +// MUI function flag values +const ( + MUI_LANGUAGE_ID = 0x4 + MUI_LANGUAGE_NAME = 0x8 + MUI_MERGE_SYSTEM_FALLBACK = 0x10 + MUI_MERGE_USER_FALLBACK = 0x20 + MUI_UI_FALLBACK = MUI_MERGE_SYSTEM_FALLBACK | MUI_MERGE_USER_FALLBACK + MUI_THREAD_LANGUAGES = 0x40 + MUI_CONSOLE_FILTER = 0x100 + MUI_COMPLEX_SCRIPT_FILTER = 0x200 + MUI_RESET_FILTERS = 0x001 + MUI_USER_PREFERRED_UI_LANGUAGES = 0x10 + MUI_USE_INSTALLED_LANGUAGES = 0x20 + MUI_USE_SEARCH_ALL_LANGUAGES = 0x40 + MUI_LANG_NEUTRAL_PE_FILE = 0x100 + MUI_NON_LANG_NEUTRAL_FILE = 0x200 + MUI_MACHINE_LANGUAGE_SETTINGS = 0x400 + MUI_FILETYPE_NOT_LANGUAGE_NEUTRAL = 0x001 + MUI_FILETYPE_LANGUAGE_NEUTRAL_MAIN = 0x002 + MUI_FILETYPE_LANGUAGE_NEUTRAL_MUI = 0x004 + MUI_QUERY_TYPE = 0x001 + MUI_QUERY_CHECKSUM = 0x002 + MUI_QUERY_LANGUAGE_NAME = 0x004 + MUI_QUERY_RESOURCE_TYPES = 0x008 + MUI_FILEINFO_VERSION = 0x001 + + MUI_FULL_LANGUAGE = 0x01 + MUI_PARTIAL_LANGUAGE = 0x02 + MUI_LIP_LANGUAGE = 0x04 + MUI_LANGUAGE_INSTALLED = 0x20 + MUI_LANGUAGE_LICENSED = 0x40 +) diff --git a/go/vendor/golang.org/x/sys/windows/zerrors_windows.go b/go/vendor/golang.org/x/sys/windows/zerrors_windows.go index 2b4cea5b9..f02120035 100644 --- a/go/vendor/golang.org/x/sys/windows/zerrors_windows.go +++ b/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 diff --git a/go/vendor/golang.org/x/sys/windows/zknownfolderids_windows.go b/go/vendor/golang.org/x/sys/windows/zknownfolderids_windows.go new file mode 100644 index 000000000..6048ac679 --- /dev/null +++ b/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}} +) diff --git a/go/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/go/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 96377a8e2..2aa4fa642 100644 --- a/go/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/go/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -42,6 +42,9 @@ var ( modmswsock = NewLazySystemDLL("mswsock.dll") modcrypt32 = NewLazySystemDLL("crypt32.dll") moduser32 = NewLazySystemDLL("user32.dll") + modole32 = NewLazySystemDLL("ole32.dll") + modntdll = NewLazySystemDLL("ntdll.dll") + modpsapi = NewLazySystemDLL("psapi.dll") modws2_32 = NewLazySystemDLL("ws2_32.dll") moddnsapi = NewLazySystemDLL("dnsapi.dll") modiphlpapi = NewLazySystemDLL("iphlpapi.dll") @@ -49,248 +52,308 @@ var ( modnetapi32 = NewLazySystemDLL("netapi32.dll") modwtsapi32 = NewLazySystemDLL("wtsapi32.dll") - procRegisterEventSourceW = modadvapi32.NewProc("RegisterEventSourceW") - procDeregisterEventSource = modadvapi32.NewProc("DeregisterEventSource") - procReportEventW = modadvapi32.NewProc("ReportEventW") - procOpenSCManagerW = modadvapi32.NewProc("OpenSCManagerW") - procCloseServiceHandle = modadvapi32.NewProc("CloseServiceHandle") - procCreateServiceW = modadvapi32.NewProc("CreateServiceW") - procOpenServiceW = modadvapi32.NewProc("OpenServiceW") - procDeleteService = modadvapi32.NewProc("DeleteService") - procStartServiceW = modadvapi32.NewProc("StartServiceW") - procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") - procControlService = modadvapi32.NewProc("ControlService") - procStartServiceCtrlDispatcherW = modadvapi32.NewProc("StartServiceCtrlDispatcherW") - procSetServiceStatus = modadvapi32.NewProc("SetServiceStatus") - procChangeServiceConfigW = modadvapi32.NewProc("ChangeServiceConfigW") - procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") - procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W") - procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W") - procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") - procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx") - procNotifyServiceStatusChangeW = modadvapi32.NewProc("NotifyServiceStatusChangeW") - procGetLastError = modkernel32.NewProc("GetLastError") - procLoadLibraryW = modkernel32.NewProc("LoadLibraryW") - procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW") - procFreeLibrary = modkernel32.NewProc("FreeLibrary") - procGetProcAddress = modkernel32.NewProc("GetProcAddress") - procGetVersion = modkernel32.NewProc("GetVersion") - procFormatMessageW = modkernel32.NewProc("FormatMessageW") - procExitProcess = modkernel32.NewProc("ExitProcess") - procIsWow64Process = modkernel32.NewProc("IsWow64Process") - procCreateFileW = modkernel32.NewProc("CreateFileW") - procReadFile = modkernel32.NewProc("ReadFile") - procWriteFile = modkernel32.NewProc("WriteFile") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procSetFilePointer = modkernel32.NewProc("SetFilePointer") - procCloseHandle = modkernel32.NewProc("CloseHandle") - procGetStdHandle = modkernel32.NewProc("GetStdHandle") - procSetStdHandle = modkernel32.NewProc("SetStdHandle") - procFindFirstFileW = modkernel32.NewProc("FindFirstFileW") - procFindNextFileW = modkernel32.NewProc("FindNextFileW") - procFindClose = modkernel32.NewProc("FindClose") - procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle") - procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") - procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW") - procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW") - procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW") - procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") - procDeleteFileW = modkernel32.NewProc("DeleteFileW") - procMoveFileW = modkernel32.NewProc("MoveFileW") - procMoveFileExW = modkernel32.NewProc("MoveFileExW") - procGetComputerNameW = modkernel32.NewProc("GetComputerNameW") - procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") - procSetEndOfFile = modkernel32.NewProc("SetEndOfFile") - procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime") - procGetSystemTimePreciseAsFileTime = modkernel32.NewProc("GetSystemTimePreciseAsFileTime") - procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation") - procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") - procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") - procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") - procCancelIo = modkernel32.NewProc("CancelIo") - procCancelIoEx = modkernel32.NewProc("CancelIoEx") - procCreateProcessW = modkernel32.NewProc("CreateProcessW") - procOpenProcess = modkernel32.NewProc("OpenProcess") - procShellExecuteW = modshell32.NewProc("ShellExecuteW") - procTerminateProcess = modkernel32.NewProc("TerminateProcess") - procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess") - procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW") - procGetCurrentProcess = modkernel32.NewProc("GetCurrentProcess") - procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") - procGetProcessTimes = modkernel32.NewProc("GetProcessTimes") - procDuplicateHandle = modkernel32.NewProc("DuplicateHandle") - procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject") - procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects") - procGetTempPathW = modkernel32.NewProc("GetTempPathW") - procCreatePipe = modkernel32.NewProc("CreatePipe") - procGetFileType = modkernel32.NewProc("GetFileType") - procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW") - procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext") - procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom") - procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW") - procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW") - procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW") - procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW") - procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock") - procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock") - procSetFileTime = modkernel32.NewProc("SetFileTime") - procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW") - procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW") - procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW") - procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") - procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW") - procLocalFree = modkernel32.NewProc("LocalFree") - procSetHandleInformation = modkernel32.NewProc("SetHandleInformation") - procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") - procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") - procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW") - procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW") - procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW") - procMapViewOfFile = modkernel32.NewProc("MapViewOfFile") - procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile") - procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") - procVirtualLock = modkernel32.NewProc("VirtualLock") - procVirtualUnlock = modkernel32.NewProc("VirtualUnlock") - procVirtualAlloc = modkernel32.NewProc("VirtualAlloc") - procVirtualFree = modkernel32.NewProc("VirtualFree") - procVirtualProtect = modkernel32.NewProc("VirtualProtect") - procTransmitFile = modmswsock.NewProc("TransmitFile") - procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW") - procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW") - procCertOpenStore = modcrypt32.NewProc("CertOpenStore") - procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore") - procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore") - procCertCloseStore = modcrypt32.NewProc("CertCloseStore") - procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain") - procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain") - procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext") - procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext") - procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy") - procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW") - procRegCloseKey = modadvapi32.NewProc("RegCloseKey") - procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW") - procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW") - procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW") - procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") - procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") - procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") - procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo") - procWriteConsoleW = modkernel32.NewProc("WriteConsoleW") - procReadConsoleW = modkernel32.NewProc("ReadConsoleW") - procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") - procProcess32FirstW = modkernel32.NewProc("Process32FirstW") - procProcess32NextW = modkernel32.NewProc("Process32NextW") - procDeviceIoControl = modkernel32.NewProc("DeviceIoControl") - procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW") - procCreateHardLinkW = modkernel32.NewProc("CreateHardLinkW") - procGetCurrentThreadId = modkernel32.NewProc("GetCurrentThreadId") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procCreateEventExW = modkernel32.NewProc("CreateEventExW") - procOpenEventW = modkernel32.NewProc("OpenEventW") - procSetEvent = modkernel32.NewProc("SetEvent") - procResetEvent = modkernel32.NewProc("ResetEvent") - procPulseEvent = modkernel32.NewProc("PulseEvent") - procSleepEx = modkernel32.NewProc("SleepEx") - procCreateJobObjectW = modkernel32.NewProc("CreateJobObjectW") - procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") - procTerminateJobObject = modkernel32.NewProc("TerminateJobObject") - procSetErrorMode = modkernel32.NewProc("SetErrorMode") - procResumeThread = modkernel32.NewProc("ResumeThread") - procSetPriorityClass = modkernel32.NewProc("SetPriorityClass") - procGetPriorityClass = modkernel32.NewProc("GetPriorityClass") - procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject") - procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW") - procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW") - procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW") - procFindFirstVolumeMountPointW = modkernel32.NewProc("FindFirstVolumeMountPointW") - procFindNextVolumeW = modkernel32.NewProc("FindNextVolumeW") - procFindNextVolumeMountPointW = modkernel32.NewProc("FindNextVolumeMountPointW") - procFindVolumeClose = modkernel32.NewProc("FindVolumeClose") - procFindVolumeMountPointClose = modkernel32.NewProc("FindVolumeMountPointClose") - procGetDriveTypeW = modkernel32.NewProc("GetDriveTypeW") - procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives") - procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") - procGetVolumeInformationW = modkernel32.NewProc("GetVolumeInformationW") - procGetVolumeInformationByHandleW = modkernel32.NewProc("GetVolumeInformationByHandleW") - procGetVolumeNameForVolumeMountPointW = modkernel32.NewProc("GetVolumeNameForVolumeMountPointW") - procGetVolumePathNameW = modkernel32.NewProc("GetVolumePathNameW") - procGetVolumePathNamesForVolumeNameW = modkernel32.NewProc("GetVolumePathNamesForVolumeNameW") - procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW") - procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW") - procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW") - procMessageBoxW = moduser32.NewProc("MessageBoxW") - procWSAStartup = modws2_32.NewProc("WSAStartup") - procWSACleanup = modws2_32.NewProc("WSACleanup") - procWSAIoctl = modws2_32.NewProc("WSAIoctl") - procsocket = modws2_32.NewProc("socket") - procsetsockopt = modws2_32.NewProc("setsockopt") - procgetsockopt = modws2_32.NewProc("getsockopt") - procbind = modws2_32.NewProc("bind") - procconnect = modws2_32.NewProc("connect") - procgetsockname = modws2_32.NewProc("getsockname") - procgetpeername = modws2_32.NewProc("getpeername") - proclisten = modws2_32.NewProc("listen") - procshutdown = modws2_32.NewProc("shutdown") - procclosesocket = modws2_32.NewProc("closesocket") - procAcceptEx = modmswsock.NewProc("AcceptEx") - procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs") - procWSARecv = modws2_32.NewProc("WSARecv") - procWSASend = modws2_32.NewProc("WSASend") - procWSARecvFrom = modws2_32.NewProc("WSARecvFrom") - procWSASendTo = modws2_32.NewProc("WSASendTo") - procgethostbyname = modws2_32.NewProc("gethostbyname") - procgetservbyname = modws2_32.NewProc("getservbyname") - procntohs = modws2_32.NewProc("ntohs") - procgetprotobyname = modws2_32.NewProc("getprotobyname") - procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") - procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") - procDnsNameCompare_W = moddnsapi.NewProc("DnsNameCompare_W") - procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") - procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") - procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") - procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") - procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") - procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW") - procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") - procGetACP = modkernel32.NewProc("GetACP") - procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") - procTranslateNameW = modsecur32.NewProc("TranslateNameW") - procGetUserNameExW = modsecur32.NewProc("GetUserNameExW") - procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") - procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation") - procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree") - procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") - procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") - procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") - procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") - procGetLengthSid = modadvapi32.NewProc("GetLengthSid") - procCopySid = modadvapi32.NewProc("CopySid") - procAllocateAndInitializeSid = modadvapi32.NewProc("AllocateAndInitializeSid") - procCreateWellKnownSid = modadvapi32.NewProc("CreateWellKnownSid") - procIsWellKnownSid = modadvapi32.NewProc("IsWellKnownSid") - procFreeSid = modadvapi32.NewProc("FreeSid") - procEqualSid = modadvapi32.NewProc("EqualSid") - procGetSidIdentifierAuthority = modadvapi32.NewProc("GetSidIdentifierAuthority") - procGetSidSubAuthorityCount = modadvapi32.NewProc("GetSidSubAuthorityCount") - procGetSidSubAuthority = modadvapi32.NewProc("GetSidSubAuthority") - procIsValidSid = modadvapi32.NewProc("IsValidSid") - procCheckTokenMembership = modadvapi32.NewProc("CheckTokenMembership") - procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") - procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") - procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") - procRevertToSelf = modadvapi32.NewProc("RevertToSelf") - procSetThreadToken = modadvapi32.NewProc("SetThreadToken") - procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") - procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") - procAdjustTokenGroups = modadvapi32.NewProc("AdjustTokenGroups") - procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") - procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") - procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") - procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") - procGetSystemDirectoryW = modkernel32.NewProc("GetSystemDirectoryW") - procWTSQueryUserToken = modwtsapi32.NewProc("WTSQueryUserToken") - procWTSEnumerateSessionsW = modwtsapi32.NewProc("WTSEnumerateSessionsW") - procWTSFreeMemory = modwtsapi32.NewProc("WTSFreeMemory") + procRegisterEventSourceW = modadvapi32.NewProc("RegisterEventSourceW") + procDeregisterEventSource = modadvapi32.NewProc("DeregisterEventSource") + procReportEventW = modadvapi32.NewProc("ReportEventW") + procOpenSCManagerW = modadvapi32.NewProc("OpenSCManagerW") + procCloseServiceHandle = modadvapi32.NewProc("CloseServiceHandle") + procCreateServiceW = modadvapi32.NewProc("CreateServiceW") + procOpenServiceW = modadvapi32.NewProc("OpenServiceW") + procDeleteService = modadvapi32.NewProc("DeleteService") + procStartServiceW = modadvapi32.NewProc("StartServiceW") + procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus") + procQueryServiceLockStatusW = modadvapi32.NewProc("QueryServiceLockStatusW") + procControlService = modadvapi32.NewProc("ControlService") + procStartServiceCtrlDispatcherW = modadvapi32.NewProc("StartServiceCtrlDispatcherW") + procSetServiceStatus = modadvapi32.NewProc("SetServiceStatus") + procChangeServiceConfigW = modadvapi32.NewProc("ChangeServiceConfigW") + procQueryServiceConfigW = modadvapi32.NewProc("QueryServiceConfigW") + procChangeServiceConfig2W = modadvapi32.NewProc("ChangeServiceConfig2W") + procQueryServiceConfig2W = modadvapi32.NewProc("QueryServiceConfig2W") + procEnumServicesStatusExW = modadvapi32.NewProc("EnumServicesStatusExW") + procQueryServiceStatusEx = modadvapi32.NewProc("QueryServiceStatusEx") + procNotifyServiceStatusChangeW = modadvapi32.NewProc("NotifyServiceStatusChangeW") + procGetLastError = modkernel32.NewProc("GetLastError") + procLoadLibraryW = modkernel32.NewProc("LoadLibraryW") + procLoadLibraryExW = modkernel32.NewProc("LoadLibraryExW") + procFreeLibrary = modkernel32.NewProc("FreeLibrary") + procGetProcAddress = modkernel32.NewProc("GetProcAddress") + procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW") + procGetModuleHandleExW = modkernel32.NewProc("GetModuleHandleExW") + procGetVersion = modkernel32.NewProc("GetVersion") + procFormatMessageW = modkernel32.NewProc("FormatMessageW") + procExitProcess = modkernel32.NewProc("ExitProcess") + procIsWow64Process = modkernel32.NewProc("IsWow64Process") + procCreateFileW = modkernel32.NewProc("CreateFileW") + procReadFile = modkernel32.NewProc("ReadFile") + procWriteFile = modkernel32.NewProc("WriteFile") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procSetFilePointer = modkernel32.NewProc("SetFilePointer") + procCloseHandle = modkernel32.NewProc("CloseHandle") + procGetStdHandle = modkernel32.NewProc("GetStdHandle") + procSetStdHandle = modkernel32.NewProc("SetStdHandle") + procFindFirstFileW = modkernel32.NewProc("FindFirstFileW") + procFindNextFileW = modkernel32.NewProc("FindNextFileW") + procFindClose = modkernel32.NewProc("FindClose") + procGetFileInformationByHandle = modkernel32.NewProc("GetFileInformationByHandle") + procGetFileInformationByHandleEx = modkernel32.NewProc("GetFileInformationByHandleEx") + procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW") + procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW") + procCreateDirectoryW = modkernel32.NewProc("CreateDirectoryW") + procRemoveDirectoryW = modkernel32.NewProc("RemoveDirectoryW") + procDeleteFileW = modkernel32.NewProc("DeleteFileW") + procMoveFileW = modkernel32.NewProc("MoveFileW") + procMoveFileExW = modkernel32.NewProc("MoveFileExW") + procLockFileEx = modkernel32.NewProc("LockFileEx") + procUnlockFileEx = modkernel32.NewProc("UnlockFileEx") + procGetComputerNameW = modkernel32.NewProc("GetComputerNameW") + procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") + procSetEndOfFile = modkernel32.NewProc("SetEndOfFile") + procGetSystemTimeAsFileTime = modkernel32.NewProc("GetSystemTimeAsFileTime") + procGetSystemTimePreciseAsFileTime = modkernel32.NewProc("GetSystemTimePreciseAsFileTime") + procGetTimeZoneInformation = modkernel32.NewProc("GetTimeZoneInformation") + procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort") + procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus") + procPostQueuedCompletionStatus = modkernel32.NewProc("PostQueuedCompletionStatus") + procCancelIo = modkernel32.NewProc("CancelIo") + procCancelIoEx = modkernel32.NewProc("CancelIoEx") + procCreateProcessW = modkernel32.NewProc("CreateProcessW") + procOpenProcess = modkernel32.NewProc("OpenProcess") + procShellExecuteW = modshell32.NewProc("ShellExecuteW") + procSHGetKnownFolderPath = modshell32.NewProc("SHGetKnownFolderPath") + procTerminateProcess = modkernel32.NewProc("TerminateProcess") + procGetExitCodeProcess = modkernel32.NewProc("GetExitCodeProcess") + procGetStartupInfoW = modkernel32.NewProc("GetStartupInfoW") + procGetProcessTimes = modkernel32.NewProc("GetProcessTimes") + procDuplicateHandle = modkernel32.NewProc("DuplicateHandle") + procWaitForSingleObject = modkernel32.NewProc("WaitForSingleObject") + procWaitForMultipleObjects = modkernel32.NewProc("WaitForMultipleObjects") + procGetTempPathW = modkernel32.NewProc("GetTempPathW") + procCreatePipe = modkernel32.NewProc("CreatePipe") + procGetFileType = modkernel32.NewProc("GetFileType") + procCryptAcquireContextW = modadvapi32.NewProc("CryptAcquireContextW") + procCryptReleaseContext = modadvapi32.NewProc("CryptReleaseContext") + procCryptGenRandom = modadvapi32.NewProc("CryptGenRandom") + procGetEnvironmentStringsW = modkernel32.NewProc("GetEnvironmentStringsW") + procFreeEnvironmentStringsW = modkernel32.NewProc("FreeEnvironmentStringsW") + procGetEnvironmentVariableW = modkernel32.NewProc("GetEnvironmentVariableW") + procSetEnvironmentVariableW = modkernel32.NewProc("SetEnvironmentVariableW") + procCreateEnvironmentBlock = moduserenv.NewProc("CreateEnvironmentBlock") + procDestroyEnvironmentBlock = moduserenv.NewProc("DestroyEnvironmentBlock") + procGetTickCount64 = modkernel32.NewProc("GetTickCount64") + procSetFileTime = modkernel32.NewProc("SetFileTime") + procGetFileAttributesW = modkernel32.NewProc("GetFileAttributesW") + procSetFileAttributesW = modkernel32.NewProc("SetFileAttributesW") + procGetFileAttributesExW = modkernel32.NewProc("GetFileAttributesExW") + procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") + procCommandLineToArgvW = modshell32.NewProc("CommandLineToArgvW") + procLocalFree = modkernel32.NewProc("LocalFree") + procSetHandleInformation = modkernel32.NewProc("SetHandleInformation") + procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") + procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") + procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW") + procGetShortPathNameW = modkernel32.NewProc("GetShortPathNameW") + procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW") + procMapViewOfFile = modkernel32.NewProc("MapViewOfFile") + procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile") + procFlushViewOfFile = modkernel32.NewProc("FlushViewOfFile") + procVirtualLock = modkernel32.NewProc("VirtualLock") + procVirtualUnlock = modkernel32.NewProc("VirtualUnlock") + procVirtualAlloc = modkernel32.NewProc("VirtualAlloc") + procVirtualFree = modkernel32.NewProc("VirtualFree") + procVirtualProtect = modkernel32.NewProc("VirtualProtect") + procTransmitFile = modmswsock.NewProc("TransmitFile") + procReadDirectoryChangesW = modkernel32.NewProc("ReadDirectoryChangesW") + procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW") + procCertOpenStore = modcrypt32.NewProc("CertOpenStore") + procCertEnumCertificatesInStore = modcrypt32.NewProc("CertEnumCertificatesInStore") + procCertAddCertificateContextToStore = modcrypt32.NewProc("CertAddCertificateContextToStore") + procCertCloseStore = modcrypt32.NewProc("CertCloseStore") + procCertGetCertificateChain = modcrypt32.NewProc("CertGetCertificateChain") + procCertFreeCertificateChain = modcrypt32.NewProc("CertFreeCertificateChain") + procCertCreateCertificateContext = modcrypt32.NewProc("CertCreateCertificateContext") + procCertFreeCertificateContext = modcrypt32.NewProc("CertFreeCertificateContext") + procCertVerifyCertificateChainPolicy = modcrypt32.NewProc("CertVerifyCertificateChainPolicy") + procRegOpenKeyExW = modadvapi32.NewProc("RegOpenKeyExW") + procRegCloseKey = modadvapi32.NewProc("RegCloseKey") + procRegQueryInfoKeyW = modadvapi32.NewProc("RegQueryInfoKeyW") + procRegEnumKeyExW = modadvapi32.NewProc("RegEnumKeyExW") + procRegQueryValueExW = modadvapi32.NewProc("RegQueryValueExW") + procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") + procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") + procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") + procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo") + procWriteConsoleW = modkernel32.NewProc("WriteConsoleW") + procReadConsoleW = modkernel32.NewProc("ReadConsoleW") + procCreateToolhelp32Snapshot = modkernel32.NewProc("CreateToolhelp32Snapshot") + procProcess32FirstW = modkernel32.NewProc("Process32FirstW") + procProcess32NextW = modkernel32.NewProc("Process32NextW") + procThread32First = modkernel32.NewProc("Thread32First") + procThread32Next = modkernel32.NewProc("Thread32Next") + procDeviceIoControl = modkernel32.NewProc("DeviceIoControl") + procCreateSymbolicLinkW = modkernel32.NewProc("CreateSymbolicLinkW") + procCreateHardLinkW = modkernel32.NewProc("CreateHardLinkW") + procGetCurrentThreadId = modkernel32.NewProc("GetCurrentThreadId") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procCreateEventExW = modkernel32.NewProc("CreateEventExW") + procOpenEventW = modkernel32.NewProc("OpenEventW") + procSetEvent = modkernel32.NewProc("SetEvent") + procResetEvent = modkernel32.NewProc("ResetEvent") + procPulseEvent = modkernel32.NewProc("PulseEvent") + procCreateMutexW = modkernel32.NewProc("CreateMutexW") + procCreateMutexExW = modkernel32.NewProc("CreateMutexExW") + procOpenMutexW = modkernel32.NewProc("OpenMutexW") + procReleaseMutex = modkernel32.NewProc("ReleaseMutex") + procSleepEx = modkernel32.NewProc("SleepEx") + procCreateJobObjectW = modkernel32.NewProc("CreateJobObjectW") + procAssignProcessToJobObject = modkernel32.NewProc("AssignProcessToJobObject") + procTerminateJobObject = modkernel32.NewProc("TerminateJobObject") + procSetErrorMode = modkernel32.NewProc("SetErrorMode") + procResumeThread = modkernel32.NewProc("ResumeThread") + procSetPriorityClass = modkernel32.NewProc("SetPriorityClass") + procGetPriorityClass = modkernel32.NewProc("GetPriorityClass") + procSetInformationJobObject = modkernel32.NewProc("SetInformationJobObject") + procGenerateConsoleCtrlEvent = modkernel32.NewProc("GenerateConsoleCtrlEvent") + procGetProcessId = modkernel32.NewProc("GetProcessId") + procOpenThread = modkernel32.NewProc("OpenThread") + procSetProcessPriorityBoost = modkernel32.NewProc("SetProcessPriorityBoost") + procDefineDosDeviceW = modkernel32.NewProc("DefineDosDeviceW") + procDeleteVolumeMountPointW = modkernel32.NewProc("DeleteVolumeMountPointW") + procFindFirstVolumeW = modkernel32.NewProc("FindFirstVolumeW") + procFindFirstVolumeMountPointW = modkernel32.NewProc("FindFirstVolumeMountPointW") + procFindNextVolumeW = modkernel32.NewProc("FindNextVolumeW") + procFindNextVolumeMountPointW = modkernel32.NewProc("FindNextVolumeMountPointW") + procFindVolumeClose = modkernel32.NewProc("FindVolumeClose") + procFindVolumeMountPointClose = modkernel32.NewProc("FindVolumeMountPointClose") + procGetDiskFreeSpaceExW = modkernel32.NewProc("GetDiskFreeSpaceExW") + procGetDriveTypeW = modkernel32.NewProc("GetDriveTypeW") + procGetLogicalDrives = modkernel32.NewProc("GetLogicalDrives") + procGetLogicalDriveStringsW = modkernel32.NewProc("GetLogicalDriveStringsW") + procGetVolumeInformationW = modkernel32.NewProc("GetVolumeInformationW") + procGetVolumeInformationByHandleW = modkernel32.NewProc("GetVolumeInformationByHandleW") + procGetVolumeNameForVolumeMountPointW = modkernel32.NewProc("GetVolumeNameForVolumeMountPointW") + procGetVolumePathNameW = modkernel32.NewProc("GetVolumePathNameW") + procGetVolumePathNamesForVolumeNameW = modkernel32.NewProc("GetVolumePathNamesForVolumeNameW") + procQueryDosDeviceW = modkernel32.NewProc("QueryDosDeviceW") + procSetVolumeLabelW = modkernel32.NewProc("SetVolumeLabelW") + procSetVolumeMountPointW = modkernel32.NewProc("SetVolumeMountPointW") + procMessageBoxW = moduser32.NewProc("MessageBoxW") + procExitWindowsEx = moduser32.NewProc("ExitWindowsEx") + procInitiateSystemShutdownExW = modadvapi32.NewProc("InitiateSystemShutdownExW") + procSetProcessShutdownParameters = modkernel32.NewProc("SetProcessShutdownParameters") + procGetProcessShutdownParameters = modkernel32.NewProc("GetProcessShutdownParameters") + procCLSIDFromString = modole32.NewProc("CLSIDFromString") + procStringFromGUID2 = modole32.NewProc("StringFromGUID2") + procCoCreateGuid = modole32.NewProc("CoCreateGuid") + procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procRtlGetVersion = modntdll.NewProc("RtlGetVersion") + procRtlGetNtVersionNumbers = modntdll.NewProc("RtlGetNtVersionNumbers") + procGetProcessPreferredUILanguages = modkernel32.NewProc("GetProcessPreferredUILanguages") + procGetThreadPreferredUILanguages = modkernel32.NewProc("GetThreadPreferredUILanguages") + procGetUserPreferredUILanguages = modkernel32.NewProc("GetUserPreferredUILanguages") + procGetSystemPreferredUILanguages = modkernel32.NewProc("GetSystemPreferredUILanguages") + procEnumProcesses = modpsapi.NewProc("EnumProcesses") + procWSAStartup = modws2_32.NewProc("WSAStartup") + procWSACleanup = modws2_32.NewProc("WSACleanup") + procWSAIoctl = modws2_32.NewProc("WSAIoctl") + procsocket = modws2_32.NewProc("socket") + procsendto = modws2_32.NewProc("sendto") + procrecvfrom = modws2_32.NewProc("recvfrom") + procsetsockopt = modws2_32.NewProc("setsockopt") + procgetsockopt = modws2_32.NewProc("getsockopt") + procbind = modws2_32.NewProc("bind") + procconnect = modws2_32.NewProc("connect") + procgetsockname = modws2_32.NewProc("getsockname") + procgetpeername = modws2_32.NewProc("getpeername") + proclisten = modws2_32.NewProc("listen") + procshutdown = modws2_32.NewProc("shutdown") + procclosesocket = modws2_32.NewProc("closesocket") + procAcceptEx = modmswsock.NewProc("AcceptEx") + procGetAcceptExSockaddrs = modmswsock.NewProc("GetAcceptExSockaddrs") + procWSARecv = modws2_32.NewProc("WSARecv") + procWSASend = modws2_32.NewProc("WSASend") + procWSARecvFrom = modws2_32.NewProc("WSARecvFrom") + procWSASendTo = modws2_32.NewProc("WSASendTo") + procgethostbyname = modws2_32.NewProc("gethostbyname") + procgetservbyname = modws2_32.NewProc("getservbyname") + procntohs = modws2_32.NewProc("ntohs") + procgetprotobyname = modws2_32.NewProc("getprotobyname") + procDnsQuery_W = moddnsapi.NewProc("DnsQuery_W") + procDnsRecordListFree = moddnsapi.NewProc("DnsRecordListFree") + procDnsNameCompare_W = moddnsapi.NewProc("DnsNameCompare_W") + procGetAddrInfoW = modws2_32.NewProc("GetAddrInfoW") + procFreeAddrInfoW = modws2_32.NewProc("FreeAddrInfoW") + procGetIfEntry = modiphlpapi.NewProc("GetIfEntry") + procGetAdaptersInfo = modiphlpapi.NewProc("GetAdaptersInfo") + procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes") + procWSAEnumProtocolsW = modws2_32.NewProc("WSAEnumProtocolsW") + procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") + procGetACP = modkernel32.NewProc("GetACP") + procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") + procTranslateNameW = modsecur32.NewProc("TranslateNameW") + procGetUserNameExW = modsecur32.NewProc("GetUserNameExW") + procNetUserGetInfo = modnetapi32.NewProc("NetUserGetInfo") + procNetGetJoinInformation = modnetapi32.NewProc("NetGetJoinInformation") + procNetApiBufferFree = modnetapi32.NewProc("NetApiBufferFree") + procLookupAccountSidW = modadvapi32.NewProc("LookupAccountSidW") + procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") + procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") + procConvertStringSidToSidW = modadvapi32.NewProc("ConvertStringSidToSidW") + procGetLengthSid = modadvapi32.NewProc("GetLengthSid") + procCopySid = modadvapi32.NewProc("CopySid") + procAllocateAndInitializeSid = modadvapi32.NewProc("AllocateAndInitializeSid") + procCreateWellKnownSid = modadvapi32.NewProc("CreateWellKnownSid") + procIsWellKnownSid = modadvapi32.NewProc("IsWellKnownSid") + procFreeSid = modadvapi32.NewProc("FreeSid") + procEqualSid = modadvapi32.NewProc("EqualSid") + procGetSidIdentifierAuthority = modadvapi32.NewProc("GetSidIdentifierAuthority") + procGetSidSubAuthorityCount = modadvapi32.NewProc("GetSidSubAuthorityCount") + procGetSidSubAuthority = modadvapi32.NewProc("GetSidSubAuthority") + procIsValidSid = modadvapi32.NewProc("IsValidSid") + procCheckTokenMembership = modadvapi32.NewProc("CheckTokenMembership") + procOpenProcessToken = modadvapi32.NewProc("OpenProcessToken") + procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") + procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") + procRevertToSelf = modadvapi32.NewProc("RevertToSelf") + procSetThreadToken = modadvapi32.NewProc("SetThreadToken") + procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW") + procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges") + procAdjustTokenGroups = modadvapi32.NewProc("AdjustTokenGroups") + procGetTokenInformation = modadvapi32.NewProc("GetTokenInformation") + procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation") + procDuplicateTokenEx = modadvapi32.NewProc("DuplicateTokenEx") + procGetUserProfileDirectoryW = moduserenv.NewProc("GetUserProfileDirectoryW") + procGetSystemDirectoryW = modkernel32.NewProc("GetSystemDirectoryW") + procGetWindowsDirectoryW = modkernel32.NewProc("GetWindowsDirectoryW") + procGetSystemWindowsDirectoryW = modkernel32.NewProc("GetSystemWindowsDirectoryW") + procWTSQueryUserToken = modwtsapi32.NewProc("WTSQueryUserToken") + procWTSEnumerateSessionsW = modwtsapi32.NewProc("WTSEnumerateSessionsW") + procWTSFreeMemory = modwtsapi32.NewProc("WTSFreeMemory") + procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") + procSetSecurityInfo = modadvapi32.NewProc("SetSecurityInfo") + procGetNamedSecurityInfoW = modadvapi32.NewProc("GetNamedSecurityInfoW") + procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW") + procBuildSecurityDescriptorW = modadvapi32.NewProc("BuildSecurityDescriptorW") + procInitializeSecurityDescriptor = modadvapi32.NewProc("InitializeSecurityDescriptor") + procGetSecurityDescriptorControl = modadvapi32.NewProc("GetSecurityDescriptorControl") + procGetSecurityDescriptorDacl = modadvapi32.NewProc("GetSecurityDescriptorDacl") + procGetSecurityDescriptorSacl = modadvapi32.NewProc("GetSecurityDescriptorSacl") + procGetSecurityDescriptorOwner = modadvapi32.NewProc("GetSecurityDescriptorOwner") + procGetSecurityDescriptorGroup = modadvapi32.NewProc("GetSecurityDescriptorGroup") + procGetSecurityDescriptorLength = modadvapi32.NewProc("GetSecurityDescriptorLength") + procGetSecurityDescriptorRMControl = modadvapi32.NewProc("GetSecurityDescriptorRMControl") + procIsValidSecurityDescriptor = modadvapi32.NewProc("IsValidSecurityDescriptor") + procSetSecurityDescriptorControl = modadvapi32.NewProc("SetSecurityDescriptorControl") + procSetSecurityDescriptorDacl = modadvapi32.NewProc("SetSecurityDescriptorDacl") + procSetSecurityDescriptorSacl = modadvapi32.NewProc("SetSecurityDescriptorSacl") + procSetSecurityDescriptorOwner = modadvapi32.NewProc("SetSecurityDescriptorOwner") + procSetSecurityDescriptorGroup = modadvapi32.NewProc("SetSecurityDescriptorGroup") + procSetSecurityDescriptorRMControl = modadvapi32.NewProc("SetSecurityDescriptorRMControl") + procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") + procConvertSecurityDescriptorToStringSecurityDescriptorW = modadvapi32.NewProc("ConvertSecurityDescriptorToStringSecurityDescriptorW") + procMakeAbsoluteSD = modadvapi32.NewProc("MakeAbsoluteSD") + procMakeSelfRelativeSD = modadvapi32.NewProc("MakeSelfRelativeSD") + procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW") ) func RegisterEventSource(uncServerName *uint16, sourceName *uint16) (handle Handle, err error) { @@ -417,6 +480,18 @@ func QueryServiceStatus(service Handle, status *SERVICE_STATUS) (err error) { return } +func QueryServiceLockStatus(mgr Handle, lockStatus *QUERY_SERVICE_LOCK_STATUS, bufSize uint32, bytesNeeded *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procQueryServiceLockStatusW.Addr(), 4, uintptr(mgr), uintptr(unsafe.Pointer(lockStatus)), uintptr(bufSize), uintptr(unsafe.Pointer(bytesNeeded)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func ControlService(service Handle, control uint32, status *SERVICE_STATUS) (err error) { r1, _, e1 := syscall.Syscall(procControlService.Addr(), 3, uintptr(service), uintptr(control), uintptr(unsafe.Pointer(status))) if r1 == 0 { @@ -619,6 +694,31 @@ func _GetProcAddress(module Handle, procname *byte) (proc uintptr, err error) { return } +func GetModuleFileName(module Handle, filename *uint16, size uint32) (n uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetModuleFileNameW.Addr(), 3, uintptr(module), uintptr(unsafe.Pointer(filename)), uintptr(size)) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetModuleHandleEx(flags uint32, moduleName *uint16, module *Handle) (err error) { + r1, _, e1 := syscall.Syscall(procGetModuleHandleExW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(moduleName)), uintptr(unsafe.Pointer(module))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetVersion() (ver uint32, err error) { r0, _, e1 := syscall.Syscall(procGetVersion.Addr(), 0, 0, 0, 0) ver = uint32(r0) @@ -655,7 +755,14 @@ func ExitProcess(exitcode uint32) { } func IsWow64Process(handle Handle, isWow64 *bool) (err error) { - r1, _, e1 := syscall.Syscall(procIsWow64Process.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(isWow64)), 0) + var _p0 uint32 + if *isWow64 { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procIsWow64Process.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(&_p0)), 0) + *isWow64 = _p0 != 0 if r1 == 0 { if e1 != 0 { err = errnoErr(e1) @@ -925,6 +1032,30 @@ func MoveFileEx(from *uint16, to *uint16, flags uint32) (err error) { return } +func LockFileEx(file Handle, flags uint32, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) { + r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6, uintptr(file), uintptr(flags), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func UnlockFileEx(file Handle, reserved uint32, bytesLow uint32, bytesHigh uint32, overlapped *Overlapped) (err error) { + r1, _, e1 := syscall.Syscall6(procUnlockFileEx.Addr(), 5, uintptr(file), uintptr(reserved), uintptr(bytesLow), uintptr(bytesHigh), uintptr(unsafe.Pointer(overlapped)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetComputerName(buf *uint16, n *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetComputerNameW.Addr(), 2, uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(n)), 0) if r1 == 0 { @@ -1063,14 +1194,14 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA return } -func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err error) { +func OpenProcess(desiredAccess uint32, inheritHandle bool, processId uint32) (handle Handle, err error) { var _p0 uint32 if inheritHandle { _p0 = 1 } else { _p0 = 0 } - r0, _, e1 := syscall.Syscall(procOpenProcess.Addr(), 3, uintptr(da), uintptr(_p0), uintptr(pid)) + r0, _, e1 := syscall.Syscall(procOpenProcess.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(processId)) handle = Handle(r0) if handle == 0 { if e1 != 0 { @@ -1084,7 +1215,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle Handle, err func ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *uint16, showCmd int32) (err error) { r1, _, e1 := syscall.Syscall6(procShellExecuteW.Addr(), 6, uintptr(hwnd), uintptr(unsafe.Pointer(verb)), uintptr(unsafe.Pointer(file)), uintptr(unsafe.Pointer(args)), uintptr(unsafe.Pointer(cwd)), uintptr(showCmd)) - if r1 == 0 { + if r1 <= 32 { if e1 != 0 { err = errnoErr(e1) } else { @@ -1094,6 +1225,14 @@ func ShellExecute(hwnd Handle, verb *uint16, file *uint16, args *uint16, cwd *ui return } +func shGetKnownFolderPath(id *KNOWNFOLDERID, flags uint32, token Token, path **uint16) (ret error) { + r0, _, _ := syscall.Syscall6(procSHGetKnownFolderPath.Addr(), 4, uintptr(unsafe.Pointer(id)), uintptr(flags), uintptr(token), uintptr(unsafe.Pointer(path)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + func TerminateProcess(handle Handle, exitcode uint32) (err error) { r1, _, e1 := syscall.Syscall(procTerminateProcess.Addr(), 2, uintptr(handle), uintptr(exitcode), 0) if r1 == 0 { @@ -1130,32 +1269,6 @@ func GetStartupInfo(startupInfo *StartupInfo) (err error) { return } -func GetCurrentProcess() (pseudoHandle Handle, err error) { - r0, _, e1 := syscall.Syscall(procGetCurrentProcess.Addr(), 0, 0, 0, 0) - pseudoHandle = Handle(r0) - if pseudoHandle == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func GetCurrentThread() (pseudoHandle Handle, err error) { - r0, _, e1 := syscall.Syscall(procGetCurrentThread.Addr(), 0, 0, 0, 0) - pseudoHandle = Handle(r0) - if pseudoHandle == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } - } - return -} - func GetProcessTimes(handle Handle, creationTime *Filetime, exitTime *Filetime, kernelTime *Filetime, userTime *Filetime) (err error) { r1, _, e1 := syscall.Syscall6(procGetProcessTimes.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(creationTime)), uintptr(unsafe.Pointer(exitTime)), uintptr(unsafe.Pointer(kernelTime)), uintptr(unsafe.Pointer(userTime)), 0) if r1 == 0 { @@ -1372,6 +1485,12 @@ func DestroyEnvironmentBlock(block *uint16) (err error) { return } +func getTickCount64() (ms uint64) { + r0, _, _ := syscall.Syscall(procGetTickCount64.Addr(), 0, 0, 0, 0) + ms = uint64(r0) + return +} + func SetFileTime(handle Handle, ctime *Filetime, atime *Filetime, wtime *Filetime) (err error) { r1, _, e1 := syscall.Syscall6(procSetFileTime.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(ctime)), uintptr(unsafe.Pointer(atime)), uintptr(unsafe.Pointer(wtime)), 0, 0) if r1 == 0 { @@ -1814,7 +1933,7 @@ func RegQueryValueEx(key Handle, name *uint16, reserved *uint32, valtype *uint32 return } -func getCurrentProcessId() (pid uint32) { +func GetCurrentProcessId() (pid uint32) { r0, _, _ := syscall.Syscall(procGetCurrentProcessId.Addr(), 0, 0, 0, 0) pid = uint32(r0) return @@ -1917,6 +2036,30 @@ func Process32Next(snapshot Handle, procEntry *ProcessEntry32) (err error) { return } +func Thread32First(snapshot Handle, threadEntry *ThreadEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procThread32First.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func Thread32Next(snapshot Handle, threadEntry *ThreadEntry32) (err error) { + r1, _, e1 := syscall.Syscall(procThread32Next.Addr(), 2, uintptr(snapshot), uintptr(unsafe.Pointer(threadEntry)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func DeviceIoControl(handle Handle, ioControlCode uint32, inBuffer *byte, inBufferSize uint32, outBuffer *byte, outBufferSize uint32, bytesReturned *uint32, overlapped *Overlapped) (err error) { r1, _, e1 := syscall.Syscall9(procDeviceIoControl.Addr(), 8, uintptr(handle), uintptr(ioControlCode), uintptr(unsafe.Pointer(inBuffer)), uintptr(inBufferSize), uintptr(unsafe.Pointer(outBuffer)), uintptr(outBufferSize), uintptr(unsafe.Pointer(bytesReturned)), uintptr(unsafe.Pointer(overlapped)), 0) if r1 == 0 { @@ -2040,6 +2183,69 @@ func PulseEvent(event Handle) (err error) { return } +func CreateMutex(mutexAttrs *SecurityAttributes, initialOwner bool, name *uint16) (handle Handle, err error) { + var _p0 uint32 + if initialOwner { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall(procCreateMutexW.Addr(), 3, uintptr(unsafe.Pointer(mutexAttrs)), uintptr(_p0), uintptr(unsafe.Pointer(name))) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func CreateMutexEx(mutexAttrs *SecurityAttributes, name *uint16, flags uint32, desiredAccess uint32) (handle Handle, err error) { + r0, _, e1 := syscall.Syscall6(procCreateMutexExW.Addr(), 4, uintptr(unsafe.Pointer(mutexAttrs)), uintptr(unsafe.Pointer(name)), uintptr(flags), uintptr(desiredAccess), 0, 0) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func OpenMutex(desiredAccess uint32, inheritHandle bool, name *uint16) (handle Handle, err error) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall(procOpenMutexW.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(unsafe.Pointer(name))) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func ReleaseMutex(mutex Handle) (err error) { + r1, _, e1 := syscall.Syscall(procReleaseMutex.Addr(), 1, uintptr(mutex), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func SleepEx(milliseconds uint32, alertable bool) (ret uint32) { var _p0 uint32 if alertable { @@ -2146,6 +2352,68 @@ func SetInformationJobObject(job Handle, JobObjectInformationClass uint32, JobOb return } +func GenerateConsoleCtrlEvent(ctrlEvent uint32, processGroupID uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGenerateConsoleCtrlEvent.Addr(), 2, uintptr(ctrlEvent), uintptr(processGroupID), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetProcessId(process Handle) (id uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetProcessId.Addr(), 1, uintptr(process), 0, 0) + id = uint32(r0) + if id == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func OpenThread(desiredAccess uint32, inheritHandle bool, threadId uint32) (handle Handle, err error) { + var _p0 uint32 + if inheritHandle { + _p0 = 1 + } else { + _p0 = 0 + } + r0, _, e1 := syscall.Syscall(procOpenThread.Addr(), 3, uintptr(desiredAccess), uintptr(_p0), uintptr(threadId)) + handle = Handle(r0) + if handle == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetProcessPriorityBoost(process Handle, disable bool) (err error) { + var _p0 uint32 + if disable { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procSetProcessPriorityBoost.Addr(), 2, uintptr(process), uintptr(_p0), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func DefineDosDevice(flags uint32, deviceName *uint16, targetPath *uint16) (err error) { r1, _, e1 := syscall.Syscall(procDefineDosDeviceW.Addr(), 3, uintptr(flags), uintptr(unsafe.Pointer(deviceName)), uintptr(unsafe.Pointer(targetPath))) if r1 == 0 { @@ -2244,6 +2512,18 @@ func FindVolumeMountPointClose(findVolumeMountPoint Handle) (err error) { return } +func GetDiskFreeSpaceEx(directoryName *uint16, freeBytesAvailableToCaller *uint64, totalNumberOfBytes *uint64, totalNumberOfFreeBytes *uint64) (err error) { + r1, _, e1 := syscall.Syscall6(procGetDiskFreeSpaceExW.Addr(), 4, uintptr(unsafe.Pointer(directoryName)), uintptr(unsafe.Pointer(freeBytesAvailableToCaller)), uintptr(unsafe.Pointer(totalNumberOfBytes)), uintptr(unsafe.Pointer(totalNumberOfFreeBytes)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func GetDriveType(rootPathName *uint16) (driveType uint32) { r0, _, _ := syscall.Syscall(procGetDriveTypeW.Addr(), 1, uintptr(unsafe.Pointer(rootPathName)), 0, 0) driveType = uint32(r0) @@ -2386,6 +2666,170 @@ func MessageBox(hwnd Handle, text *uint16, caption *uint16, boxtype uint32) (ret return } +func ExitWindowsEx(flags uint32, reason uint32) (err error) { + r1, _, e1 := syscall.Syscall(procExitWindowsEx.Addr(), 2, uintptr(flags), uintptr(reason), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func InitiateSystemShutdownEx(machineName *uint16, message *uint16, timeout uint32, forceAppsClosed bool, rebootAfterShutdown bool, reason uint32) (err error) { + var _p0 uint32 + if forceAppsClosed { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if rebootAfterShutdown { + _p1 = 1 + } else { + _p1 = 0 + } + r1, _, e1 := syscall.Syscall6(procInitiateSystemShutdownExW.Addr(), 6, uintptr(unsafe.Pointer(machineName)), uintptr(unsafe.Pointer(message)), uintptr(timeout), uintptr(_p0), uintptr(_p1), uintptr(reason)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetProcessShutdownParameters(level uint32, flags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetProcessShutdownParameters.Addr(), 2, uintptr(level), uintptr(flags), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func GetProcessShutdownParameters(level *uint32, flags *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetProcessShutdownParameters.Addr(), 2, uintptr(unsafe.Pointer(level)), uintptr(unsafe.Pointer(flags)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func clsidFromString(lpsz *uint16, pclsid *GUID) (ret error) { + r0, _, _ := syscall.Syscall(procCLSIDFromString.Addr(), 2, uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func stringFromGUID2(rguid *GUID, lpsz *uint16, cchMax int32) (chars int32) { + r0, _, _ := syscall.Syscall(procStringFromGUID2.Addr(), 3, uintptr(unsafe.Pointer(rguid)), uintptr(unsafe.Pointer(lpsz)), uintptr(cchMax)) + chars = int32(r0) + return +} + +func coCreateGuid(pguid *GUID) (ret error) { + r0, _, _ := syscall.Syscall(procCoCreateGuid.Addr(), 1, uintptr(unsafe.Pointer(pguid)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func CoTaskMemFree(address unsafe.Pointer) { + syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(address), 0, 0) + return +} + +func rtlGetVersion(info *OsVersionInfoEx) (ret error) { + r0, _, _ := syscall.Syscall(procRtlGetVersion.Addr(), 1, uintptr(unsafe.Pointer(info)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) { + syscall.Syscall(procRtlGetNtVersionNumbers.Addr(), 3, uintptr(unsafe.Pointer(majorVersion)), uintptr(unsafe.Pointer(minorVersion)), uintptr(unsafe.Pointer(buildNumber))) + return +} + +func getProcessPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetProcessPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getThreadPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetThreadPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getUserPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetUserPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSystemPreferredUILanguages(flags uint32, numLanguages *uint32, buf *uint16, bufSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procGetSystemPreferredUILanguages.Addr(), 4, uintptr(flags), uintptr(unsafe.Pointer(numLanguages)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(bufSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func EnumProcesses(processIds []uint32, bytesReturned *uint32) (err error) { + var _p0 *uint32 + if len(processIds) > 0 { + _p0 = &processIds[0] + } + r1, _, e1 := syscall.Syscall(procEnumProcesses.Addr(), 3, uintptr(unsafe.Pointer(_p0)), uintptr(len(processIds)), uintptr(unsafe.Pointer(bytesReturned))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func WSAStartup(verreq uint32, data *WSAData) (sockerr error) { r0, _, _ := syscall.Syscall(procWSAStartup.Addr(), 2, uintptr(verreq), uintptr(unsafe.Pointer(data)), 0) if r0 != 0 { @@ -2431,6 +2875,39 @@ func socket(af int32, typ int32, protocol int32) (handle Handle, err error) { return } +func sendto(s Handle, buf []byte, flags int32, to unsafe.Pointer, tolen int32) (err error) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } + r1, _, e1 := syscall.Syscall6(procsendto.Addr(), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(to), uintptr(tolen)) + if r1 == socket_error { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func recvfrom(s Handle, buf []byte, flags int32, from *RawSockaddrAny, fromlen *int32) (n int32, err error) { + var _p0 *byte + if len(buf) > 0 { + _p0 = &buf[0] + } + r0, _, e1 := syscall.Syscall6(procrecvfrom.Addr(), 6, uintptr(s), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))) + n = int32(r0) + if n == -1 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func Setsockopt(s Handle, level int32, optname int32, optval *byte, optlen int32) (err error) { r1, _, e1 := syscall.Syscall6(procsetsockopt.Addr(), 5, uintptr(s), uintptr(level), uintptr(optname), uintptr(unsafe.Pointer(optval)), uintptr(optlen), 0) if r1 == socket_error { @@ -3163,6 +3640,32 @@ func getSystemDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { return } +func getWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetWindowsDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0) + len = uint32(r0) + if len == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSystemWindowsDirectory(dir *uint16, dirLen uint32) (len uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetSystemWindowsDirectoryW.Addr(), 2, uintptr(unsafe.Pointer(dir)), uintptr(dirLen), 0) + len = uint32(r0) + if len == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func WTSQueryUserToken(session uint32, token *Token) (err error) { r1, _, e1 := syscall.Syscall(procWTSQueryUserToken.Addr(), 2, uintptr(session), uintptr(unsafe.Pointer(token)), 0) if r1 == 0 { @@ -3191,3 +3694,358 @@ func WTSFreeMemory(ptr uintptr) { syscall.Syscall(procWTSFreeMemory.Addr(), 1, uintptr(ptr), 0, 0) return } + +func getSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) { + r0, _, _ := syscall.Syscall9(procGetSecurityInfo.Addr(), 8, uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func SetSecurityInfo(handle Handle, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) { + syscall.Syscall9(procSetSecurityInfo.Addr(), 7, uintptr(handle), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) + return +} + +func getNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) { + var _p0 *uint16 + _p0, ret = syscall.UTF16PtrFromString(objectName) + if ret != nil { + return + } + return _getNamedSecurityInfo(_p0, objectType, securityInformation, owner, group, dacl, sacl, sd) +} + +func _getNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner **SID, group **SID, dacl **ACL, sacl **ACL, sd **SECURITY_DESCRIPTOR) (ret error) { + r0, _, _ := syscall.Syscall9(procGetNamedSecurityInfoW.Addr(), 8, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(sd)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func SetNamedSecurityInfo(objectName string, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) { + var _p0 *uint16 + _p0, ret = syscall.UTF16PtrFromString(objectName) + if ret != nil { + return + } + return _SetNamedSecurityInfo(_p0, objectType, securityInformation, owner, group, dacl, sacl) +} + +func _SetNamedSecurityInfo(objectName *uint16, objectType SE_OBJECT_TYPE, securityInformation SECURITY_INFORMATION, owner *SID, group *SID, dacl *ACL, sacl *ACL) (ret error) { + r0, _, _ := syscall.Syscall9(procSetNamedSecurityInfoW.Addr(), 7, uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(sacl)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func 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) { + r0, _, _ := syscall.Syscall9(procBuildSecurityDescriptorW.Addr(), 9, uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(countAccessEntries), uintptr(unsafe.Pointer(accessEntries)), uintptr(countAuditEntries), uintptr(unsafe.Pointer(auditEntries)), uintptr(unsafe.Pointer(oldSecurityDescriptor)), uintptr(unsafe.Pointer(sizeNewSecurityDescriptor)), uintptr(unsafe.Pointer(newSecurityDescriptor))) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func initializeSecurityDescriptor(absoluteSD *SECURITY_DESCRIPTOR, revision uint32) (err error) { + r1, _, e1 := syscall.Syscall(procInitializeSecurityDescriptor.Addr(), 2, uintptr(unsafe.Pointer(absoluteSD)), uintptr(revision), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, control *SECURITY_DESCRIPTOR_CONTROL, revision *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorControl.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(control)), uintptr(unsafe.Pointer(revision))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent *bool, dacl **ACL, daclDefaulted *bool) (err error) { + var _p0 uint32 + if *daclPresent { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if *daclDefaulted { + _p1 = 1 + } else { + _p1 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(&_p1)), 0, 0) + *daclPresent = _p0 != 0 + *daclDefaulted = _p1 != 0 + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent *bool, sacl **ACL, saclDefaulted *bool) (err error) { + var _p0 uint32 + if *saclPresent { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if *saclDefaulted { + _p1 = 1 + } else { + _p1 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetSecurityDescriptorSacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(&_p0)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(&_p1)), 0, 0) + *saclPresent = _p0 != 0 + *saclDefaulted = _p1 != 0 + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner **SID, ownerDefaulted *bool) (err error) { + var _p0 uint32 + if *ownerDefaulted { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorOwner.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(&_p0))) + *ownerDefaulted = _p0 != 0 + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group **SID, groupDefaulted *bool) (err error) { + var _p0 uint32 + if *groupDefaulted { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procGetSecurityDescriptorGroup.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(&_p0))) + *groupDefaulted = _p0 != 0 + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getSecurityDescriptorLength(sd *SECURITY_DESCRIPTOR) (len uint32) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorLength.Addr(), 1, uintptr(unsafe.Pointer(sd)), 0, 0) + len = uint32(r0) + return +} + +func getSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) (ret error) { + r0, _, _ := syscall.Syscall(procGetSecurityDescriptorRMControl.Addr(), 2, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl)), 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} + +func isValidSecurityDescriptor(sd *SECURITY_DESCRIPTOR) (isValid bool) { + r0, _, _ := syscall.Syscall(procIsValidSecurityDescriptor.Addr(), 1, uintptr(unsafe.Pointer(sd)), 0, 0) + isValid = r0 != 0 + return +} + +func setSecurityDescriptorControl(sd *SECURITY_DESCRIPTOR, controlBitsOfInterest SECURITY_DESCRIPTOR_CONTROL, controlBitsToSet SECURITY_DESCRIPTOR_CONTROL) (err error) { + r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorControl.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(controlBitsOfInterest), uintptr(controlBitsToSet)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setSecurityDescriptorDacl(sd *SECURITY_DESCRIPTOR, daclPresent bool, dacl *ACL, daclDefaulted bool) (err error) { + var _p0 uint32 + if daclPresent { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if daclDefaulted { + _p1 = 1 + } else { + _p1 = 0 + } + r1, _, e1 := syscall.Syscall6(procSetSecurityDescriptorDacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(dacl)), uintptr(_p1), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setSecurityDescriptorSacl(sd *SECURITY_DESCRIPTOR, saclPresent bool, sacl *ACL, saclDefaulted bool) (err error) { + var _p0 uint32 + if saclPresent { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if saclDefaulted { + _p1 = 1 + } else { + _p1 = 0 + } + r1, _, e1 := syscall.Syscall6(procSetSecurityDescriptorSacl.Addr(), 4, uintptr(unsafe.Pointer(sd)), uintptr(_p0), uintptr(unsafe.Pointer(sacl)), uintptr(_p1), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setSecurityDescriptorOwner(sd *SECURITY_DESCRIPTOR, owner *SID, ownerDefaulted bool) (err error) { + var _p0 uint32 + if ownerDefaulted { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorOwner.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(owner)), uintptr(_p0)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setSecurityDescriptorGroup(sd *SECURITY_DESCRIPTOR, group *SID, groupDefaulted bool) (err error) { + var _p0 uint32 + if groupDefaulted { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall(procSetSecurityDescriptorGroup.Addr(), 3, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(group)), uintptr(_p0)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setSecurityDescriptorRMControl(sd *SECURITY_DESCRIPTOR, rmControl *uint8) { + syscall.Syscall(procSetSecurityDescriptorRMControl.Addr(), 2, uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(rmControl)), 0) + return +} + +func convertStringSecurityDescriptorToSecurityDescriptor(str string, revision uint32, sd **SECURITY_DESCRIPTOR, size *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(str) + if err != nil { + return + } + return _convertStringSecurityDescriptorToSecurityDescriptor(_p0, revision, sd, size) +} + +func _convertStringSecurityDescriptorToSecurityDescriptor(str *uint16, revision uint32, sd **SECURITY_DESCRIPTOR, size *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertStringSecurityDescriptorToSecurityDescriptorW.Addr(), 4, uintptr(unsafe.Pointer(str)), uintptr(revision), uintptr(unsafe.Pointer(sd)), uintptr(unsafe.Pointer(size)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func convertSecurityDescriptorToStringSecurityDescriptor(sd *SECURITY_DESCRIPTOR, revision uint32, securityInformation SECURITY_INFORMATION, str **uint16, strLen *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procConvertSecurityDescriptorToStringSecurityDescriptorW.Addr(), 5, uintptr(unsafe.Pointer(sd)), uintptr(revision), uintptr(securityInformation), uintptr(unsafe.Pointer(str)), uintptr(unsafe.Pointer(strLen)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func 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) { + r1, _, e1 := syscall.Syscall12(procMakeAbsoluteSD.Addr(), 11, uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(absoluteSDSize)), uintptr(unsafe.Pointer(dacl)), uintptr(unsafe.Pointer(daclSize)), uintptr(unsafe.Pointer(sacl)), uintptr(unsafe.Pointer(saclSize)), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(ownerSize)), uintptr(unsafe.Pointer(group)), uintptr(unsafe.Pointer(groupSize)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall(procMakeSelfRelativeSD.Addr(), 3, uintptr(unsafe.Pointer(absoluteSD)), uintptr(unsafe.Pointer(selfRelativeSD)), uintptr(unsafe.Pointer(selfRelativeSDSize))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) { + r0, _, _ := syscall.Syscall6(procSetEntriesInAclW.Addr(), 4, uintptr(countExplicitEntries), uintptr(unsafe.Pointer(explicitEntries)), uintptr(unsafe.Pointer(oldACL)), uintptr(unsafe.Pointer(newACL)), 0, 0) + if r0 != 0 { + ret = syscall.Errno(r0) + } + return +} diff --git a/go/vendor/modules.txt b/go/vendor/modules.txt index 8e321f759..0bfd12bd2 100644 --- a/go/vendor/modules.txt +++ b/go/vendor/modules.txt @@ -4,5 +4,5 @@ github.com/Microsoft/go-winio/pkg/guid # github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 github.com/hectane/go-acl github.com/hectane/go-acl/api -# golang.org/x/sys v0.0.0-20190529164535-6a60838ec259 +# golang.org/x/sys v0.0.0-20200107162124-548cf772de50 golang.org/x/sys/windows diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 9daa3551d..e58e6eaeb 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -19,7 +19,12 @@ #ifndef ZT_ZEROTIER_API_H #define ZT_ZEROTIER_API_H +#ifdef __cplusplus +#include +extern "C" { +#else #include +#endif /* For struct sockaddr_storage, which is referenced here. */ #if defined(_WIN32) || defined(_WIN64) @@ -39,10 +44,6 @@ #define ZT_SDK_API #endif -#ifdef __cplusplus -extern "C" { -#endif - /****************************************************************************/ /* Core constants */ /****************************************************************************/ @@ -91,11 +92,6 @@ extern "C" { */ #define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM) -/** - * Maximum size of a remote trace message's serialized Dictionary - */ -#define ZT_MAX_REMOTE_TRACE_SIZE 10000 - /** * Maximum length of network short name */ @@ -258,78 +254,6 @@ extern "C" { */ #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL -/****************************************************************************/ - -// Fields in remote trace dictionaries -#define ZT_REMOTE_TRACE_FIELD__EVENT "event" -#define ZT_REMOTE_TRACE_FIELD__NODE_ID "nodeId" -#define ZT_REMOTE_TRACE_FIELD__PACKET_ID "packetId" -#define ZT_REMOTE_TRACE_FIELD__PACKET_VERB "packetVerb" -#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_ID "packetTrustedPathId" -#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_APPROVED "packetTrustedPathApproved" -#define ZT_REMOTE_TRACE_FIELD__PACKET_HOPS "packetHops" -#define ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR "remoteZtAddr" -#define ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR "remotePhyAddr" -#define ZT_REMOTE_TRACE_FIELD__LOCAL_ZTADDR "localZtAddr" -#define ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR "localPhyAddr" -#define ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET "localSocket" -#define ZT_REMOTE_TRACE_FIELD__IP_SCOPE "phyAddrIpScope" -#define ZT_REMOTE_TRACE_FIELD__NETWORK_ID "networkId" -#define ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR "sourceZtAddr" -#define ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR "destZtAddr" -#define ZT_REMOTE_TRACE_FIELD__SOURCE_MAC "sourceMac" -#define ZT_REMOTE_TRACE_FIELD__DEST_MAC "destMac" -#define ZT_REMOTE_TRACE_FIELD__ETHERTYPE "etherType" -#define ZT_REMOTE_TRACE_FIELD__VLAN_ID "vlanId" -#define ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH "frameLength" -#define ZT_REMOTE_TRACE_FIELD__FRAME_DATA "frameData" -#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE "filterNoTee" -#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND "filterInbound" -#define ZT_REMOTE_TRACE_FIELD__FILTER_RESULT "filterResult" -#define ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG "filterBaseRuleLog" -#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG "filterCapRuleLog" -#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID "filterMatchingCapId" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE "credType" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID "credId" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP "credTs" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO "credInfo" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO "credIssuedTo" -#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET "credRevocationTarget" -#define ZT_REMOTE_TRACE_FIELD__REASON "reason" -#define ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID "networkControllerId" - -// Event types in remote traces -#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE 0x1000 -#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH 0x1001 -#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH 0x1002 -#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED 0x1003 -#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE 0x1004 -#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID 0x1005 -#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO 0x1006 -#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED 0x2000 -#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED 0x2001 -#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED 0x2002 -#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED 0x2003 -#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED 0x2004 -#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT 0x2005 -#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE 0x2006 - -// Event types in remote traces in hex string form -#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S "1000" -#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S "1001" -#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S "1002" -#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S "1003" -#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S "1004" -#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S "1005" -#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO_S "1006" -#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S "2000" -#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S "2001" -#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S "2002" -#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S "2003" -#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED_S "2004" -#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S "2005" -#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S "2006" - /****************************************************************************/ /* Structures and other types */ /****************************************************************************/ @@ -397,35 +321,6 @@ enum ZT_ResultCode */ #define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000)) -/** - * The multipath algorithm in use by this node. - */ -enum ZT_MultipathMode -{ - /** - * No active multipath. - * - * Traffic is merely sent over the strongest path. That being - * said, this mode will automatically failover in the event that a link goes down. - */ - ZT_MULTIPATH_NONE = 0, - - /** - * Traffic is randomly distributed among all active paths. - * - * Will cease sending traffic over links that appear to be stale. - */ - ZT_MULTIPATH_RANDOM = 1, - - /** - * Traffic is allocated across all active paths in proportion to their strength and - * reliability. - * - * Will cease sending traffic over links that appear to be stale. - */ - ZT_MULTIPATH_PROPORTIONALLY_BALANCED = 2, -}; - /** * Status codes sent to status update callback when things happen */ @@ -499,87 +394,23 @@ enum ZT_Event * * Meta-data: ZT_UserMessage structure */ - ZT_EVENT_USER_MESSAGE = 6, - - /** - * Remote trace received - * - * NOTE: any node can fling a VERB_REMOTE_TRACE at you. It's up to you - * to determine if you want to do anything with it or just silently - * drop it on the floor. It's also up to you to handle these securely! - * - * Meta-data: ZT_RemoteTrace structure - */ - ZT_EVENT_REMOTE_TRACE = 7 + ZT_EVENT_USER_MESSAGE = 6 }; /** - * A root server + * Identity type codes */ -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 - */ -typedef struct { - /** - * Number of root servers - */ - unsigned int count; - - /** - * Array of root servers - */ - ZT_Root roots[]; -} ZT_RootList; - -/** - * Payload of REMOTE_TRACE event - */ -typedef struct +enum ZT_Identity_Type { - /** - * ZeroTier address of sender (in least significant 40 bits only) - */ - uint64_t origin; + /* These values must be the same as in Identity.hpp in the core. */ + ZT_IDENTITY_TYPE_C25519 = 0, + ZT_IDENTITY_TYPE_P384 = 1 +}; - /** - * 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; +/** + * A ZeroTier identity (opaque) + */ +typedef void ZT_Identity; /** * User message used with ZT_EVENT_USER_MESSAGE @@ -624,6 +455,11 @@ typedef struct */ uint64_t address; + /** + * Actual identity object for this node + */ + const ZT_Identity *identity; + /** * Public identity in string-serialized form (safe to send to others) * @@ -1000,39 +836,6 @@ enum ZT_VirtualNetworkConfigOperation ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4 }; -/** - * What trust hierarchy role does this peer have? - */ -enum ZT_PeerRole -{ - ZT_PEER_ROLE_LEAF = 0, // ordinary node - ZT_PEER_ROLE_MOON = 1, // moon root - ZT_PEER_ROLE_PLANET = 2 // planetary root -}; - -/** - * DNS record types for reporting DNS results - * - * These integer IDs (other than end of results) are the same as the DNS protocol's - * internal IDs. Not all of these are used by ZeroTier, and not all DNS record types - * are listed here. These are just common ones that are used now or may be used in - * the future for some purpose. - */ -enum ZT_DNSRecordType -{ - ZT_DNS_RECORD__END_OF_RESULTS = 0, - ZT_DNS_RECORD_A = 1, - ZT_DNS_RECORD_NS = 2, - ZT_DNS_RECORD_CNAME = 5, - ZT_DNS_RECORD_PTR = 12, - ZT_DNS_RECORD_MX = 15, - ZT_DNS_RECORD_TXT = 16, - ZT_DNS_RECORD_AAAA = 28, - ZT_DNS_RECORD_LOC = 29, - ZT_DNS_RECORD_SRV = 33, - ZT_DNS_RECORD_DNAME = 39 -}; - /** * Virtual network configuration */ @@ -1180,56 +983,6 @@ typedef struct */ uint64_t trustedPathId; - /** - * One-way latency - */ - float latency; - - /** - * How much latency varies over time - */ - float packetDelayVariance; - - /** - * How much observed throughput varies over time - */ - float throughputDisturbCoeff; - - /** - * Packet Error Ratio (PER) - */ - float packetErrorRatio; - - /** - * Packet Loss Ratio (PLR) - */ - float packetLossRatio; - - /** - * Stability of the path - */ - float stability; - - /** - * Current throughput (moving average) - */ - uint64_t throughput; - - /** - * Maximum observed throughput for this path - */ - uint64_t maxThroughput; - - /** - * Percentage of traffic allocated to this path - */ - float allocation; - - /** - * Name of physical interface (for monitoring) - */ - char *ifname; - /** * Is path alive? */ @@ -1241,6 +994,15 @@ typedef struct int preferred; } ZT_PeerPhysicalPath; +/** + * What trust hierarchy role does this peer have? + */ +enum ZT_PeerRole +{ + ZT_PEER_ROLE_LEAF = 0, // ordinary node + ZT_PEER_ROLE_ROOT = 1 // root server +}; + /** * Peer status result buffer */ @@ -1251,6 +1013,11 @@ typedef struct */ uint64_t address; + /** + * Peer identity + */ + const ZT_Identity *identity; + /** * Remote major version or -1 if not known */ @@ -1281,11 +1048,6 @@ typedef struct */ unsigned int pathCount; - /** - * Whether this peer was ever reachable via an aggregate link - */ - int hadAggregateLink; - /** * Known network paths to peer */ @@ -1541,8 +1303,9 @@ typedef int (*ZT_PathCheckFunction)( * (1) Node * (2) User pointer * (3) ZeroTier address (least significant 40 bits) - * (4) Desired address family or -1 for any - * (5) Buffer to fill with result + * (4) Identity in string form + * (5) Desired address family or -1 for any + * (6) Buffer to fill with result * * If provided this function will be occasionally called to get physical * addresses that might be tried to reach a ZeroTier address. It must @@ -1554,53 +1317,10 @@ typedef int (*ZT_PathLookupFunction)( void *, /* User ptr */ void *, /* Thread ptr */ uint64_t, /* ZeroTier address (40 bits) */ + const ZT_Identity *, /* Full identity of node */ int, /* Desired ss_family or -1 for any */ struct sockaddr_storage *); /* Result buffer */ -/** - * Function to request an asynchronous DNS TXT lookup - * - * Parameters: - * (1) Node - * (2) User pointer - * (3) Thread pointer - * (4) Array of DNS record types we want - * (5) Number of DNS record types in array - * (6) DNS name to fetch - * (7) DNS request ID to supply to ZT_Node_processDNSResult() - * - * DNS is not handled in the core because every platform and runtime - * typically has its own DNS functions or libraries and these may need - * to interface with OS or network services in your local environment. - * Instead this function and its result submission counterpart are - * provided so you can provide a DNS implementation. - * - * If this callback is set in your callback struct to a NULL value, - * DNS will not be available. The ZeroTier protocol is designed to - * work in the absence of DNS but you may not get optimal results. For - * example you may default to root servers that are not geographically - * optimal or your node may cease to function if a root server's IP - * changes and there's no way to signal this. - * - * This function requests resolution of a DNS record. The result - * submission method ZT_Node_processDNSResult() must be called at - * least once in response. See its documentation. - * - * Right now ZeroTier only requests resolution of TXT records, but - * it's possible that this will change in the future. - * - * It's safe to call processDNSResult() from within your handler - * for this function. - */ -typedef void (*ZT_DNSResolver)( - ZT_Node *, /* Node */ - void *, /* User ptr */ - void *, /* Thread ptr */ - const enum ZT_DNSRecordType *, /* DNS record type(s) to fetch */ - unsigned int, /* Number of DNS record type(s) */ - const char *, /* DNS name to fetch */ - uintptr_t); /* Request ID for returning results */ - /****************************************************************************/ /* C Node API */ /****************************************************************************/ @@ -1641,17 +1361,12 @@ struct ZT_Node_Callbacks ZT_EventCallback eventCallback; /** - * STRONGLY RECOMMENDED: Function to request a DNS lookup - */ - ZT_DNSResolver dnsResolver; - - /** - * OPTIONAL: Function to check whether a given physical path should be used + * OPTIONAL: Function to check whether a given physical path should be used for ZeroTier traffic */ ZT_PathCheckFunction pathCheckFunction; /** - * OPTIONAL: Function to get hints to physical paths to ZeroTier addresses + * RECOMMENDED: Function to look up paths to ZeroTier nodes */ ZT_PathLookupFunction pathLookupFunction; }; @@ -1750,60 +1465,6 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks( int64_t now, volatile int64_t *nextBackgroundTaskDeadline); -/** - * Submit the result(s) of a requested DNS query - * - * This MUST be called at least once after the node requsts DNS resolution. - * If there are no results or DNS is not implemented or available, just - * send one ZT_DNS_RECORD__END_OF_RESULTS to signal that no results were - * obtained. - * - * If result is non-NULL but resultLength is zero then result is assumed to - * be a C string terminated by a zero. Passing an unterminated string with a - * zero resultLength will result in a crash. - * - * The results of A and AAAA records can be returned as either strings or - * binary IP address bytes (network byte order). If the result is a string, - * resultLength must be 0 to signal that result is a C string. Otherwise for - * A resultLength must be 4 and for AAAA it must be 16 if the result is - * in binary format. - * - * The Node implementation makes an effort to ignore obviously invalid - * submissions like an AAAA record in bianry form with length 25, but this - * is not guaranteed. It's possible to crash your program by calling this - * with garbage inputs. - * - * Results may be submitted in any order and order should not be assumed - * to have any meaning. - * - * The ZT_DNS_RECORD__END_OF_RESULTS pseudo-response must be sent after all - * results have been submitted. The result and resultLength paramters are - * ignored for this type ID. - * - * It is safe to call this function from inside the DNS request callback, - * such as to return a locally cached result or a result from some kind - * of local database. It's also safe to call this function from threads - * other than the one that received the DNS request. - * - * @param node Node instance that requested DNS resolution - * @param tptr Thread pointer to pass to functions/callbacks resulting from this call - * @param dnsRequestID Request ID supplied to DNS request callback - * @param name DNS name - * @param recordType Record type of this result - * @param result Result (content depends on record type) - * @param resultLength Length of result - * @param resultIsString If non-zero, IP results for A and AAAA records are being given as C strings not binary IPs - */ -ZT_SDK_API void ZT_Node_processDNSResult( - ZT_Node *node, - void *tptr, - uintptr_t dnsRequestID, - const char *name, - enum ZT_DNSRecordType recordType, - const void *result, - unsigned int resultLength, - int resultIsString); - /** * Join a network * @@ -1884,36 +1545,25 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tpt ZT_SDK_API enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); /** - * List roots for this node + * Add a root server (has no effect if already added) * * @param node Node instance - * @param now Current time - * @return List of roots, use ZT_Node_freeQueryResult to free this when done + * @param identity Identity of this root server in string format + * @return OK (0) or error code if a fatal error condition has occurred */ -ZT_SDK_API ZT_RootList *ZT_Node_listRoots(ZT_Node *node,int64_t now); +ZT_SDK_API enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,const char *identity); /** - * Add or update a root + * Remove a root server * - * The node will begin trying to resolve the DNS TXT record for - * this root and possibly obtain it from other peers. + * This removes this node's root designation but does not prevent this node + * from communicating with it or close active paths to it. * * @param node Node instance - * @param name DNS name or simply the address in hex form for static roots - * @param locator Binary-serialized locator of NULL if none - * @param locatorSize Size of locator or 0 if none - * @return OK (0) or error code + * @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_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 - */ -ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *name); +ZT_SDK_API enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *identity); /** * Get this node's 40-bit ZeroTier address @@ -2035,6 +1685,115 @@ ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterIns */ ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig); +/** + * Generate a new identity + * + * Due to a small amount of proof of work this can be a time consuming and CPU + * intensive operation. It takes less than a second on most desktop-class systems + * but can take longer on e.g. phones. + * + * @param type Type of identity to generate + * @return New identity or NULL on error + */ +ZT_SDK_API ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type); + +/** + * Create a new identity object from a string-serialized identity + * + * @param idStr Identity in string format + * @return Identity object or NULL if the supplied identity string was not valid + */ +ZT_SDK_API ZT_Identity *ZT_Identity_fromString(const char *idStr); + +/** + * Validate this identity + * + * This can be slightly time consuming due to address derivation (work) checking. + * + * @return Non-zero if identity is valid + */ +ZT_SDK_API int ZT_Identity_validate(const ZT_Identity *id); + +/** + * Sign a data object with this identity + * + * The identity must have a private key or this will fail. + * + * @param id Identity to use to sign + * @param data Data to sign + * @param len Length of data + * @param signature Buffer to store signature + * @param signatureBufferLength Length of buffer (must be at least 96 bytes) + * @return Length of signature in bytes or 0 on failure. + */ +ZT_SDK_API unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength); + +/** + * Verify a signature + * + * @param id Identity to use to verify + * @param data Data to verify + * @param len Length of data + * @param signature Signature to check + * @param sigLen Length of signature in bytes + * @return Non-zero if signature is valid + */ +ZT_SDK_API int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen); + +/** + * Get identity type + * + * @param id Identity to query + * @return Identity type code + */ +ZT_SDK_API enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id); + +/** + * Convert an identity to its string representation + * + * @param id Identity to convert + * @param buf Buffer to store identity (should be at least about 1024 bytes in length) + * @param capacity Capacity of buffer + * @param includePrivate If true include the private key if present + * @return Pointer to buf or NULL on overflow or other error + */ +ZT_SDK_API char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate); + +/** + * Check whether this identity object also holds a private key + * + * @param id Identity to query + * @return Non-zero if a private key is held + */ +ZT_SDK_API int ZT_Identity_hasPrivate(const ZT_Identity *id); + +/** + * Get the ZeroTier address associated with this identity + * + * @param id Identity to query + * @return ZeroTier address (only least significant 40 bits are meaningful, rest will be 0) + */ +ZT_SDK_API uint64_t ZT_Identity_address(const ZT_Identity *id); + +/** + * Compute a hash of this identity's public keys (or both public and private if includePrivate is true) + * + * @param id Identity to query + * @param h Buffer for 384-bit hash + * @param includePrivate If true include private keys if any + */ +ZT_SDK_API void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate); + +/** + * Delete an identity and free associated memory + * + * This should only be used with identities created via Identity_new + * and Identity_fromString(). + * + * @param id Identity to delete + */ +ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id); + /** * Get ZeroTier One version * diff --git a/node/AES-aesni.c b/node/AES-aesni.c deleted file mode 100644 index d89765773..000000000 --- a/node/AES-aesni.c +++ /dev/null @@ -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 -#include -#include -#include - -#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 @@ -25,7 +21,7 @@ namespace ZeroTier { #ifdef ZT_NO_TYPE_PUNNING -static ZT_ALWAYS_INLINE uint32_t readuint32_t(const void *in) +static inline uint32_t readuint32_t(const void *in) { uint32_t v = ((const uint8_t *)in)[0]; v <<= 8; @@ -36,7 +32,7 @@ static ZT_ALWAYS_INLINE uint32_t readuint32_t(const void *in) v |= ((const uint8_t *)in)[3]; return v; } -static ZT_ALWAYS_INLINE void writeuint32_t(void *out,const uint32_t v) +static inline void writeuint32_t(void *out,const uint32_t v) { ((uint8_t *)out)[0] = (uint8_t)(v >> 24); ((uint8_t *)out)[1] = (uint8_t)(v >> 16); @@ -48,28 +44,6 @@ static ZT_ALWAYS_INLINE void writeuint32_t(void *out,const uint32_t v) #define writeuint32_t(o,v) (*((uint32_t *)(o)) = Utils::hton(v)) #endif -#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) -static bool _zt_aesni_supported() -{ -#ifdef __WINDOWS__ - int regs[4]; - __cpuid(regs,1); - return ( (((regs[2] >> 25) & 1) != 0) && (((regs[2] >> 19) & 1) != 0) && (((regs[2] >> 1) & 1) != 0) ); // AES-NI, SSE4.1, PCLMUL -#else - uint32_t eax,ebx,ecx,edx; - __asm__ __volatile__ ( - "cpuid" - : "=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx) - : "a"(1),"c"(0) - ); - return ( ((ecx & (1 << 25)) != 0) && ((ecx & (1 << 19)) != 0) && ((ecx & (1 << 1)) != 0) ); // AES-NI, SSE4.1, PCLMUL -#endif -} -const bool AES::HW_ACCEL = _zt_aesni_supported(); -#else -const bool AES::HW_ACCEL = false; -#endif - const uint32_t AES::Te0[256] = { 0xc66363a5,0xf87c7c84,0xee777799,0xf67b7b8d,0xfff2f20d,0xd66b6bbd,0xde6f6fb1,0x91c5c554,0x60303050,0x02010103,0xce6767a9,0x562b2b7d,0xe7fefe19,0xb5d7d762,0x4dababe6,0xec76769a,0x8fcaca45,0x1f82829d,0x89c9c940,0xfa7d7d87,0xeffafa15,0xb25959eb,0x8e4747c9,0xfbf0f00b,0x41adadec,0xb3d4d467,0x5fa2a2fd,0x45afafea,0x239c9cbf,0x53a4a4f7,0xe4727296,0x9bc0c05b,0x75b7b7c2,0xe1fdfd1c,0x3d9393ae,0x4c26266a,0x6c36365a,0x7e3f3f41,0xf5f7f702,0x83cccc4f,0x6834345c,0x51a5a5f4,0xd1e5e534,0xf9f1f108,0xe2717193,0xabd8d873,0x62313153,0x2a15153f,0x0804040c,0x95c7c752,0x46232365,0x9dc3c35e,0x30181828,0x379696a1,0x0a05050f,0x2f9a9ab5,0x0e070709,0x24121236,0x1b80809b,0xdfe2e23d,0xcdebeb26,0x4e272769,0x7fb2b2cd,0xea75759f,0x1209091b,0x1d83839e,0x582c2c74,0x341a1a2e,0x361b1b2d,0xdc6e6eb2,0xb45a5aee,0x5ba0a0fb,0xa45252f6,0x763b3b4d,0xb7d6d661,0x7db3b3ce,0x5229297b,0xdde3e33e,0x5e2f2f71,0x13848497,0xa65353f5,0xb9d1d168,0x00000000,0xc1eded2c,0x40202060,0xe3fcfc1f,0x79b1b1c8,0xb65b5bed,0xd46a6abe,0x8dcbcb46,0x67bebed9,0x7239394b,0x944a4ade,0x984c4cd4,0xb05858e8,0x85cfcf4a,0xbbd0d06b,0xc5efef2a,0x4faaaae5,0xedfbfb16,0x864343c5,0x9a4d4dd7,0x66333355,0x11858594,0x8a4545cf,0xe9f9f910,0x04020206,0xfe7f7f81,0xa05050f0,0x783c3c44,0x259f9fba,0x4ba8a8e3,0xa25151f3,0x5da3a3fe,0x804040c0,0x058f8f8a,0x3f9292ad,0x219d9dbc,0x70383848,0xf1f5f504,0x63bcbcdf,0x77b6b6c1,0xafdada75,0x42212163,0x20101030,0xe5ffff1a,0xfdf3f30e,0xbfd2d26d,0x81cdcd4c,0x180c0c14,0x26131335,0xc3ecec2f,0xbe5f5fe1,0x359797a2,0x884444cc,0x2e171739,0x93c4c457,0x55a7a7f2,0xfc7e7e82,0x7a3d3d47,0xc86464ac,0xba5d5de7,0x3219192b,0xe6737395,0xc06060a0,0x19818198,0x9e4f4fd1,0xa3dcdc7f,0x44222266,0x542a2a7e,0x3b9090ab,0x0b888883,0x8c4646ca,0xc7eeee29,0x6bb8b8d3,0x2814143c,0xa7dede79,0xbc5e5ee2,0x160b0b1d,0xaddbdb76,0xdbe0e03b,0x64323256,0x743a3a4e,0x140a0a1e,0x924949db,0x0c06060a,0x4824246c,0xb85c5ce4,0x9fc2c25d,0xbdd3d36e,0x43acacef,0xc46262a6,0x399191a8,0x319595a4,0xd3e4e437,0xf279798b,0xd5e7e732,0x8bc8c843,0x6e373759,0xda6d6db7,0x018d8d8c,0xb1d5d564,0x9c4e4ed2,0x49a9a9e0,0xd86c6cb4,0xac5656fa,0xf3f4f407,0xcfeaea25,0xca6565af,0xf47a7a8e,0x47aeaee9,0x10080818,0x6fbabad5,0xf0787888,0x4a25256f,0x5c2e2e72,0x381c1c24,0x57a6a6f1,0x73b4b4c7,0x97c6c651,0xcbe8e823,0xa1dddd7c,0xe874749c,0x3e1f1f21,0x964b4bdd,0x61bdbddc,0x0d8b8b86,0x0f8a8a85,0xe0707090,0x7c3e3e42,0x71b5b5c4,0xcc6666aa,0x904848d8,0x06030305,0xf7f6f601,0x1c0e0e12,0xc26161a3,0x6a35355f,0xae5757f9,0x69b9b9d0,0x17868691,0x99c1c158,0x3a1d1d27,0x279e9eb9,0xd9e1e138,0xebf8f813,0x2b9898b3,0x22111133,0xd26969bb,0xa9d9d970,0x078e8e89,0x339494a7,0x2d9b9bb6,0x3c1e1e22,0x15878792,0xc9e9e920,0x87cece49,0xaa5555ff,0x50282878,0xa5dfdf7a,0x038c8c8f,0x59a1a1f8,0x09898980,0x1a0d0d17,0x65bfbfda,0xd7e6e631,0x844242c6,0xd06868b8,0x824141c3,0x299999b0,0x5a2d2d77,0x1e0f0f11,0x7bb0b0cb,0xa85454fc,0x6dbbbbd6,0x2c16163a }; const uint32_t AES::Te1[256] = { 0xa5c66363,0x84f87c7c,0x99ee7777,0x8df67b7b,0x0dfff2f2,0xbdd66b6b,0xb1de6f6f,0x5491c5c5,0x50603030,0x03020101,0xa9ce6767,0x7d562b2b,0x19e7fefe,0x62b5d7d7,0xe64dabab,0x9aec7676,0x458fcaca,0x9d1f8282,0x4089c9c9,0x87fa7d7d,0x15effafa,0xebb25959,0xc98e4747,0x0bfbf0f0,0xec41adad,0x67b3d4d4,0xfd5fa2a2,0xea45afaf,0xbf239c9c,0xf753a4a4,0x96e47272,0x5b9bc0c0,0xc275b7b7,0x1ce1fdfd,0xae3d9393,0x6a4c2626,0x5a6c3636,0x417e3f3f,0x02f5f7f7,0x4f83cccc,0x5c683434,0xf451a5a5,0x34d1e5e5,0x08f9f1f1,0x93e27171,0x73abd8d8,0x53623131,0x3f2a1515,0x0c080404,0x5295c7c7,0x65462323,0x5e9dc3c3,0x28301818,0xa1379696,0x0f0a0505,0xb52f9a9a,0x090e0707,0x36241212,0x9b1b8080,0x3ddfe2e2,0x26cdebeb,0x694e2727,0xcd7fb2b2,0x9fea7575,0x1b120909,0x9e1d8383,0x74582c2c,0x2e341a1a,0x2d361b1b,0xb2dc6e6e,0xeeb45a5a,0xfb5ba0a0,0xf6a45252,0x4d763b3b,0x61b7d6d6,0xce7db3b3,0x7b522929,0x3edde3e3,0x715e2f2f,0x97138484,0xf5a65353,0x68b9d1d1,0x00000000,0x2cc1eded,0x60402020,0x1fe3fcfc,0xc879b1b1,0xedb65b5b,0xbed46a6a,0x468dcbcb,0xd967bebe,0x4b723939,0xde944a4a,0xd4984c4c,0xe8b05858,0x4a85cfcf,0x6bbbd0d0,0x2ac5efef,0xe54faaaa,0x16edfbfb,0xc5864343,0xd79a4d4d,0x55663333,0x94118585,0xcf8a4545,0x10e9f9f9,0x06040202,0x81fe7f7f,0xf0a05050,0x44783c3c,0xba259f9f,0xe34ba8a8,0xf3a25151,0xfe5da3a3,0xc0804040,0x8a058f8f,0xad3f9292,0xbc219d9d,0x48703838,0x04f1f5f5,0xdf63bcbc,0xc177b6b6,0x75afdada,0x63422121,0x30201010,0x1ae5ffff,0x0efdf3f3,0x6dbfd2d2,0x4c81cdcd,0x14180c0c,0x35261313,0x2fc3ecec,0xe1be5f5f,0xa2359797,0xcc884444,0x392e1717,0x5793c4c4,0xf255a7a7,0x82fc7e7e,0x477a3d3d,0xacc86464,0xe7ba5d5d,0x2b321919,0x95e67373,0xa0c06060,0x98198181,0xd19e4f4f,0x7fa3dcdc,0x66442222,0x7e542a2a,0xab3b9090,0x830b8888,0xca8c4646,0x29c7eeee,0xd36bb8b8,0x3c281414,0x79a7dede,0xe2bc5e5e,0x1d160b0b,0x76addbdb,0x3bdbe0e0,0x56643232,0x4e743a3a,0x1e140a0a,0xdb924949,0x0a0c0606,0x6c482424,0xe4b85c5c,0x5d9fc2c2,0x6ebdd3d3,0xef43acac,0xa6c46262,0xa8399191,0xa4319595,0x37d3e4e4,0x8bf27979,0x32d5e7e7,0x438bc8c8,0x596e3737,0xb7da6d6d,0x8c018d8d,0x64b1d5d5,0xd29c4e4e,0xe049a9a9,0xb4d86c6c,0xfaac5656,0x07f3f4f4,0x25cfeaea,0xafca6565,0x8ef47a7a,0xe947aeae,0x18100808,0xd56fbaba,0x88f07878,0x6f4a2525,0x725c2e2e,0x24381c1c,0xf157a6a6,0xc773b4b4,0x5197c6c6,0x23cbe8e8,0x7ca1dddd,0x9ce87474,0x213e1f1f,0xdd964b4b,0xdc61bdbd,0x860d8b8b,0x850f8a8a,0x90e07070,0x427c3e3e,0xc471b5b5,0xaacc6666,0xd8904848,0x05060303,0x01f7f6f6,0x121c0e0e,0xa3c26161,0x5f6a3535,0xf9ae5757,0xd069b9b9,0x91178686,0x5899c1c1,0x273a1d1d,0xb9279e9e,0x38d9e1e1,0x13ebf8f8,0xb32b9898,0x33221111,0xbbd26969,0x70a9d9d9,0x89078e8e,0xa7339494,0xb62d9b9b,0x223c1e1e,0x92158787,0x20c9e9e9,0x4987cece,0xffaa5555,0x78502828,0x7aa5dfdf,0x8f038c8c,0xf859a1a1,0x80098989,0x171a0d0d,0xda65bfbf,0x31d7e6e6,0xc6844242,0xb8d06868,0xc3824141,0xb0299999,0x775a2d2d,0x111e0f0f,0xcb7bb0b0,0xfca85454,0xd66dbbbb,0x3a2c1616 }; const uint32_t AES::Te2[256] = { 0x63a5c663,0x7c84f87c,0x7799ee77,0x7b8df67b,0xf20dfff2,0x6bbdd66b,0x6fb1de6f,0xc55491c5,0x30506030,0x01030201,0x67a9ce67,0x2b7d562b,0xfe19e7fe,0xd762b5d7,0xabe64dab,0x769aec76,0xca458fca,0x829d1f82,0xc94089c9,0x7d87fa7d,0xfa15effa,0x59ebb259,0x47c98e47,0xf00bfbf0,0xadec41ad,0xd467b3d4,0xa2fd5fa2,0xafea45af,0x9cbf239c,0xa4f753a4,0x7296e472,0xc05b9bc0,0xb7c275b7,0xfd1ce1fd,0x93ae3d93,0x266a4c26,0x365a6c36,0x3f417e3f,0xf702f5f7,0xcc4f83cc,0x345c6834,0xa5f451a5,0xe534d1e5,0xf108f9f1,0x7193e271,0xd873abd8,0x31536231,0x153f2a15,0x040c0804,0xc75295c7,0x23654623,0xc35e9dc3,0x18283018,0x96a13796,0x050f0a05,0x9ab52f9a,0x07090e07,0x12362412,0x809b1b80,0xe23ddfe2,0xeb26cdeb,0x27694e27,0xb2cd7fb2,0x759fea75,0x091b1209,0x839e1d83,0x2c74582c,0x1a2e341a,0x1b2d361b,0x6eb2dc6e,0x5aeeb45a,0xa0fb5ba0,0x52f6a452,0x3b4d763b,0xd661b7d6,0xb3ce7db3,0x297b5229,0xe33edde3,0x2f715e2f,0x84971384,0x53f5a653,0xd168b9d1,0x00000000,0xed2cc1ed,0x20604020,0xfc1fe3fc,0xb1c879b1,0x5bedb65b,0x6abed46a,0xcb468dcb,0xbed967be,0x394b7239,0x4ade944a,0x4cd4984c,0x58e8b058,0xcf4a85cf,0xd06bbbd0,0xef2ac5ef,0xaae54faa,0xfb16edfb,0x43c58643,0x4dd79a4d,0x33556633,0x85941185,0x45cf8a45,0xf910e9f9,0x02060402,0x7f81fe7f,0x50f0a050,0x3c44783c,0x9fba259f,0xa8e34ba8,0x51f3a251,0xa3fe5da3,0x40c08040,0x8f8a058f,0x92ad3f92,0x9dbc219d,0x38487038,0xf504f1f5,0xbcdf63bc,0xb6c177b6,0xda75afda,0x21634221,0x10302010,0xff1ae5ff,0xf30efdf3,0xd26dbfd2,0xcd4c81cd,0x0c14180c,0x13352613,0xec2fc3ec,0x5fe1be5f,0x97a23597,0x44cc8844,0x17392e17,0xc45793c4,0xa7f255a7,0x7e82fc7e,0x3d477a3d,0x64acc864,0x5de7ba5d,0x192b3219,0x7395e673,0x60a0c060,0x81981981,0x4fd19e4f,0xdc7fa3dc,0x22664422,0x2a7e542a,0x90ab3b90,0x88830b88,0x46ca8c46,0xee29c7ee,0xb8d36bb8,0x143c2814,0xde79a7de,0x5ee2bc5e,0x0b1d160b,0xdb76addb,0xe03bdbe0,0x32566432,0x3a4e743a,0x0a1e140a,0x49db9249,0x060a0c06,0x246c4824,0x5ce4b85c,0xc25d9fc2,0xd36ebdd3,0xacef43ac,0x62a6c462,0x91a83991,0x95a43195,0xe437d3e4,0x798bf279,0xe732d5e7,0xc8438bc8,0x37596e37,0x6db7da6d,0x8d8c018d,0xd564b1d5,0x4ed29c4e,0xa9e049a9,0x6cb4d86c,0x56faac56,0xf407f3f4,0xea25cfea,0x65afca65,0x7a8ef47a,0xaee947ae,0x08181008,0xbad56fba,0x7888f078,0x256f4a25,0x2e725c2e,0x1c24381c,0xa6f157a6,0xb4c773b4,0xc65197c6,0xe823cbe8,0xdd7ca1dd,0x749ce874,0x1f213e1f,0x4bdd964b,0xbddc61bd,0x8b860d8b,0x8a850f8a,0x7090e070,0x3e427c3e,0xb5c471b5,0x66aacc66,0x48d89048,0x03050603,0xf601f7f6,0x0e121c0e,0x61a3c261,0x355f6a35,0x57f9ae57,0xb9d069b9,0x86911786,0xc15899c1,0x1d273a1d,0x9eb9279e,0xe138d9e1,0xf813ebf8,0x98b32b98,0x11332211,0x69bbd269,0xd970a9d9,0x8e89078e,0x94a73394,0x9bb62d9b,0x1e223c1e,0x87921587,0xe920c9e9,0xce4987ce,0x55ffaa55,0x28785028,0xdf7aa5df,0x8c8f038c,0xa1f859a1,0x89800989,0x0d171a0d,0xbfda65bf,0xe631d7e6,0x42c68442,0x68b8d068,0x41c38241,0x99b02999,0x2d775a2d,0x0f111e0f,0xb0cb7bb0,0x54fca854,0xbbd66dbb,0x163a2c16 }; @@ -175,6 +149,39 @@ void AES::_encryptSW(const uint8_t in[16],uint8_t out[16]) const writeuint32_t(out + 12,(Te2[(t3 >> 24)] & 0xff000000) ^ (Te3[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te0[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te1[(t2) & 0xff] & 0x000000ff) ^ rk[59]); } +void AES::_ctrSW(const uint8_t iv[16],const void *in,unsigned int len,void *out) const +{ + 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> 32); uint32_t h_high_l = (uint32_t)h_high; @@ -337,9 +344,8 @@ void AES::_gmacSW(const uint8_t iv[12],const uint8_t *in,unsigned int len,uint8_ for(unsigned int i=0;i<8;++i) ((uint8_t *)&y1)[i] ^= *(in++); #else y0 ^= *((const uint64_t *)in); - in += 8; - y1 ^= *((const uint64_t *)in); - in += 8; + y1 ^= *((const uint64_t *)(in + 8)); + in += 16; #endif s_gfmul(h0,h1,y0,y1); len -= 16; diff --git a/node/AES.hpp b/node/AES.hpp index 15f8077c9..a1f5661ef 100644 --- a/node/AES.hpp +++ b/node/AES.hpp @@ -18,35 +18,24 @@ #include "Utils.hpp" #include "SHA512.hpp" -#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +#include +#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +#include #include #include #include - #define ZT_AES_AESNI 1 - -// AES-aesni.c -extern "C" void zt_crypt_ctr_aesni(const __m128i key[14],const uint8_t iv[16],const uint8_t *in,unsigned int len,uint8_t *out); - -#endif // x64 - -#define ZT_AES_KEY_SIZE 32 -#define ZT_AES_BLOCK_SIZE 16 +#endif namespace ZeroTier { /** - * AES-256 and pals + * AES-256 and pals including GMAC, CTR, etc. */ class AES { public: - /** - * This will be true if your platform's type of AES acceleration is supported on this machine - */ - static const bool HW_ACCEL; - ZT_ALWAYS_INLINE AES() {} ZT_ALWAYS_INLINE AES(const uint8_t key[32]) { this->init(key); } ZT_ALWAYS_INLINE ~AES() { Utils::burn(&_k,sizeof(_k)); } @@ -57,12 +46,11 @@ public: ZT_ALWAYS_INLINE void init(const uint8_t key[32]) { #ifdef ZT_AES_AESNI - if (likely(HW_ACCEL)) { + if (likely(Utils::CPUID.aes)) { _init_aesni(key); return; } #endif - _initSW(key); } @@ -75,12 +63,11 @@ public: ZT_ALWAYS_INLINE void encrypt(const uint8_t in[16],uint8_t out[16]) const { #ifdef ZT_AES_AESNI - if (likely(HW_ACCEL)) { + if (likely(Utils::CPUID.aes)) { _encrypt_aesni(in,out); return; } #endif - _encryptSW(in,out); } @@ -95,12 +82,11 @@ public: ZT_ALWAYS_INLINE void gmac(const uint8_t iv[12],const void *in,const unsigned int len,uint8_t out[16]) const { #ifdef ZT_AES_AESNI - if (likely(HW_ACCEL)) { + if (likely(Utils::CPUID.aes)) { _gmac_aesni(iv,(const uint8_t *)in,len,out); return; } #endif - _gmacSW(iv,(const uint8_t *)in,len,out); } @@ -119,41 +105,12 @@ public: ZT_ALWAYS_INLINE void ctr(const uint8_t iv[16],const void *in,unsigned int len,void *out) const { #ifdef ZT_AES_AESNI - if (likely(HW_ACCEL)) { - zt_crypt_ctr_aesni(_k.ni.k,iv,(const uint8_t *)in,len,(uint8_t *)out); + if (likely(Utils::CPUID.aes)) { + _ctr_aesni(_k.ni.k,iv,(const uint8_t *)in,len,(uint8_t *)out); return; } #endif - - uint64_t ctr[2],cenc[2]; - memcpy(ctr,iv,16); - uint64_t bctr = Utils::ntoh(ctr[1]); - - const uint8_t *i = (const uint8_t *)in; - uint8_t *o = (uint8_t *)out; - - while (len >= 16) { - _encryptSW((const uint8_t *)ctr,(uint8_t *)cenc); - ctr[1] = Utils::hton(++bctr); -#ifdef ZT_NO_TYPE_PUNNING - for(unsigned int k=0;k<16;++k) - *(o++) = *(i++) ^ ((uint8_t *)cenc)[k]; -#else - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[0]; - o += 8; - i += 8; - *((uint64_t *)o) = *((const uint64_t *)i) ^ cenc[1]; - o += 8; - i += 8; -#endif - len -= 16; - } - - if (len) { - _encryptSW((const uint8_t *)ctr,(uint8_t *)cenc); - for(unsigned int k=0;k= 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 -#include -#include -#include +#include +#include +#include +#include +#include #include +#include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -34,8 +37,7 @@ class Address { public: ZT_ALWAYS_INLINE Address() : _a(0) {} - ZT_ALWAYS_INLINE Address(const Address &a) : _a(a._a) {} - ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {} + explicit ZT_ALWAYS_INLINE Address(uint64_t a) : _a(a & 0xffffffffffULL) {} /** * @param bits Raw address -- 5 bytes, big-endian byte order @@ -43,7 +45,6 @@ public: */ ZT_ALWAYS_INLINE Address(const void *bits,unsigned int len) { setTo(bits,len); } - ZT_ALWAYS_INLINE Address &operator=(const Address &a) { _a = a._a; return *this; } ZT_ALWAYS_INLINE Address &operator=(const uint64_t a) { _a = (a & 0xffffffffffULL); return *this; } /** @@ -105,18 +106,13 @@ public: /** * @return Hash code for use with Hashtable */ - ZT_ALWAYS_INLINE unsigned long hashCode() const { return reinterpret_cast(_a); } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_a; } /** * @return Hexadecimal string */ ZT_ALWAYS_INLINE char *toString(char buf[11]) const { return Utils::hex10(_a,buf); } - /** - * @return True if this address is not zero - */ - ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); } - /** * Check if this address is reserved * @@ -126,7 +122,7 @@ public: * * @return True if address is reserved and may not be used */ - ZT_ALWAYS_INLINE bool isReserved() const { return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX)); } + ZT_ALWAYS_INLINE bool isReserved() const { return ((!_a)||((_a >> 32U) == ZT_ADDRESS_RESERVED_PREFIX)); } /** * @param i Value from 0 to 4 (inclusive) @@ -134,9 +130,10 @@ public: */ ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); } - ZT_ALWAYS_INLINE operator unsigned int() const { return reinterpret_cast(_a); } - ZT_ALWAYS_INLINE operator unsigned long() const { return reinterpret_cast(_a); } - ZT_ALWAYS_INLINE operator unsigned long long() const { return reinterpret_cast(_a); } + ZT_ALWAYS_INLINE operator bool() const { return (_a != 0); } + ZT_ALWAYS_INLINE operator unsigned int() const { return (unsigned int)_a; } + ZT_ALWAYS_INLINE operator unsigned long() const { return (unsigned long)_a; } + ZT_ALWAYS_INLINE operator unsigned long long() const { return (unsigned long long)_a; } ZT_ALWAYS_INLINE void zero() { _a = 0; } @@ -154,6 +151,90 @@ public: ZT_ALWAYS_INLINE bool operator>=(const Address &a) const { return (_a >= a._a); } ZT_ALWAYS_INLINE bool operator<=(const Address &a) const { return (_a <= a._a); } +#if 0 + /** + * Create a list of the first N bits of a list of unique addresses with N as the minimum unique size + * + * The list is stored in a space-efficient packed bit format. + * + * @param start Starting Address iterator/pointer + * @param end Ending Address iterator/pointer + * @param list Pointer to location to write list + * @param listCapacityBytes Number of bytes available for list + * @return Number of bytes written or -1 on overflow or other error + * @tparam I Input iterator type + */ + template + static inline int createMinPrefixList(I start,I end,uint8_t *list,const int listCapacityBytes) + { + std::vector
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
::iterator a(sortedAddrs.begin()); + uint64_t aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + uint64_t lastMaskedAddress = aa; + while (a != sortedAddrs.end()) { + aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + if (aa == lastMaskedAddress) { + ++bits; + goto try_additional_bits; + } + lastMaskedAddress = aa; + } + } + + int l = 0; + unsigned int bitPtr = 0; + for(I a(start);a!=end;) { + uint64_t aa = *(a++) & mask; + aa |= (uint64_t)(aa == 0); + unsigned int br = bits; + if (bitPtr > 0) { + unsigned int w = 8 - bitPtr; + if (w > br) w = br; + list[l] = (list[l] << w) | (((uint8_t)aa) & (0xff >> (8 - w))); + bitPtr += w; + if (bitPtr == 8) { + bitPtr = 0; + if (l >= listCapacityBytes) + return -1; + ++l; + } + aa >>= w; + br -= w; + } + while (br >= 8) { + if (l >= listCapacityBytes) + return -1; + list[l++] = (uint8_t)aa; + br -= 8; + aa >>= 8; + } + if (br > 0) { + list[l] = (uint8_t)aa; + bitPtr = br; + } + } + if (bitPtr > 0) { + if (l >= listCapacityBytes) + return -1; + ++l; + } + + return l; + } +#endif + private: uint64_t _a; }; diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp index 4e6b95315..6bfc7025d 100644 --- a/node/AtomicCounter.hpp +++ b/node/AtomicCounter.hpp @@ -24,25 +24,30 @@ namespace ZeroTier { /** * Simple atomic counter supporting increment and decrement + * + * This is used as the reference counter in reference counted objects that + * work with SharedPtr<>. */ class AtomicCounter { public: - ZT_ALWAYS_INLINE AtomicCounter() { _v = 0; } + ZT_ALWAYS_INLINE AtomicCounter() : _v(0) {} ZT_ALWAYS_INLINE int load() const { #ifdef __GNUC__ - return __sync_or_and_fetch(const_cast(&_v),0); + return _v; #else return _v.load(); #endif } + ZT_ALWAYS_INLINE void zero() { _v = 0; } + ZT_ALWAYS_INLINE int operator++() { #ifdef __GNUC__ - return __sync_add_and_fetch(&_v,1); + return __sync_add_and_fetch((int *)&_v,1); #else return ++_v; #endif @@ -51,7 +56,7 @@ public: ZT_ALWAYS_INLINE int operator--() { #ifdef __GNUC__ - return __sync_sub_and_fetch(&_v,1); + return __sync_sub_and_fetch((int *)&_v,1); #else return --_v; #endif @@ -62,7 +67,7 @@ private: ZT_ALWAYS_INLINE const AtomicCounter &operator=(const AtomicCounter &) { return *this; } #ifdef __GNUC__ - int _v; + volatile int _v; #else std::atomic_int _v; #endif diff --git a/node/Buf.cpp b/node/Buf.cpp new file mode 100644 index 000000000..80df63c73 --- /dev/null +++ b/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 +#endif + +namespace ZeroTier { + +#ifdef __GNUC__ +static uintptr_t s_pool = 0; +#else +static std::atomic 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::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(); + } 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(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 diff --git a/node/Buf.hpp b/node/Buf.hpp new file mode 100644 index 000000000..a3059f023 --- /dev/null +++ b/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 +#include +#include + +// 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; + +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 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(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(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(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 + 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(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(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(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 + 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 diff --git a/node/C25519.cpp b/node/C25519.cpp index f14875b7a..3e05859c3 100644 --- a/node/C25519.cpp +++ b/node/C25519.cpp @@ -7,16 +7,11 @@ Derived from public domain code by D. J. Bernstein. // Modified very slightly for ZeroTier One by Adam Ierymenko // This code remains in the public domain. -#include -#include -#include +#include +#include -#include "Constants.hpp" #include "C25519.hpp" #include "SHA512.hpp" -#include "Buffer.hpp" -#include "Hashtable.hpp" -#include "Mutex.hpp" #ifdef __WINDOWS__ #pragma warning(disable: 4146) @@ -28,10 +23,7 @@ Derived from public domain code by D. J. Bernstein. namespace { -#define crypto_int32 int32_t #define crypto_uint32 uint32_t -#define crypto_int64 int64_t -#define crypto_uint64 uint64_t #define crypto_hash_sha512_BYTES 64 ////////////////////////////////////////////////////////////////////////////// @@ -41,7 +33,7 @@ typedef uint8_t u8; typedef int32_t s32; typedef int64_t limb; -static ZT_ALWAYS_INLINE void fsum(limb *output, const limb *in) { +static inline void fsum(limb *output, const limb *in) { unsigned i; for (i = 0; i < 10; i += 2) { output[0+i] = output[0+i] + in[0+i]; @@ -49,21 +41,21 @@ static ZT_ALWAYS_INLINE void fsum(limb *output, const limb *in) { } } -static ZT_ALWAYS_INLINE void fdifference(limb *output, const limb *in) { +static inline void fdifference(limb *output, const limb *in) { unsigned i; for (i = 0; i < 10; ++i) { output[i] = in[i] - output[i]; } } -static ZT_ALWAYS_INLINE void fscalar_product(limb *output, const limb *in, const limb scalar) { +static inline void fscalar_product(limb *output, const limb *in, const limb scalar) { unsigned i; for (i = 0; i < 10; ++i) { output[i] = in[i] * scalar; } } -static ZT_ALWAYS_INLINE void fproduct(limb *output, const limb *in2, const limb *in) { +static inline void fproduct(limb *output, const limb *in2, const limb *in) { output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]); output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) + ((limb) ((s32) in2[1])) * ((s32) in[0]); @@ -166,7 +158,7 @@ static ZT_ALWAYS_INLINE void fproduct(limb *output, const limb *in2, const limb output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]); } -static ZT_ALWAYS_INLINE void freduce_degree(limb *output) { +static inline void freduce_degree(limb *output) { output[8] += output[18] << 4; output[8] += output[18] << 1; output[8] += output[18]; @@ -200,7 +192,7 @@ static ZT_ALWAYS_INLINE void freduce_degree(limb *output) { #error "This code only works on a two's complement system" #endif -static ZT_ALWAYS_INLINE limb div_by_2_26(const limb v) +static inline limb div_by_2_26(const limb v) { /* High word of v; no shift needed. */ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); @@ -212,7 +204,7 @@ static ZT_ALWAYS_INLINE limb div_by_2_26(const limb v) return (v + roundoff) >> 26; } -static ZT_ALWAYS_INLINE limb div_by_2_25(const limb v) +static inline limb div_by_2_25(const limb v) { /* High word of v; no shift needed*/ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32); @@ -224,7 +216,7 @@ static ZT_ALWAYS_INLINE limb div_by_2_25(const limb v) return (v + roundoff) >> 25; } -static ZT_ALWAYS_INLINE void freduce_coefficients(limb *output) { +static inline void freduce_coefficients(limb *output) { unsigned i; output[10] = 0; @@ -277,7 +269,7 @@ static inline void fmul(limb *output, const limb *in, const limb *in2) { memcpy(output, t, sizeof(limb) * 10); } -static ZT_ALWAYS_INLINE void fsquare_inner(limb *output, const limb *in) { +static inline void fsquare_inner(limb *output, const limb *in) { output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]); output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]); output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) + @@ -347,7 +339,7 @@ static inline void fsquare(limb *output, const limb *in) { memcpy(output, t, sizeof(limb) * 10); } -static ZT_ALWAYS_INLINE void fexpand(limb *output, const u8 *input) { +static inline void fexpand(limb *output, const u8 *input) { #define F(n,start,shift,mask) \ output[n] = ((((limb) input[start + 0]) | \ ((limb) input[start + 1]) << 8 | \ @@ -370,7 +362,7 @@ static ZT_ALWAYS_INLINE void fexpand(limb *output, const u8 *input) { #error "This code only works when >> does sign-extension on negative numbers" #endif -static ZT_ALWAYS_INLINE s32 s32_eq(s32 a, s32 b) { +static inline s32 s32_eq(s32 a, s32 b) { a = ~(a ^ b); a &= a << 16; a &= a << 8; @@ -380,7 +372,7 @@ static ZT_ALWAYS_INLINE s32 s32_eq(s32 a, s32 b) { return a >> 31; } -static ZT_ALWAYS_INLINE s32 s32_gte(s32 a, s32 b) { +static inline s32 s32_gte(s32 a, s32 b) { a -= b; /* a >= 0 iff a >= b. */ return ~(a >> 31); @@ -560,7 +552,7 @@ static inline void fmonty(limb *x2, limb *z2, /* output 2Q */ /* |z2|i| < 2^26 */ } -static ZT_ALWAYS_INLINE void swap_conditional(limb a[19], limb b[19], limb iswap) { +static inline void swap_conditional(limb a[19], limb b[19], limb iswap) { unsigned i; const s32 swap = (s32) -iswap; @@ -701,7 +693,7 @@ static inline void crypto_scalarmult(u8 *mypublic, const u8 *secret, const u8 *b } static const unsigned char base[32] = {9}; -static ZT_ALWAYS_INLINE void crypto_scalarmult_base(unsigned char *q,const unsigned char *n) { crypto_scalarmult(q,n,base); } +static inline void crypto_scalarmult_base(unsigned char *q,const unsigned char *n) { crypto_scalarmult(q,n,base); } ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// @@ -759,7 +751,7 @@ typedef struct static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y); -static ZT_ALWAYS_INLINE crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ +static inline crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ { crypto_uint32 x = a ^ b; /* 0: yes; 1..65535: no */ x -= 1; /* 4294967295: yes; 0..65534: no */ @@ -767,7 +759,7 @@ static ZT_ALWAYS_INLINE crypto_uint32 equal(crypto_uint32 a,crypto_uint32 b) /* return x; } -static ZT_ALWAYS_INLINE crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ +static inline crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ { unsigned int x = a; x -= (unsigned int) b; /* 0..65535: yes; 4294901761..4294967295: no */ @@ -776,10 +768,10 @@ static ZT_ALWAYS_INLINE crypto_uint32 ge(crypto_uint32 a,crypto_uint32 b) /* 16- return x; } -static ZT_ALWAYS_INLINE crypto_uint32 times19(crypto_uint32 a) { return (a << 4) + (a << 1) + a; } -static ZT_ALWAYS_INLINE crypto_uint32 times38(crypto_uint32 a) { return (a << 5) + (a << 2) + (a << 1); } +static inline crypto_uint32 times19(crypto_uint32 a) { return (a << 4) + (a << 1) + a; } +static inline crypto_uint32 times38(crypto_uint32 a) { return (a << 5) + (a << 2) + (a << 1); } -static ZT_ALWAYS_INLINE void reduce_add_sub(fe25519 *r) +static inline void reduce_add_sub(fe25519 *r) { int i,rep; for(rep=0;rep<4;rep++) @@ -797,7 +789,7 @@ static ZT_ALWAYS_INLINE void reduce_add_sub(fe25519 *r) } } -static ZT_ALWAYS_INLINE void reduce_mul(fe25519 *r) +static inline void reduce_mul(fe25519 *r) { int i,rep; for(rep=0;rep<2;rep++) @@ -815,7 +807,7 @@ static ZT_ALWAYS_INLINE void reduce_mul(fe25519 *r) } } -static ZT_ALWAYS_INLINE void fe25519_freeze(fe25519 *r) +static inline void fe25519_freeze(fe25519 *r) { int i; crypto_uint32 mm = equal(r->v[31],127); @@ -831,14 +823,14 @@ static ZT_ALWAYS_INLINE void fe25519_freeze(fe25519 *r) r->v[0] -= mm&237; } -static ZT_ALWAYS_INLINE void fe25519_unpack(fe25519 *r, const unsigned char x[32]) +static inline void fe25519_unpack(fe25519 *r, const unsigned char x[32]) { int i; for(i=0;i<32;i++) r->v[i] = x[i]; r->v[31] &= 127; } -static ZT_ALWAYS_INLINE void fe25519_pack(unsigned char r[32], const fe25519 *x) +static inline void fe25519_pack(unsigned char r[32], const fe25519 *x) { int i; fe25519 y = *x; @@ -859,7 +851,7 @@ static inline int fe25519_iseq_vartime(const fe25519 *x, const fe25519 *y) return 1; } -static ZT_ALWAYS_INLINE void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b) +static inline void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned char b) { int i; crypto_uint32 mask = b; @@ -867,27 +859,27 @@ static ZT_ALWAYS_INLINE void fe25519_cmov(fe25519 *r, const fe25519 *x, unsigned for(i=0;i<32;i++) r->v[i] ^= mask & (x->v[i] ^ r->v[i]); } -static ZT_ALWAYS_INLINE unsigned char fe25519_getparity(const fe25519 *x) +static inline unsigned char fe25519_getparity(const fe25519 *x) { fe25519 t = *x; fe25519_freeze(&t); return t.v[0] & 1; } -static ZT_ALWAYS_INLINE void fe25519_setone(fe25519 *r) +static inline void fe25519_setone(fe25519 *r) { int i; r->v[0] = 1; for(i=1;i<32;i++) r->v[i]=0; } -static ZT_ALWAYS_INLINE void fe25519_setzero(fe25519 *r) +static inline void fe25519_setzero(fe25519 *r) { int i; for(i=0;i<32;i++) r->v[i]=0; } -static ZT_ALWAYS_INLINE void fe25519_neg(fe25519 *r, const fe25519 *x) +static inline void fe25519_neg(fe25519 *r, const fe25519 *x) { fe25519 t; int i; @@ -896,14 +888,14 @@ static ZT_ALWAYS_INLINE void fe25519_neg(fe25519 *r, const fe25519 *x) fe25519_sub(r, r, &t); } -static ZT_ALWAYS_INLINE void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y) +static inline void fe25519_add(fe25519 *r, const fe25519 *x, const fe25519 *y) { int i; for(i=0;i<32;i++) r->v[i] = x->v[i] + y->v[i]; reduce_add_sub(r); } -static ZT_ALWAYS_INLINE void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y) +static inline void fe25519_sub(fe25519 *r, const fe25519 *x, const fe25519 *y) { int i; crypto_uint32 t[32]; @@ -914,7 +906,7 @@ static ZT_ALWAYS_INLINE void fe25519_sub(fe25519 *r, const fe25519 *x, const fe2 reduce_add_sub(r); } -static ZT_ALWAYS_INLINE void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y) +static inline void fe25519_mul(fe25519 *r, const fe25519 *x, const fe25519 *y) { int i,j; crypto_uint32 t[63]; @@ -931,7 +923,7 @@ static ZT_ALWAYS_INLINE void fe25519_mul(fe25519 *r, const fe25519 *x, const fe2 reduce_mul(r); } -static ZT_ALWAYS_INLINE void fe25519_square(fe25519 *r, const fe25519 *x) { fe25519_mul(r, x, x); } +static inline void fe25519_square(fe25519 *r, const fe25519 *x) { fe25519_mul(r, x, x); } static inline void fe25519_invert(fe25519 *r, const fe25519 *x) { @@ -1057,7 +1049,7 @@ static inline void fe25519_pow2523(fe25519 *r, const fe25519 *x) static const crypto_uint32 m[32] = {0xED, 0xD3, 0xF5, 0x5C, 0x1A, 0x63, 0x12, 0x58, 0xD6, 0x9C, 0xF7, 0xA2, 0xDE, 0xF9, 0xDE, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; static const crypto_uint32 mu[33] = {0x1B, 0x13, 0x2C, 0x0A, 0xA3, 0xE5, 0x9C, 0xED, 0xA7, 0x29, 0x63, 0x08, 0x5D, 0x21, 0x06, 0x21, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F}; -static ZT_ALWAYS_INLINE crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ +static inline crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16-bit inputs */ { unsigned int x = a; x -= (unsigned int) b; /* 0..65535: no; 4294901761..4294967295: yes */ @@ -1065,7 +1057,7 @@ static ZT_ALWAYS_INLINE crypto_uint32 lt(crypto_uint32 a,crypto_uint32 b) /* 16- return x; } -static ZT_ALWAYS_INLINE void reduce_add_sub(sc25519 *r) +static inline void reduce_add_sub(sc25519 *r) { crypto_uint32 pb = 0; crypto_uint32 b; @@ -1144,7 +1136,7 @@ static inline void sc25519_from64bytes(sc25519 *r, const unsigned char x[64]) barrett_reduce(r, t); } -static ZT_ALWAYS_INLINE void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) +static inline void sc25519_to32bytes(unsigned char r[32], const sc25519 *x) { int i; for(i=0;i<32;i++) r[i] = x->v[i]; @@ -1181,7 +1173,7 @@ static inline void sc25519_mul(sc25519 *r, const sc25519 *x, const sc25519 *y) barrett_reduce(r, t); } -static ZT_ALWAYS_INLINE void sc25519_window3(signed char r[85], const sc25519 *s) +static inline void sc25519_window3(signed char r[85], const sc25519 *s) { char carry; int i; @@ -1218,7 +1210,7 @@ static ZT_ALWAYS_INLINE void sc25519_window3(signed char r[85], const sc25519 *s r[84] += carry; } -static ZT_ALWAYS_INLINE void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2) +static inline void sc25519_2interleave2(unsigned char r[127], const sc25519 *s1, const sc25519 *s2) { int i; for(i=0;i<31;i++) @@ -2107,21 +2099,21 @@ static const ge25519_aff ge25519_base_multiples_affine[425] = { {{0x69, 0x3e, 0x47, 0x97, 0x2c, 0xaf, 0x52, 0x7c, 0x78, 0x83, 0xad, 0x1b, 0x39, 0x82, 0x2f, 0x02, 0x6f, 0x47, 0xdb, 0x2a, 0xb0, 0xe1, 0x91, 0x99, 0x55, 0xb8, 0x99, 0x3a, 0xa0, 0x44, 0x11, 0x51}}} }; -static ZT_ALWAYS_INLINE void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) +static inline void p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) { fe25519_mul(&r->x, &p->x, &p->t); fe25519_mul(&r->y, &p->y, &p->z); fe25519_mul(&r->z, &p->z, &p->t); } -static ZT_ALWAYS_INLINE void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p) +static inline void p1p1_to_p2_2(ge25519_p3 *r, const ge25519_p1p1 *p) { fe25519_mul(&r->x, &p->x, &p->t); fe25519_mul(&r->y, &p->y, &p->z); fe25519_mul(&r->z, &p->z, &p->t); } -static ZT_ALWAYS_INLINE void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) +static inline void p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) { p1p1_to_p2_2(r, p); fe25519_mul(&r->t, &p->x, &p->y); @@ -2190,13 +2182,13 @@ static inline void dbl_p1p1(ge25519_p1p1 *r, const ge25519_p2 *p) } /* Constant-time version of: if(b) r = p */ -static ZT_ALWAYS_INLINE void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b) +static inline void cmov_aff(ge25519_aff *r, const ge25519_aff *p, unsigned char b) { fe25519_cmov(&r->x, &p->x, b); fe25519_cmov(&r->y, &p->y, b); } -static ZT_ALWAYS_INLINE unsigned char equal(signed char b,signed char c) +static inline unsigned char equal(signed char b,signed char c) { unsigned char ub = b; unsigned char uc = c; @@ -2207,7 +2199,7 @@ static ZT_ALWAYS_INLINE unsigned char equal(signed char b,signed char c) return (unsigned char)y; } -static ZT_ALWAYS_INLINE unsigned char negative(signed char b) +static inline unsigned char negative(signed char b) { unsigned long long x = b; /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ x >>= 63; /* 1: yes; 0: no */ @@ -2356,7 +2348,7 @@ static inline void ge25519_scalarmult_base(ge25519_p3 *r, const sc25519 *s) } } -static ZT_ALWAYS_INLINE void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen) +static inline void get_hram(unsigned char *hram, const unsigned char *sm, const unsigned char *pk, unsigned char *playground, unsigned long long smlen) { unsigned long long i; diff --git a/node/C25519.hpp b/node/C25519.hpp index 94b62c321..b73e0ec11 100644 --- a/node/C25519.hpp +++ b/node/C25519.hpp @@ -32,7 +32,7 @@ public: /** * Generate a C25519 elliptic curve key pair */ - static ZT_ALWAYS_INLINE void generate(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]) + static inline void generate(uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]) { Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN); _calcPubDH(pub,priv); @@ -53,7 +53,7 @@ public: * @tparam F Type of 'cond' */ template - static ZT_ALWAYS_INLINE void generateSatisfying(F cond,uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]) + static inline void generateSatisfying(F cond,uint8_t pub[ZT_C25519_PUBLIC_KEY_LEN],uint8_t priv[ZT_C25519_PRIVATE_KEY_LEN]) { Utils::getSecureRandom(priv,ZT_C25519_PRIVATE_KEY_LEN); _calcPubED(pub,priv); // do Ed25519 key -- bytes 32-63 of pub and priv diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt index 9dac4bc1b..399e602c0 100644 --- a/node/CMakeLists.txt +++ b/node/CMakeLists.txt @@ -7,8 +7,8 @@ endif(WIN32) set(core_headers Address.hpp - AES.hpp AtomicCounter.hpp + Buf.hpp Buffer.hpp C25519.hpp Capability.hpp @@ -29,6 +29,7 @@ set(core_headers Network.hpp NetworkConfig.hpp Node.hpp + OS.hpp Packet.hpp Path.hpp Peer.hpp @@ -50,13 +51,14 @@ set(core_headers set(core_src AES.cpp - AES-aesni.c + Buf.cpp C25519.cpp Credential.cpp ECC384.cpp Identity.cpp IncomingPacket.cpp InetAddress.cpp + Locator.cpp Membership.cpp Network.cpp NetworkConfig.cpp @@ -69,23 +71,10 @@ set(core_src SelfAwareness.cpp SHA512.cpp Switch.cpp - Trace.cpp + Topology.cpp Utils.cpp ) add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers}) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}) - -#if(UNIX) -# set_source_files_properties( -# AES.cpp -# AES-aesni.c -# ECC384.cpp -# Salsa20.cpp -# C25519.cpp -# Poly1305.cpp -# PROPERTIES -# COMPILE_FLAGS "-Wall -O3" -# ) -#endif(UNIX) diff --git a/node/Capability.hpp b/node/Capability.hpp index 902869e8d..8606cd5d8 100644 --- a/node/Capability.hpp +++ b/node/Capability.hpp @@ -14,9 +14,9 @@ #ifndef ZT_CAPABILITY_HPP #define ZT_CAPABILITY_HPP -#include -#include -#include +#include +#include +#include #include "Constants.hpp" #include "Credential.hpp" @@ -61,7 +61,7 @@ class Capability : public Credential public: static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_CAPABILITY; } - ZT_ALWAYS_INLINE Capability() : + inline Capability() : _nwid(0), _ts(0), _id(0), @@ -80,7 +80,7 @@ public: * @param rules Network flow rules for this capability * @param ruleCount Number of flow rules */ - ZT_ALWAYS_INLINE Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) : + inline Capability(uint32_t id,uint64_t nwid,int64_t ts,unsigned int mccl,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) : _nwid(nwid), _ts(ts), _id(id), @@ -94,32 +94,32 @@ public: /** * @return Rules -- see ruleCount() for size of array */ - ZT_ALWAYS_INLINE const ZT_VirtualNetworkRule *rules() const { return _rules; } + inline const ZT_VirtualNetworkRule *rules() const { return _rules; } /** * @return Number of rules in rules() */ - ZT_ALWAYS_INLINE unsigned int ruleCount() const { return _ruleCount; } + inline unsigned int ruleCount() const { return _ruleCount; } /** * @return ID and evaluation order of this capability in network */ - ZT_ALWAYS_INLINE uint32_t id() const { return _id; } + inline uint32_t id() const { return _id; } /** * @return Network ID for which this capability was issued */ - ZT_ALWAYS_INLINE uint64_t networkId() const { return _nwid; } + inline uint64_t networkId() const { return _nwid; } /** * @return Timestamp */ - ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; } + inline int64_t timestamp() const { return _ts; } /** * @return Last 'to' address in chain of custody */ - ZT_ALWAYS_INLINE Address issuedTo() const + inline Address issuedTo() const { Address i2; for(unsigned int i=0;i static inline void serializeRules(Buffer &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount) @@ -460,10 +460,10 @@ public: } // Provides natural sort order by ID - ZT_ALWAYS_INLINE bool operator<(const Capability &c) const { return (_id < c._id); } + inline bool operator<(const Capability &c) const { return (_id < c._id); } - ZT_ALWAYS_INLINE bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); } - ZT_ALWAYS_INLINE bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); } + inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); } + inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); } private: uint64_t _nwid; diff --git a/node/CertificateOfMembership.hpp b/node/CertificateOfMembership.hpp index ba21afd46..976c48b47 100644 --- a/node/CertificateOfMembership.hpp +++ b/node/CertificateOfMembership.hpp @@ -14,8 +14,8 @@ #ifndef ZT_CERTIFICATEOFMEMBERSHIP_HPP #define ZT_CERTIFICATEOFMEMBERSHIP_HPP -#include -#include +#include +#include #include #include @@ -69,7 +69,7 @@ class CertificateOfMembership : public Credential friend class Credential; public: - static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; } + static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COM; } /** * Reserved qualifier IDs @@ -101,7 +101,7 @@ public: /** * Create an empty certificate of membership */ - ZT_ALWAYS_INLINE CertificateOfMembership() : + inline CertificateOfMembership() : _qualifierCount(0), _signatureLength(0) {} @@ -113,7 +113,7 @@ public: * @param nwid Network ID * @param issuedTo Certificate recipient */ - ZT_ALWAYS_INLINE CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo) + inline CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo) { _qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP; _qualifiers[0].value = timestamp; @@ -135,22 +135,22 @@ public: * @param startAt Position to start in buffer */ template - ZT_ALWAYS_INLINE CertificateOfMembership(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } + inline CertificateOfMembership(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } /** * @return True if there's something here */ - ZT_ALWAYS_INLINE operator bool() const { return (_qualifierCount != 0); } + inline operator bool() const { return (_qualifierCount != 0); } /** * @return Credential ID, always 0 for COMs */ - ZT_ALWAYS_INLINE uint32_t id() const { return 0; } + inline uint32_t id() const { return 0; } /** * @return Timestamp for this cert and maximum delta for timestamp */ - ZT_ALWAYS_INLINE int64_t timestamp() const + inline int64_t timestamp() const { for(unsigned int i=0;i<_qualifierCount;++i) { if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP) @@ -162,7 +162,7 @@ public: /** * @return Address to which this cert was issued */ - ZT_ALWAYS_INLINE Address issuedTo() const + inline Address issuedTo() const { for(unsigned int i=0;i<_qualifierCount;++i) { if (_qualifiers[i].id == COM_RESERVED_ID_ISSUED_TO) @@ -174,7 +174,7 @@ public: /** * @return Network ID for which this cert was issued */ - ZT_ALWAYS_INLINE uint64_t networkId() const + inline uint64_t networkId() const { for(unsigned int i=0;i<_qualifierCount;++i) { if (_qualifiers[i].id == COM_RESERVED_ID_NETWORK_ID) @@ -211,7 +211,7 @@ public: } } - ZT_ALWAYS_INLINE void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); } + inline void setQualifier(ReservedId id,uint64_t value,uint64_t maxDelta) { setQualifier((uint64_t)id,value,maxDelta); } /** * Compare two certificates for parameter agreement @@ -294,17 +294,17 @@ public: * @param RR Runtime environment for looking up peers * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call */ - ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } + inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } /** * @return True if signed */ - ZT_ALWAYS_INLINE bool isSigned() const { return (_signedBy); } + inline bool isSigned() const { return (_signedBy); } /** * @return Address that signed this certificate or null address if none */ - ZT_ALWAYS_INLINE const Address &signedBy() const { return _signedBy; } + inline const Address &signedBy() const { return _signedBy; } template inline void serialize(Buffer &b) const @@ -369,7 +369,7 @@ public: return (p - startAt); } - ZT_ALWAYS_INLINE bool operator==(const CertificateOfMembership &c) const + inline bool operator==(const CertificateOfMembership &c) const { if (_signedBy != c._signedBy) return false; @@ -385,7 +385,7 @@ public: } return (memcmp(_signature,c._signature,_signatureLength) == 0); } - ZT_ALWAYS_INLINE bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); } + inline bool operator!=(const CertificateOfMembership &c) const { return (!(*this == c)); } private: struct _Qualifier diff --git a/node/CertificateOfOwnership.hpp b/node/CertificateOfOwnership.hpp index a77ebaae2..336577767 100644 --- a/node/CertificateOfOwnership.hpp +++ b/node/CertificateOfOwnership.hpp @@ -14,10 +14,10 @@ #ifndef ZT_CERTIFICATEOFOWNERSHIP_HPP #define ZT_CERTIFICATEOFOWNERSHIP_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include "Constants.hpp" #include "Credential.hpp" @@ -46,7 +46,7 @@ class CertificateOfOwnership : public Credential friend class Credential; public: - static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; } + static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_COO; } enum Thing { @@ -56,12 +56,12 @@ public: THING_IPV6_ADDRESS = 3 }; - ZT_ALWAYS_INLINE CertificateOfOwnership() + inline CertificateOfOwnership() { memset(reinterpret_cast(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(this),0,sizeof(CertificateOfOwnership)); _networkId = nwid; @@ -70,19 +70,19 @@ public: _issuedTo = issuedTo; } - ZT_ALWAYS_INLINE uint64_t networkId() const { return _networkId; } - ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; } - ZT_ALWAYS_INLINE uint32_t id() const { return _id; } - ZT_ALWAYS_INLINE const Address &issuedTo() const { return _issuedTo; } - ZT_ALWAYS_INLINE const Address &signer() const { return _signedBy; } - ZT_ALWAYS_INLINE const uint8_t *signature() const { return _signature; } - ZT_ALWAYS_INLINE unsigned int signatureLength() const { return _signatureLength; } + inline uint64_t networkId() const { return _networkId; } + inline int64_t timestamp() const { return _ts; } + inline uint32_t id() const { return _id; } + inline const Address &issuedTo() const { return _issuedTo; } + inline const Address &signer() const { return _signedBy; } + inline const uint8_t *signature() const { return _signature; } + inline unsigned int signatureLength() const { return _signatureLength; } - ZT_ALWAYS_INLINE unsigned int thingCount() const { return (unsigned int)_thingCount; } - ZT_ALWAYS_INLINE Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; } - ZT_ALWAYS_INLINE const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; } + inline unsigned int thingCount() const { return (unsigned int)_thingCount; } + inline Thing thingType(const unsigned int i) const { return (Thing)_thingTypes[i]; } + inline const uint8_t *thingValue(const unsigned int i) const { return _thingValues[i]; } - ZT_ALWAYS_INLINE bool owns(const InetAddress &ip) const + inline bool owns(const InetAddress &ip) const { if (ip.ss_family == AF_INET) return this->_owns(THING_IPV4_ADDRESS,&(reinterpret_cast(&ip)->sin_addr.s_addr),4); @@ -91,7 +91,7 @@ public: return false; } - ZT_ALWAYS_INLINE bool owns(const MAC &mac) const + inline bool owns(const MAC &mac) const { uint8_t tmp[6]; mac.copyTo(tmp,6); @@ -136,7 +136,7 @@ public: return false; } - ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } + inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } template inline void serialize(Buffer &b,const bool forSign = false) const @@ -206,10 +206,10 @@ public: } // Provides natural sort order by ID - ZT_ALWAYS_INLINE bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); } + inline bool operator<(const CertificateOfOwnership &coo) const { return (_id < coo._id); } - ZT_ALWAYS_INLINE bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); } - ZT_ALWAYS_INLINE bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); } + inline bool operator==(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) == 0); } + inline bool operator!=(const CertificateOfOwnership &coo) const { return (memcmp(this,&coo,sizeof(CertificateOfOwnership)) != 0); } private: inline bool _owns(const Thing &t,const void *v,unsigned int l) const diff --git a/node/Constants.hpp b/node/Constants.hpp index d6da00f01..51948213e 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -14,11 +14,8 @@ #ifndef ZT_CONSTANTS_HPP #define ZT_CONSTANTS_HPP -/****************************************************************************/ -/* Core includes and OS/platform setup stuff */ -/****************************************************************************/ - #include "../include/ZeroTierCore.h" +#include "OS.hpp" #if __has_include("version.h") #include "version.h" @@ -29,187 +26,25 @@ #define ZEROTIER_ONE_VERSION_BUILD 255 #endif -// -// This include file also auto-detects and canonicalizes some environment -// information defines: -// -// __LINUX__ -// __APPLE__ -// __BSD__ (OSX also defines this) -// __UNIX_LIKE__ (Linux, BSD, etc.) -// __WINDOWS__ -// -// Also makes sure __BYTE_ORDER is defined reasonably. -// - -// Hack: make sure __GCC__ is defined on old GCC compilers -#ifndef __GCC__ -#if defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) -#define __GCC__ -#endif -#endif - -#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) -#ifndef __LINUX__ -#define __LINUX__ -#endif -#ifndef __UNIX_LIKE__ -#define __UNIX_LIKE__ -#endif -#include -#endif - -#ifdef __APPLE__ -#include -#ifndef __UNIX_LIKE__ -#define __UNIX_LIKE__ -#endif -#ifndef __BSD__ -#define __BSD__ -#endif -#include -#endif - -#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) -#ifndef __UNIX_LIKE__ -#define __UNIX_LIKE__ -#endif -#ifndef __BSD__ -#define __BSD__ -#endif -#include -#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 -#include -#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 -#endif - -#ifdef __WINDOWS__ -#define ZT_PATH_SEPARATOR '\\' -#define ZT_PATH_SEPARATOR_S "\\" -#define ZT_EOL_S "\r\n" -#else -#define ZT_PATH_SEPARATOR '/' -#define ZT_PATH_SEPARATOR_S "/" -#define ZT_EOL_S "\n" -#endif - -#if (defined(__GNUC__) && (__GNUC__ >= 3)) || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) || defined(__clang__) -#define ZT_ALWAYS_INLINE inline __attribute__((always_inline)) -#ifndef likely -#define likely(x) __builtin_expect((x),1) -#endif -#ifndef unlikely -#define unlikely(x) __builtin_expect((x),0) -#endif -#else -#ifndef likely -#define ZT_ALWAYS_INLINE inline -#define likely(x) (x) -#endif -#ifndef unlikely -#define unlikely(x) (x) -#endif -#endif - -#if defined(__WINDOWS__) && !defined(__GNUC__) && !defined (__clang__) && !defined(__INTEL_COMPILER) -#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop)) -#else -#define ZT_PACKED_STRUCT(D) D __attribute__((packed)) -#endif - -#if __cplusplus > 199711L -#ifndef __CPP11__ -#define __CPP11__ -#endif -#endif - -#ifdef SOCKET -#define ZT_SOCKET SOCKET -#else -#define ZT_SOCKET int -#endif -#ifdef INVALID_SOCKET -#define ZT_INVALID_SOCKET INVALID_SOCKET -#else -#define ZT_INVALID_SOCKET -1 -#endif - -/****************************************************************************/ -/* Internal ZeroTier constants */ -/****************************************************************************/ - /** * Length of a ZeroTier address in bytes */ #define ZT_ADDRESS_LENGTH 5 -/** - * Length of a hexadecimal ZeroTier address - */ -#define ZT_ADDRESS_LENGTH_HEX 10 - /** * Addresses beginning with this byte are reserved for the joy of in-band signaling */ #define ZT_ADDRESS_RESERVED_PREFIX 0xff /** - * Secure DNS name for ZeroTier's default root - * - * This resolves via GeoDNS to the (probably) nearest actual root server's locator. + * Maximum DNS or URL name size for an Endpoint (set so that max marshaled endpoint size is 64 bytes) */ -#define ZT_DEFAULT_ROOT_NAME "ztl-aj4zes4l6zumq64na6borruuvd6diw2koxrjcaatolcekt2gj5rrhric.ztl-6lhxeo7n3z7kzkgcqzj3ndliaq.zerotier.network" +#define ZT_ENDPOINT_MAX_NAME_SIZE 61 /** - * Default locator for default root - * - * This is used before the root has been successfully fetched, or if DNS is unavailable. + * Size of an identity hash (SHA384) */ -#define ZT_DEFAULT_ROOT_LOCATOR "AAAAAW2OuYyfOkbxvzAAduZvqzPihUmmLuIGTRhDJzwsMAukXD8gvvAtutIlcju1mpu0sTU1cwlhruz1oWOs5HfM6wcnAluZrBSlFmoJowAEBLm0DVInCQS5tA1SAbsGKgJuoMgVAAAAAAAAAAAAACcJBioCbqDIFQAAAAAAAAAAAAABuwAAYDvTNB2snbn7TYom4PBTh/ohRgCnI2/A/nfKakGCb+2hGJTtxTCiGTzKZdbjd0vyKAKQLJxhj7RaoCo3XjPn8w9nDEmhdNCgCM/IITCJIzc9tEKFsSQnJY4VmB3dopBAfQAA" +#define ZT_IDENTITY_HASH_SIZE 48 /** * Default virtual network MTU (not physical) @@ -282,246 +117,34 @@ #define ZT_RELAY_MAX_HOPS 4 /** - * Expire time for multicast 'likes' and indirect multicast memberships in ms - */ -#define ZT_MULTICAST_LIKE_EXPIRE 600000 - -/** - * Period for multicast LIKE re-announcements to connected nodes - */ -#define ZT_MULTICAST_ANNOUNCE_PERIOD 60000 - -/** - * Period for multicast GATHER on multicast groups - */ -#define ZT_MULTICAST_GATHER_PERIOD ZT_MULTICAST_ANNOUNCE_PERIOD - -/** - * Period for multicast GATHER if there are no known recipients - */ -#define ZT_MULTICAST_GATHER_PERIOD_WHEN_NO_RECIPIENTS 2500 - -/** - * Timeout for outgoing multicasts - * - * This is how long we wait for explicit or implicit gather results. - */ -#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000 - -/** - * How frequently to check for changes to the system's network interfaces. When - * the service decides to use this constant it's because we want to react more - * quickly to new interfaces that pop up or go down. - */ -#define ZT_MULTIPATH_BINDER_REFRESH_PERIOD 5000 - -/** - * Packets are only used for QoS/ACK statistical sampling if their packet ID is divisible by - * this integer. This is to provide a mechanism for both peers to agree on which packets need - * special treatment without having to exchange information. Changing this value would be - * a breaking change and would necessitate a protocol version upgrade. Since each incoming and - * outgoing packet ID is checked against this value its evaluation is of the form: - * (id & (divisor - 1)) == 0, thus the divisor must be a power of 2. - * - * This value is set at (16) so that given a normally-distributed RNG output we will sample - * 1/16th (or ~6.25%) of packets. - */ -#define ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR 0x10 - -/** - * Time horizon for VERB_QOS_MEASUREMENT and VERB_ACK packet processing cutoff - */ -#define ZT_PATH_QOS_ACK_CUTOFF_TIME 30000 - -/** - * Maximum number of VERB_QOS_MEASUREMENT and VERB_ACK packets allowed to be - * processed within cutoff time. Separate totals are kept for each type but - * the limit is the same for both. - * - * This limits how often this peer will compute statistical estimates - * of various QoS measures from a VERB_QOS_MEASUREMENT or VERB_ACK packets to - * CUTOFF_LIMIT times per CUTOFF_TIME milliseconds per peer to prevent - * this from being useful for DOS amplification attacks. - */ -#define ZT_PATH_QOS_ACK_CUTOFF_LIMIT 128 - -/** - * Path choice history window size. This is used to keep track of which paths were - * previously selected so that we can maintain a target allocation over time. - */ -#define ZT_MULTIPATH_PROPORTION_WIN_SZ 128 - -/** - * Interval used for rate-limiting the computation of path quality estimates. - */ -#define ZT_PATH_QUALITY_COMPUTE_INTERVAL 1000 - -/** - * Number of samples to consider when computing real-time path statistics - */ -#define ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ 128 - -/** - * Number of samples to consider when computing performing long-term path quality analysis. - * By default this value is set to ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ but can - * be set to any value greater than that to observe longer-term path quality behavior. - */ -#define ZT_PATH_QUALITY_METRIC_WIN_SZ ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ - -/** - * Maximum acceptable Packet Delay Variance (PDV) over a path - */ -#define ZT_PATH_MAX_PDV 1000 - -/** - * Maximum acceptable time interval between expectation and receipt of at least one ACK over a path - */ -#define ZT_PATH_MAX_AGE 30000 - -/** - * Maximum acceptable mean latency over a path - */ -#define ZT_PATH_MAX_MEAN_LATENCY 1000 - -/** - * How much each factor contributes to the "stability" score of a path - */ -#define ZT_PATH_CONTRIB_PDV (1.0 / 3.0) -#define ZT_PATH_CONTRIB_LATENCY (1.0 / 3.0) -#define ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE (1.0 / 3.0) - -/** - * How much each factor contributes to the "quality" score of a path - */ -#define ZT_PATH_CONTRIB_STABILITY (0.75 / 3.0) -#define ZT_PATH_CONTRIB_THROUGHPUT (1.50 / 3.0) -#define ZT_PATH_CONTRIB_SCOPE (0.75 / 3.0) - -/** - * How often a QoS packet is sent - */ -#define ZT_PATH_QOS_INTERVAL 3000 - -/** - * Min and max acceptable sizes for a VERB_QOS_MEASUREMENT packet - */ -#define ZT_PATH_MIN_QOS_PACKET_SZ 8 + 1 -#define ZT_PATH_MAX_QOS_PACKET_SZ 1400 - -/** - * How many ID:sojourn time pairs in a single QoS packet - */ -#define ZT_PATH_QOS_TABLE_SIZE ((ZT_PATH_MAX_QOS_PACKET_SZ * 8) / (64 + 16)) - -/** - * Maximum number of outgoing packets we monitor for QoS information - */ -#define ZT_PATH_MAX_OUTSTANDING_QOS_RECORDS 128 - -/** - * Timeout for QoS records - */ -#define ZT_PATH_QOS_TIMEOUT (ZT_PATH_QOS_INTERVAL * 2) - -/** - * How often the service tests the path throughput - */ -#define ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL (ZT_PATH_ACK_INTERVAL * 8) - -/** - * Minimum amount of time between each ACK packet - */ -#define ZT_PATH_ACK_INTERVAL 1000 - -/** - * How often an aggregate link statistics report is emitted into this tracing system - */ -#define ZT_PATH_AGGREGATE_STATS_REPORT_INTERVAL 60000 - -/** - * How much an aggregate link's component paths can vary from their target allocation - * before the link is considered to be in a state of imbalance. - */ -#define ZT_PATH_IMBALANCE_THRESHOLD 0.20 - -/** - * Max allowable time spent in any queue - */ -#define ZT_QOS_TARGET 5 // ms - -/** - * Time period where the time spent in the queue by a packet should fall below - * target at least once - */ -#define ZT_QOS_INTERVAL 100 // ms - -/** - * The number of bytes that each queue is allowed to send during each DRR cycle. - * This approximates a single-byte-based fairness queuing scheme - */ -#define ZT_QOS_QUANTUM ZT_DEFAULT_MTU - -/** - * The maximum total number of packets that can be queued among all - * active/inactive, old/new queues - */ -#define ZT_QOS_MAX_ENQUEUED_PACKETS 1024 - -/** - * Number of QoS queues (buckets) - */ -#define ZT_QOS_NUM_BUCKETS 9 - -/** - * All unspecified traffic is put in this bucket. Anything in a bucket with a smaller - * value is de-prioritized. Anything in a bucket with a higher value is prioritized over - * other traffic. - */ -#define ZT_QOS_DEFAULT_BUCKET 0 - -/** - * Do not accept HELLOs over a given path more often than this - */ -#define ZT_PATH_HELLO_RATE_LIMIT 1000 - -/** - * Delay between full-fledge pings of directly connected peers + * Period between keepalives sent to paths if no other traffic has been sent * * See https://conferences.sigcomm.org/imc/2010/papers/p260.pdf for * some real world data on NAT UDP timeouts. From the paper: "the * lowest measured timeout when a binding has seen bidirectional * traffic is 54 sec." 30 seconds is faster than really necessary. */ -#define ZT_PEER_PING_PERIOD 30000 +#define ZT_PATH_KEEPALIVE_PERIOD 30000 /** - * Delay between refreshes of locators via DNS or other methods + * Timeout for path aliveness (measured from last receive) */ -#define ZT_DYNAMIC_ROOT_UPDATE_PERIOD 120000 +#define ZT_PATH_ACTIVITY_TIMEOUT ((ZT_PATH_KEEPALIVE_PERIOD * 2) + 5000) + +/** + * Delay between full HELLO messages between peers + */ +#define ZT_PEER_PING_PERIOD 60000 /** * Timeout for overall peer activity (measured from last receive) */ -#ifndef ZT_SDK -#define ZT_PEER_ACTIVITY_TIMEOUT 500000 -#else -#define ZT_PEER_ACTIVITY_TIMEOUT 30000 -#endif +#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_PING_PERIOD * 2) + 5000) /** - * Rescan for best/fastest root every N milliseconds + * Maximum interval between sort/prioritize of paths for a peer */ -#define ZT_FIND_BEST_ROOT_PERIOD 2000 - -/** - * General rate limit timeout for multiple packet types (HELLO, etc.) - */ -#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500 - -/** - * General limit for max RTT for requests over the network - */ -#define ZT_GENERAL_RTT_LIMIT 5000 +#define ZT_PEER_PRIORITIZE_PATHS_INTERVAL 5000 /** * Delay between requests for updated network autoconf information @@ -531,15 +154,6 @@ */ #define ZT_NETWORK_AUTOCONF_DELAY 60000 -/** - * Minimum interval between attempts by relays to unite peers - * - * When a relay gets a packet destined for another peer, it sends both peers - * a RENDEZVOUS message no more than this often. This instructs the peers - * to attempt NAT-t and gives each the other's corresponding IP:port pair. - */ -#define ZT_MIN_UNITE_INTERVAL 30000 - /** * Sanity limit on maximum bridge routes * @@ -566,34 +180,10 @@ */ #define ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH 120000 -/** - * Time horizon for push direct paths cutoff - */ -#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 30000 - -/** - * Maximum number of direct path pushes within cutoff time - * - * This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses - * per CUTOFF_TIME milliseconds per peer to prevent this from being - * useful for DOS amplification attacks. - */ -#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 8 - /** * Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6) */ -#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 8 - -/** - * Time horizon for VERB_NETWORK_CREDENTIALS cutoff - */ -#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000 - -/** - * Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time - */ -#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15 +#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4 /** * WHOIS rate limit (we allow these to be pretty fast) @@ -632,12 +222,7 @@ */ #define ZT_SIGNATURE_BUFFER_SIZE 96 -/** - * Desired / recommended min stack size for threads (used on some platforms to reset thread stack size) - */ -#define ZT_THREAD_MIN_STACK_SIZE 1048576 - -// Internal cryptographic algorithm IDs +// Internal cryptographic algorithm IDs (these match relevant identity types) #define ZT_CRYPTO_ALG_C25519 0 #define ZT_CRYPTO_ALG_P384 1 @@ -651,4 +236,9 @@ #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_BAD_ENCODING 203 +/* Ethernet frame types that might be relevant to us */ +#define ZT_ETHERTYPE_IPV4 0x0800 +#define ZT_ETHERTYPE_ARP 0x0806 +#define ZT_ETHERTYPE_IPV6 0x86dd + #endif diff --git a/node/Credential.cpp b/node/Credential.cpp index d53b62f00..5e5ebe9bf 100644 --- a/node/Credential.cpp +++ b/node/Credential.cpp @@ -26,7 +26,7 @@ namespace ZeroTier { template -static ZT_ALWAYS_INLINE Credential::VerifyResult _credVerify(const RuntimeEnvironment *const RR,void *tPtr,CRED credential) +static inline Credential::VerifyResult _credVerify(const RuntimeEnvironment *const RR,void *tPtr,CRED credential) { const Address signedBy(credential.signer()); const uint64_t networkId = credential.networkId(); diff --git a/node/Credential.hpp b/node/Credential.hpp index 19ca9e009..6edc41fae 100644 --- a/node/Credential.hpp +++ b/node/Credential.hpp @@ -18,10 +18,10 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include "Constants.hpp" diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp index 1614ccb74..8099e50e0 100644 --- a/node/Dictionary.hpp +++ b/node/Dictionary.hpp @@ -19,7 +19,7 @@ #include "Buffer.hpp" #include "Address.hpp" -#include +#include namespace ZeroTier { @@ -49,9 +49,9 @@ template class Dictionary { public: - ZT_ALWAYS_INLINE Dictionary() { memset(_d,0,sizeof(_d)); } - ZT_ALWAYS_INLINE Dictionary(const char *s) { this->load(s); } - ZT_ALWAYS_INLINE Dictionary(const char *s,unsigned int len) + inline Dictionary() { memset(_d,0,sizeof(_d)); } + inline Dictionary(const char *s) { this->load(s); } + inline Dictionary(const char *s,unsigned int len) { for(unsigned int i=0;i - ZT_ALWAYS_INLINE bool get(const char *key,Buffer &dest) const + inline bool get(const char *key,Buffer &dest) const { const int r = this->get(key,const_cast(reinterpret_cast(dest.data())),BC); if (r >= 0) { @@ -236,7 +236,7 @@ public: * @param dfl Default value if not found in dictionary * @return Boolean value of key or 'dfl' if not found */ - ZT_ALWAYS_INLINE bool getB(const char *key,bool dfl = false) const + inline bool getB(const char *key,bool dfl = false) const { char tmp[4]; if (this->get(key,tmp,sizeof(tmp)) >= 0) @@ -251,7 +251,7 @@ public: * @param dfl Default value or 0 if unspecified * @return Decoded hex UInt value or 'dfl' if not found */ - ZT_ALWAYS_INLINE uint64_t getUI(const char *key,uint64_t dfl = 0) const + inline uint64_t getUI(const char *key,uint64_t dfl = 0) const { char tmp[128]; if (this->get(key,tmp,sizeof(tmp)) >= 1) @@ -266,7 +266,7 @@ public: * @param dfl Default value or 0 if unspecified * @return Decoded hex UInt value or 'dfl' if not found */ - ZT_ALWAYS_INLINE int64_t getI(const char *key,int64_t dfl = 0) const + inline int64_t getI(const char *key,int64_t dfl = 0) const { char tmp[128]; if (this->get(key,tmp,sizeof(tmp)) >= 1) @@ -366,7 +366,7 @@ public: /** * Add a boolean as a '1' or a '0' */ - ZT_ALWAYS_INLINE bool add(const char *key,bool value) + inline bool add(const char *key,bool value) { return this->add(key,(value) ? "1" : "0",1); } @@ -374,7 +374,7 @@ public: /** * Add a 64-bit integer (unsigned) as a hex value */ - ZT_ALWAYS_INLINE bool add(const char *key,uint64_t value) + inline bool add(const char *key,uint64_t value) { char tmp[32]; return this->add(key,Utils::hex(value,tmp),-1); @@ -383,7 +383,7 @@ public: /** * Add a 64-bit integer (unsigned) as a hex value */ - ZT_ALWAYS_INLINE bool add(const char *key,int64_t value) + inline bool add(const char *key,int64_t value) { char tmp[32]; if (value >= 0) { @@ -397,7 +397,7 @@ public: /** * Add a 64-bit integer (unsigned) as a hex value */ - ZT_ALWAYS_INLINE bool add(const char *key,const Address &a) + inline bool add(const char *key,const Address &a) { char tmp[32]; return this->add(key,Utils::hex(a.toInt(),tmp),-1); @@ -409,7 +409,7 @@ public: * @tparam BC Buffer capacity (usually inferred) */ template - ZT_ALWAYS_INLINE bool add(const char *key,const Buffer &value) + inline bool add(const char *key,const Buffer &value) { return this->add(key,(const char *)value.data(),(int)value.size()); } @@ -418,7 +418,7 @@ public: * @param key Key to check * @return True if key is present */ - ZT_ALWAYS_INLINE bool contains(const char *key) const + inline bool contains(const char *key) const { char tmp[2]; return (this->get(key,tmp,2) >= 0); @@ -427,10 +427,10 @@ public: /** * @return Value of C template parameter */ - ZT_ALWAYS_INLINE unsigned int capacity() const { return C; } + inline unsigned int capacity() const { return C; } - ZT_ALWAYS_INLINE const char *data() const { return _d; } - ZT_ALWAYS_INLINE char *unsafeData() { return _d; } + inline const char *data() const { return _d; } + inline char *unsafeData() { return _d; } private: char _d[C]; diff --git a/node/ECC384.cpp b/node/ECC384.cpp index fa96eeadb..b908615f1 100644 --- a/node/ECC384.cpp +++ b/node/ECC384.cpp @@ -4,10 +4,9 @@ // This code is under the BSD 2-clause license, not ZeroTier's license ////////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include +#include +#include +#include #include "Constants.hpp" #include "ECC384.hpp" @@ -141,7 +140,7 @@ static ZT_ALWAYS_INLINE void vli_set(uint64_t *p_dest, uint64_t *p_src) } /* Returns sign of p_left - p_right. */ -static ZT_ALWAYS_INLINE int vli_cmp(uint64_t *p_left, uint64_t *p_right) +static inline int vli_cmp(uint64_t *p_left, uint64_t *p_right) { int i; for(i = NUM_ECC_DIGITS-1; i >= 0; --i) @@ -376,7 +375,7 @@ static inline void vli_square(uint64_t *p_result, uint64_t *p_left) /* Computes p_result = (p_left + p_right) % p_mod. Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */ -static ZT_ALWAYS_INLINE void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod) +static inline void vli_modAdd(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod) { uint64_t l_carry = vli_add(p_result, p_left, p_right); if(l_carry || vli_cmp(p_result, p_mod) >= 0) @@ -387,7 +386,7 @@ static ZT_ALWAYS_INLINE void vli_modAdd(uint64_t *p_result, uint64_t *p_left, ui /* Computes p_result = (p_left - p_right) % p_mod. Assumes that p_left < p_mod and p_right < p_mod, p_result != p_mod. */ -static ZT_ALWAYS_INLINE void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod) +static inline void vli_modSub(uint64_t *p_result, uint64_t *p_left, uint64_t *p_right, uint64_t *p_mod) { uint64_t l_borrow = vli_sub(p_result, p_left, p_right); if(l_borrow) diff --git a/node/Endpoint.hpp b/node/Endpoint.hpp new file mode 100644 index 000000000..25ebac131 --- /dev/null +++ b/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 +#include +#include +#include + +#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(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(&_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(&_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(&_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 diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index 00b788b3f..73e6f5ace 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -94,7 +94,7 @@ public: /** * @param bc Initial capacity in buckets (default: 32, must be nonzero) */ - ZT_ALWAYS_INLINE Hashtable(unsigned long bc = 32) : + inline Hashtable(unsigned long bc = 32) : _t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))), _bc(bc), _s(0) @@ -105,7 +105,7 @@ public: _t[i] = (_Bucket *)0; } - ZT_ALWAYS_INLINE Hashtable(const Hashtable &ht) : + inline Hashtable(const Hashtable &ht) : _t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * ht._bc))), _bc(ht._bc), _s(ht._s) @@ -125,13 +125,13 @@ public: } } - ZT_ALWAYS_INLINE ~Hashtable() + inline ~Hashtable() { this->clear(); ::free(_t); } - ZT_ALWAYS_INLINE Hashtable &operator=(const Hashtable &ht) + inline Hashtable &operator=(const Hashtable &ht) { this->clear(); if (ht._s) { @@ -149,7 +149,7 @@ public: /** * Erase all entries */ - ZT_ALWAYS_INLINE void clear() + inline void clear() { if (_s) { for(unsigned long i=0;i<_bc;++i) { @@ -168,7 +168,7 @@ public: /** * @return Vector of all keys */ - ZT_ALWAYS_INLINE typename std::vector keys() const + inline typename std::vector keys() const { typename std::vector k; if (_s) { @@ -191,7 +191,7 @@ public: * @tparam Type of V (generally inferred) */ template - ZT_ALWAYS_INLINE void appendKeys(C &v) const + inline void appendKeys(C &v) const { if (_s) { for(unsigned long i=0;i<_bc;++i) { @@ -207,7 +207,7 @@ public: /** * @return Vector of all entries (pairs of K,V) */ - ZT_ALWAYS_INLINE typename std::vector< std::pair > entries() const + inline typename std::vector< std::pair > entries() const { typename std::vector< std::pair > k; if (_s) { @@ -227,7 +227,7 @@ public: * @param k Key * @return Pointer to value or NULL if not found */ - ZT_ALWAYS_INLINE V *get(const K k) + inline V *get(const K k) { _Bucket *b = _t[_hc(k) % _bc]; while (b) { @@ -237,14 +237,14 @@ public: } return (V *)0; } - ZT_ALWAYS_INLINE const V *get(const K k) const { return const_cast(this)->get(k); } + inline const V *get(const K k) const { return const_cast(this)->get(k); } /** * @param k Key * @param v Value to fill with result * @return True if value was found and set (if false, v is not modified) */ - ZT_ALWAYS_INLINE bool get(const K &k,V &v) const + inline bool get(const K &k,V &v) const { _Bucket *b = _t[_hc(k) % _bc]; while (b) { @@ -261,7 +261,7 @@ public: * @param k Key to check * @return True if key is present */ - ZT_ALWAYS_INLINE bool contains(const K &k) const + inline bool contains(const K &k) const { _Bucket *b = _t[_hc(k) % _bc]; while (b) { @@ -276,7 +276,7 @@ public: * @param k Key * @return True if value was present */ - ZT_ALWAYS_INLINE bool erase(const K &k) + inline bool erase(const K &k) { const unsigned long bidx = _hc(k) % _bc; _Bucket *lastb = (_Bucket *)0; @@ -301,7 +301,7 @@ public: * @param v Value * @return Reference to value in table */ - ZT_ALWAYS_INLINE V &set(const K &k,const V &v) + inline V &set(const K &k,const V &v) { const unsigned long h = _hc(k); unsigned long bidx = h % _bc; @@ -331,7 +331,7 @@ public: * @param k Key * @return Value, possibly newly created */ - ZT_ALWAYS_INLINE V &operator[](const K k) + inline V &operator[](const K k) { const unsigned long h = _hc(k); unsigned long bidx = h % _bc; @@ -376,7 +376,7 @@ private: static ZT_ALWAYS_INLINE unsigned long _hc(void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); } static ZT_ALWAYS_INLINE unsigned long _hc(const void *p) { return ((unsigned long)((uintptr_t)p) * (unsigned long)0x9e3779b1); } - ZT_ALWAYS_INLINE void _grow() + inline void _grow() { const unsigned long nc = _bc * 2; _Bucket **nt = reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * nc)); diff --git a/node/Identity.cpp b/node/Identity.cpp index 08377eb5b..ddcd5c948 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -11,10 +11,8 @@ */ /****/ -#include -#include -#include -#include +#include +#include #include "Constants.hpp" #include "Identity.hpp" @@ -100,8 +98,13 @@ void Identity::generate(const Type t) delete [] genmem; if (t == P384) { + // We sign with both because in pure FIPS environments we might have to say + // that we do not rely on any non-FIPS algorithms, or may even have to disable + // them. ECC384GenerateKey(_pub.p384,_priv.p384); - C25519::sign(_priv.c25519,_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.p384s); + C25519::sign(_priv.c25519,_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.c25519s); + SHA384(digest,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE); + ECC384ECDSASign(_priv.p384,digest,_pub.p384s); } } @@ -116,7 +119,10 @@ bool Identity::locallyValidate() const case C25519: break; case P384: - if (!C25519::verify(_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.p384s,ZT_C25519_SIGNATURE_LEN)) + if (!C25519::verify(_pub.c25519,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE,_pub.c25519s,ZT_C25519_SIGNATURE_LEN)) + return false; + SHA384(digest,&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE); + if (!ECC384ECDSAVerify(_pub.p384,digest,_pub.p384s)) return false; default: return false; @@ -135,6 +141,108 @@ bool Identity::locallyValidate() const return false; } +bool Identity::hash(uint8_t h[48],const bool includePrivate) const +{ + switch(_type) { + + case C25519: + if ((_hasPrivate)&&(includePrivate)) + SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); + else SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + return true; + + case P384: + if ((_hasPrivate)&&(includePrivate)) + SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv)); + else SHA384(h,&_pub,sizeof(_pub)); + return true; + + } + return false; +} + +unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const +{ + if (_hasPrivate) { + switch(_type) { + + case C25519: + if (siglen >= ZT_C25519_SIGNATURE_LEN) { + C25519::sign(_priv.c25519,_pub.c25519,data,len,sig); + return ZT_C25519_SIGNATURE_LEN; + } + + case P384: + if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { + // When signing with P384 we also hash the C25519 public key as an + // extra measure to ensure that only this identity can verify. + uint8_t h[48]; + SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); + return ZT_ECC384_SIGNATURE_SIZE; + } + + } + } + return 0; +} + +bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const +{ + switch(_type) { + + case C25519: + return C25519::verify(_pub.c25519,data,len,sig,siglen); + + case P384: + if (siglen == ZT_ECC384_SIGNATURE_SIZE) { + uint8_t h[48]; + SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig); + } + break; + + } + return false; +} + +bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const +{ + uint8_t rawkey[128]; + uint8_t h[64]; + if (_hasPrivate) { + if (_type == C25519) { + + if ((id._type == C25519)||(id._type == P384)) { + // If we are a C25519 key we can agree with another C25519 key or with only the + // C25519 portion of a type 1 P-384 key. + C25519::agree(_priv.c25519,id._pub.c25519,rawkey); + SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN); + memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); + return true; + } + + } else if (_type == P384) { + + if (id._type == P384) { + C25519::agree(_priv.c25519,id._pub.c25519,rawkey); + ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN); + SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); + memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); + return true; + } else if (id._type == C25519) { + // If the other identity is a C25519 identity we can agree using only that type. + C25519::agree(_priv.c25519,id._pub.c25519,rawkey); + SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN); + memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); + return true; + } + + } + } + return false; +} + char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const { switch(_type) { @@ -224,7 +332,7 @@ bool Identity::fromString(const char *str) switch(_type) { case C25519: - if (Utils::unhex(f,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) { + if (Utils::unhex(f,strlen(f),_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) != ZT_C25519_PUBLIC_KEY_LEN) { _address.zero(); return false; } @@ -245,7 +353,7 @@ bool Identity::fromString(const char *str) switch(_type) { case C25519: - if (Utils::unhex(f,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) { + if (Utils::unhex(f,strlen(f),_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN) != ZT_C25519_PRIVATE_KEY_LEN) { _address.zero(); return false; } else { @@ -278,3 +386,99 @@ bool Identity::fromString(const char *str) } } // namespace ZeroTier + +extern "C" { + +ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type) +{ + if ((type != ZT_IDENTITY_TYPE_C25519)&&(type != ZT_IDENTITY_TYPE_P384)) + return nullptr; + try { + ZeroTier::Identity *id = new ZeroTier::Identity(); + id->generate((ZeroTier::Identity::Type)type); + return reinterpret_cast(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(id); + } catch ( ... ) { + return nullptr; + } +} + +int ZT_Identity_validate(const ZT_Identity *id) +{ + if (!id) + return 0; + return reinterpret_cast(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(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(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(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(id)->toString(includePrivate != 0,buf); + return buf; +} + +int ZT_Identity_hasPrivate(const ZT_Identity *id) +{ + if (!id) + return 0; + return reinterpret_cast(id)->hasPrivate() ? 1 : 0; +} + +uint64_t ZT_Identity_address(const ZT_Identity *id) +{ + if (!id) + return 0; + return reinterpret_cast(id)->address().toInt(); +} + +void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate) +{ + reinterpret_cast(id)->hash(h,includePrivate != 0); +} + +ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id) +{ + if (id) + delete reinterpret_cast(id); +} + +} diff --git a/node/Identity.hpp b/node/Identity.hpp index a54a0f94c..aabdc6daa 100644 --- a/node/Identity.hpp +++ b/node/Identity.hpp @@ -14,8 +14,8 @@ #ifndef ZT_IDENTITY_HPP #define ZT_IDENTITY_HPP -#include -#include +#include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -27,6 +27,11 @@ #define ZT_IDENTITY_STRING_BUFFER_LENGTH 1024 +#define ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE (ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE) +#define ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE) + +#define ZT_IDENTITY_MARSHAL_SIZE_MAX (ZT_ADDRESS_LENGTH + 4 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) + namespace ZeroTier { /** @@ -52,15 +57,17 @@ public: }; ZT_ALWAYS_INLINE Identity() { memset(reinterpret_cast(this),0,sizeof(Identity)); } - ZT_ALWAYS_INLINE Identity(const char *str) - { - if (!fromString(str)) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; - } - template - ZT_ALWAYS_INLINE Identity(const Buffer &b,unsigned int startAt = 0) { deserialize(b,startAt); } + ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast(&this->_priv),sizeof(this->_priv)); } - ZT_ALWAYS_INLINE ~Identity() { Utils::burn(reinterpret_cast(this),sizeof(Identity)); } + /** + * Construct identity from string + * + * If the identity is not basically valid (no deep checking is done) the result will + * be a null identity. + * + * @param str Identity in canonical string format + */ + explicit ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); } /** * Set identity to NIL value (all zero) @@ -68,18 +75,18 @@ public: ZT_ALWAYS_INLINE void zero() { memset(reinterpret_cast(this),0,sizeof(Identity)); } /** - * @return Identity type + * @return Identity type (undefined if identity is null or invalid) */ ZT_ALWAYS_INLINE Type type() const { return _type; } /** * Generate a new identity (address, key pair) * - * This is a time consuming operation. + * This is a time consuming operation taking up to 5-10 seconds on some slower systems. * * @param t Type of identity to generate */ - void generate(const Type t); + void generate(Type t); /** * Check the validity of this identity's pairing of key to address @@ -94,41 +101,12 @@ public: ZT_ALWAYS_INLINE bool hasPrivate() const { return _hasPrivate; } /** - * Compute the SHA512 hash of our private key (if we have one) + * This generates a SHA384 hash of this identity's keys. * - * @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length) - * @return True on success, false if no private key - */ - ZT_ALWAYS_INLINE bool sha512PrivateKey(void *const sha) const - { - if (_hasPrivate) { - switch(_type) { - case C25519: - SHA512(sha,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); - return true; - case P384: - SHA512(sha,&_priv,sizeof(_priv)); - return true; - } - } - return false; - } - - /** * @param h Buffer to receive SHA384 of public key(s) + * @param includePrivate If true, hash private key(s) as well */ - ZT_ALWAYS_INLINE bool hash(uint8_t h[48]) const - { - switch(_type) { - case C25519: - SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - return true; - case P384: - SHA384(h,&_pub,sizeof(_pub)); - return true; - } - return false; - } + bool hash(uint8_t h[48],bool includePrivate = false) const; /** * Sign a message with this identity (private key required) @@ -142,31 +120,7 @@ public: * @param siglen Length of buffer * @return Number of bytes actually written to sig or 0 on error */ - ZT_ALWAYS_INLINE unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const - { - if (_hasPrivate) { - switch(_type) { - - case C25519: - if (siglen >= ZT_C25519_SIGNATURE_LEN) { - C25519::sign(_priv.c25519,_pub.c25519,data,len,sig); - return ZT_C25519_SIGNATURE_LEN; - } - - case P384: - if (siglen >= ZT_ECC384_SIGNATURE_SIZE) { - // Signature hash includes the C25519/Ed25519 public key after the message. - // This is an added guard against divorcing these two bound keys. - uint8_t h[48]; - SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig); - return ZT_ECC384_SIGNATURE_SIZE; - } - - } - } - return 0; - } + unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const; /** * Verify a message signature against this identity @@ -177,21 +131,7 @@ public: * @param siglen Length of signature in bytes * @return True if signature validates and data integrity checks */ - ZT_ALWAYS_INLINE bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const - { - switch(_type) { - case C25519: - return C25519::verify(_pub.c25519,data,len,sig,siglen); - case P384: - if (siglen == ZT_ECC384_SIGNATURE_SIZE) { - uint8_t h[48]; - SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig); - } - break; - } - return false; - } + bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const; /** * Shortcut method to perform key agreement with another identity @@ -202,78 +142,21 @@ public: * @param key Result parameter to fill with key bytes * @return Was agreement successful? */ - ZT_ALWAYS_INLINE bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const - { - uint8_t rawkey[128]; - uint8_t h[64]; - if (_hasPrivate) { - if (_type == C25519) { - if ((id._type == C25519)||(id._type == P384)) { - // If we are a C25519 key we can agree with another C25519 key or with only the - // C25519 portion of a type 1 P-384 key. - C25519::agree(_priv.c25519,id._pub.c25519,rawkey); - SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN); - memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); - return true; - } - } else if (_type == P384) { - if (id._type == P384) { - // Perform key agreement over both curves for the same reason that C25519 public - // keys are included in P-384 signature inputs: to bind the keys together so - // that a type 1 identity with the same C25519 public key (and therefore address) - // but a different P-384 key will not work. - C25519::agree(_priv.c25519,id._pub.c25519,rawkey); - ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN); - SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE); - memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); - return true; - } else if (id._type == C25519) { - // If the other identity is a C25519 identity we can agree using only that type. - C25519::agree(_priv.c25519,id._pub.c25519,rawkey); - SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN); - memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH); - return true; - } - } - } - return false; - } + bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const; /** * @return This identity's address */ ZT_ALWAYS_INLINE const Address &address() const { return _address; } - /** - * Attempt to generate an older type identity from a newer type - * - * If this identity has its private key this is not transferred to - * the downgraded identity. - * - * @param dest Destination to fill with downgraded identity - * @param toType Desired identity type - */ - ZT_ALWAYS_INLINE bool downgrade(Identity &dest,const Type toType) - { - if ((_type == P384)&&(toType == C25519)) { - dest._address = _address; - dest._type = C25519; - dest._hasPrivate = false; - memcpy(dest._pub.c25519,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - return true; - } - return false; - } - /** * Serialize this identity (binary) * * @param b Destination buffer to append to * @param includePrivate If true, include private key component (if present) (default: false) - * @throws std::out_of_range Buffer too small */ template - ZT_ALWAYS_INLINE void serialize(Buffer &b,bool includePrivate = false) const + inline void serialize(Buffer &b,bool includePrivate = false) const { _address.appendTo(b); switch(_type) { @@ -291,9 +174,7 @@ public: case P384: b.append((uint8_t)P384); - b.append(_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); - b.append(_pub.p384,ZT_ECC384_PUBLIC_KEY_SIZE); - b.append(_pub.p384s,ZT_C25519_SIGNATURE_LEN); + b.append(&_pub,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE); if ((_hasPrivate)&&(includePrivate)) { b.append((uint8_t)(ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)); b.append(_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); @@ -301,7 +182,7 @@ public: } else { b.append((uint8_t)0); } - b.append((uint16_t)0); // size of additional fields + b.append((uint8_t)0); // size of additional fields (should have included such a thing in v0!) break; } @@ -316,11 +197,9 @@ public: * @param b Buffer containing serialized data * @param startAt Index within buffer of serialized data (default: 0) * @return Length of serialized data read from buffer - * @throws std::out_of_range Serialized data invalid - * @throws std::invalid_argument Serialized data invalid */ template - ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { _hasPrivate = false; unsigned int p = startAt; @@ -347,12 +226,8 @@ public: break; case P384: - memcpy(_pub.c25519,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); - p += ZT_C25519_PUBLIC_KEY_LEN; - memcpy(_pub.p384,b.field(p,ZT_ECC384_PUBLIC_KEY_SIZE),ZT_ECC384_PUBLIC_KEY_SIZE); - p += ZT_ECC384_PUBLIC_KEY_SIZE; - memcpy(_pub.p384s,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); - p += ZT_ECC384_SIGNATURE_SIZE; + memcpy(&_pub,b.field(p,ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE),ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE); + p += ZT_C25519_PUBLIC_KEY_LEN + ZT_ECC384_PUBLIC_KEY_SIZE + ZT_C25519_SIGNATURE_LEN + ZT_ECC384_SIGNATURE_SIZE; pkl = (unsigned int)b[p++]; if (pkl) { if (pkl != (ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE)) @@ -365,7 +240,7 @@ public: } else { _hasPrivate = false; } - p += b.template at(p) + 2; + p += b.template at(p) + 2; break; default: @@ -381,7 +256,7 @@ public: * * @param includePrivate If true, include private key (if it exists) * @param buf Buffer to store string - * @return ASCII string representation of identity + * @return ASCII string representation of identity (pointer to buf) */ char *toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const; @@ -399,18 +274,15 @@ public: /** * @return True if this identity contains something */ - ZT_ALWAYS_INLINE operator bool() const { return (_address); } + explicit ZT_ALWAYS_INLINE operator bool() const { return (_address); } ZT_ALWAYS_INLINE bool operator==(const Identity &id) const { if ((_address == id._address)&&(_type == id._type)) { switch(_type) { - case C25519: - return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0); - case P384: - return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0); - default: - return false; + case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) == 0); + // case P384: + default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) == 0); } } return false; @@ -424,10 +296,9 @@ public: return true; if (_type == id._type) { switch(_type) { - case C25519: - return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0); - case P384: - return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0); + case C25519: return (memcmp(_pub.c25519,id._pub.c25519,ZT_C25519_PUBLIC_KEY_LEN) < 0); + // case P384: + default: return (memcmp(&_pub,&id._pub,sizeof(_pub)) < 0); } } } @@ -440,18 +311,102 @@ public: ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)_address.toInt() + (unsigned long)_pub.c25519[0] + (unsigned long)_pub.c25519[1] + (unsigned long)_pub.c25519[2]); } + // Marshal interface /////////////////////////////////////////////////////// + static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_IDENTITY_MARSHAL_SIZE_MAX; } + inline int marshal(uint8_t data[ZT_IDENTITY_MARSHAL_SIZE_MAX],const bool includePrivate = false) const + { + _address.copyTo(data,ZT_ADDRESS_LENGTH); + switch(_type) { + + case C25519: + data[ZT_ADDRESS_LENGTH] = (uint8_t)C25519; + memcpy(data + ZT_ADDRESS_LENGTH + 1,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN); + if ((includePrivate)&&(_hasPrivate)) { + data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = ZT_C25519_PRIVATE_KEY_LEN; + memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN); + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN); + } + data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN] = 0; + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1); + + case P384: + data[ZT_ADDRESS_LENGTH] = (uint8_t)P384; + memcpy(data + ZT_ADDRESS_LENGTH + 1,&_pub,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); + if ((includePrivate)&&(_hasPrivate)) { + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = ZT_C25519_PRIVATE_KEY_LEN + ZT_ECC384_PRIVATE_KEY_SIZE; + memcpy(data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,&_priv,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE] = 0; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1); + } + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE] = 0; + data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1] = 0; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2); + + } + return -1; + } + inline int unmarshal(const uint8_t *restrict data,const int len) + { + if (len < (ZT_ADDRESS_LENGTH + 1)) + return -1; + unsigned int privlen; + switch((_type = (Type)data[ZT_ADDRESS_LENGTH])) { + + case C25519: + if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1)) + return -1; + memcpy(_pub.c25519,data + ZT_ADDRESS_LENGTH + 1,ZT_C25519_PUBLIC_KEY_LEN); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN]; + if (privlen == ZT_C25519_PRIVATE_KEY_LEN) { + if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)) + return -1; + _hasPrivate = true; + memcpy(_priv.c25519,data + ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1,ZT_C25519_PRIVATE_KEY_LEN); + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN); + } else if (privlen == 0) { + _hasPrivate = false; + return (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1); + } + break; + + case P384: + if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2)) + return -1; + memcpy(&_pub,data + ZT_ADDRESS_LENGTH + 1,ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE]; + if (privlen == ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE) { + if (len < (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)) + return -1; + _hasPrivate = true; + memcpy(&_priv,data + ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1,ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE); + privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE]; + if (len < (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1))) + return -1; + return (int)(privlen + (unsigned int)(ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)); + } else if (privlen == 0) { + _hasPrivate = false; + return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2); + } + break; + + } + return -1; + } + //////////////////////////////////////////////////////////////////////////// + private: Address _address; - Type _type; + Type _type; // _type determines which fields in _priv and _pub are used bool _hasPrivate; ZT_PACKED_STRUCT(struct { // don't re-order these uint8_t c25519[ZT_C25519_PRIVATE_KEY_LEN]; uint8_t p384[ZT_ECC384_PRIVATE_KEY_SIZE]; }) _priv; ZT_PACKED_STRUCT(struct { // don't re-order these - uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; - uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; - uint8_t p384s[ZT_C25519_SIGNATURE_LEN]; // signature of both keys with ed25519 to confirm type 0 extension to type 1 + uint8_t c25519[ZT_C25519_PUBLIC_KEY_LEN]; // Curve25519 and Ed25519 public keys + uint8_t p384[ZT_ECC384_PUBLIC_KEY_SIZE]; // NIST P-384 public key + uint8_t c25519s[ZT_C25519_SIGNATURE_LEN]; // signature of both keys with ed25519 + uint8_t p384s[ZT_ECC384_SIGNATURE_SIZE]; // signature of both keys with p384 }) _pub; }; diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 4490af56a..98fac0f24 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -29,238 +29,49 @@ #include "Revocation.hpp" #include "Trace.hpp" -#include -#include -#include +#include +#include #include namespace ZeroTier { -bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr) +namespace { +////////////////////////////////////////////////////////////////////////////// +// Implementation of each protocol verb // +////////////////////////////////////////////////////////////////////////////// + +void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer,const uint64_t nwid,const SharedPtr &path) { - 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(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; - } + 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::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) -{ - const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB]; - const uint64_t inRePacketId = at(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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); - const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); - const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); - const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); - const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14)); - RR->mc->remove(at(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) -{ - 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; -} - -bool IncomingPacket::_doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) -{ - 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; -} - -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) { 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(ZT_PROTO_VERB_HELLO_IDX_REVISION); - const int64_t timestamp = at(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(ZT_PROTO_VERB_HELLO_IDX_REVISION); + const int64_t timestamp = pkt.at(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); Identity id; - unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); + unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(pkt,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); if (protoVersion < ZT_PROTO_VERSION_MIN) { - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"protocol version too old"); + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"protocol version too old"); return true; } if (fromAddress != id.address()) { - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"identity/address mismatch"); + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"identity/address mismatch"); return true; } @@ -272,32 +83,32 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool // Identity is different from the one we already have -- address collision // Check rate limits - if (!RR->node->rateGateIdentityVerification(now,_path->address())) + if (!RR->node->rateGateIdentityVerification(now,path->address())) return true; uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]; if (RR->identity.agree(id,key)) { - if (dearmor(key)) { // ensure packet is authentic, otherwise drop - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"address collision"); + if (pkt.dearmor(key)) { // ensure packet is authentic, otherwise drop + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"address collision"); Packet outp(id.address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((uint8_t)Packet::VERB_HELLO); outp.append((uint64_t)pid); outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION); outp.armor(key,true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } else { - RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC"); + RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC"); } } else { - RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid identity"); + RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid identity"); } return true; } else { // Identity is the same as the one we already have -- check packet integrity - if (!dearmor(peer->key())) { - RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC"); + if (!pkt.dearmor(peer->key())) { + RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC"); return true; } @@ -309,26 +120,26 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool // Sanity check: this basically can't happen if (alreadyAuthenticated) { - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"illegal alreadyAuthenticated state"); + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"illegal alreadyAuthenticated state"); return true; } // Check rate limits - if (!RR->node->rateGateIdentityVerification(now,_path->address())) { - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"rate limit exceeded"); + if (!RR->node->rateGateIdentityVerification(now,path->address())) { + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"rate limit exceeded"); return true; } // Check packet integrity and MAC (this is faster than locallyValidate() so do it first to filter out total crap) SharedPtr newPeer(new Peer(RR,RR->identity,id)); - if (!dearmor(newPeer->key())) { - RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,pid,fromAddress,hops(),"invalid MAC"); + if (!pkt.dearmor(newPeer->key())) { + RR->t->incomingPacketMessageAuthenticationFailure(tPtr,path,pid,fromAddress,pkt.hops(),"invalid MAC"); return true; } // Check that identity's address is valid as per the derivation function if (!id.locallyValidate()) { - RR->t->incomingPacketDroppedHELLO(tPtr,_path,pid,fromAddress,"invalid identity"); + RR->t->incomingPacketDroppedHELLO(tPtr,path,pid,fromAddress,"invalid identity"); return true; } @@ -340,12 +151,12 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool // VALID -- if we made it here, packet passed identity and authenticity checks! // Get address to which this packet was sent to learn our external surface address if packet was direct. - if (hops() == 0) { + if (pkt.hops() == 0) { InetAddress externalSurfaceAddress; - if (ptr < size()) { - ptr += externalSurfaceAddress.deserialize(*this,ptr); - if ((externalSurfaceAddress)&&(hops() == 0)) - RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now); + if (ptr < pkt.size()) { + ptr += externalSurfaceAddress.deserialize(pkt,ptr); + if ((externalSurfaceAddress)&&(pkt.hops() == 0)) + RR->sa->iam(tPtr,id.address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now); } } @@ -360,20 +171,82 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); - _path->address().serialize(outp); + path->address().serialize(outp); outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),now); + path->send(RR,tPtr,outp.data(),outp.size(),now); peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version - peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_HELLO,0,Packet::VERB_NOP,0); + peer->received(tPtr,path,pkt.hops(),pid,pkt.payloadLength(),Packet::VERB_HELLO,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doERROR(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB]; - const uint64_t inRePacketId = at(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID); + const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB]; + const uint64_t inRePacketId = pkt.at(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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + const SharedPtr 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(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + const SharedPtr 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; +} + +ZT_ALWAYS_INLINE bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) +{ + const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB]; + const uint64_t inRePacketId = pkt.at(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID); uint64_t networkId = 0; if (!RR->node->expectingReplyTo(inRePacketId)) @@ -382,34 +255,34 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP switch(inReVerb) { case Packet::VERB_HELLO: { - const uint64_t latency = RR->node->now() - at(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(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); + const uint64_t latency = RR->node->now() - pkt.at(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(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); if (vProto < ZT_PROTO_VERSION_MIN) return true; - if (hops() == 0) { - _path->updateLatency((unsigned int)latency,RR->node->now()); - if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < size()) { + if (pkt.hops() == 0) { + if ((ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2) < pkt.size()) { InetAddress externalSurfaceAddress; - externalSurfaceAddress.deserialize(*this,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2); + externalSurfaceAddress.deserialize(pkt,ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2); if (externalSurfaceAddress) - RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now()); + RR->sa->iam(tPtr,peer->address(),path->localSocket(),path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now()); } } + peer->updateLatency((unsigned int)latency); peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); } break; case Packet::VERB_WHOIS: if (RR->topology->isRoot(peer->identity())) { unsigned int p = ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY; - while (p < size()) { + while (p < pkt.size()) { try { Identity id; - p += id.deserialize(*this,p); + p += id.deserialize(pkt,p); if (id) RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr(new Peer(RR,RR->identity,id)))); } catch ( ... ) { @@ -420,47 +293,36 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP break; case Packet::VERB_NETWORK_CONFIG_REQUEST: { - networkId = at(ZT_PROTO_VERB_OK_IDX_PAYLOAD); + networkId = pkt.at(ZT_PROTO_VERB_OK_IDX_PAYLOAD); const SharedPtr network(RR->node->network(networkId)); if (network) - network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD); + network->handleConfigChunk(tPtr,pkt.packetId(),pkt.source(),pkt,ZT_PROTO_VERB_OK_IDX_PAYLOAD); } break; case Packet::VERB_MULTICAST_GATHER: { - // TODO - /* - networkId = at(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID); - const SharedPtr network(RR->node->network(networkId)); - if (network) { - const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI)); - const unsigned int count = at(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(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS)); - } - */ } break; default: break; } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_OK,inRePacketId,inReVerb,networkId); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_OK,inRePacketId,inReVerb,networkId); return true; } -bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { if (!peer->rateGateInboundWhoisRequest(RR->node->now())) return true; Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_WHOIS); - outp.append(packetId()); + outp.append(pkt.packetId()); unsigned int count = 0; unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; - while ((ptr + ZT_ADDRESS_LENGTH) <= size()) { - const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + while ((ptr + ZT_ADDRESS_LENGTH) <= pkt.size()) { + const Address addr(pkt.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH; const Identity id(RR->topology->getIdentity(tPtr,addr)); @@ -475,89 +337,90 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar if (count > 0) { outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,0); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { if (RR->topology->isRoot(peer->identity())) { - const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + uint16_t junk = (uint16_t)Utils::random(); + const Address with(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); const SharedPtr rendezvousWith(RR->topology->get(with)); if (rendezvousWith) { - const unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); - const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; + const unsigned int port = pkt.at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); + const unsigned int addrlen = pkt[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { - InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); - if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) { - const uint64_t junk = Utils::random(); - RR->node->putPacket(tPtr,_path->localSocket(),atAddr,&junk,4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls - rendezvousWith->sendHELLO(tPtr,_path->localSocket(),atAddr,RR->node->now()); + InetAddress atAddr(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); + if (rendezvousWith->shouldTryPath(tPtr,RR->node->now(),peer,atAddr)) { + if (atAddr.isV4()) + RR->node->putPacket(tPtr,path->localSocket(),atAddr,&junk,2,2); // IPv4 "firewall opener" + rendezvousWith->sendHELLO(tPtr,path->localSocket(),atAddr,RR->node->now()); } } } } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,0); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const uint64_t nwid = at(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID); + const uint64_t nwid = pkt.at(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID); const SharedPtr network(RR->node->network(nwid)); if (network) { if (network->gate(tPtr,peer)) { - if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { - const unsigned int etherType = at(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE); + if (pkt.size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { + const unsigned int etherType = pkt.at(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE); const MAC sourceMac(peer->address(),nwid); - const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD; - const uint8_t *const frameData = reinterpret_cast(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(pkt.data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD; if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0) RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen); } } else { - _sendErrorNeedCredentials(RR,tPtr,peer,nwid); + _sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path); return false; } } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_FRAME,0,Packet::VERB_NOP,nwid); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_FRAME,0,Packet::VERB_NOP,nwid); return true; } -bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const uint64_t nwid = at(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID); + const uint64_t nwid = pkt.at(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID); const SharedPtr network(RR->node->network(nwid)); if (network) { - const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS]; + const unsigned int flags = pkt[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS]; unsigned int comLen = 0; if ((flags & 0x01) != 0) { // inline COM with EXT_FRAME is deprecated but still used with old peers CertificateOfMembership com; - comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM); + comLen = com.deserialize(pkt,ZT_PROTO_VERB_EXT_FRAME_IDX_COM); if (com) network->addCredential(tPtr,com); } if (!network->gate(tPtr,peer)) { - RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true); - _sendErrorNeedCredentials(RR,tPtr,peer,nwid); + RR->t->incomingNetworkAccessDenied(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,true); + _sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path); return false; } - if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) { - const unsigned int etherType = at(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(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE); + const MAC to(pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO); + const MAC from(pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM); + const unsigned int frameLen = pkt.size() - (comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD); + const uint8_t *const frameData = (const uint8_t *)pkt.field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen); if ((!from)||(from == network->mac())) { - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); return true; } @@ -567,20 +430,20 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const if (network->config().permitsBridging(peer->address())) { network->learnBridgeRoute(from,peer->address()); } else { - RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)"); - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); + RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)"); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); return true; } } else if (to != network->mac()) { if (to.isMulticast()) { if (network->config().multicastLimit == 0) { - RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled"); - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); + RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled"); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); return true; } } else if (!network->config().permitsBridging(RR->identity.address())) { - RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)"); - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); + RR->t->incomingNetworkFrameDropped(tPtr,network,path,pkt.packetId(),pkt.size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)"); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); return true; } } @@ -591,73 +454,42 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const } } - if ((flags & 0x10) != 0) { // ACK requested + if ((flags & 0x10U) != 0) { // ACK requested Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((uint8_t)Packet::VERB_EXT_FRAME); - outp.append((uint64_t)packetId()); + outp.append((uint64_t)pkt.packetId()); outp.append((uint64_t)nwid); outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,nwid); return true; } -bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doECHO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { if (!peer->rateGateEchoRequest(RR->node->now())) return true; - const uint64_t pid = packetId(); + const uint64_t pid = pkt.packetId(); Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((unsigned char)Packet::VERB_ECHO); outp.append((uint64_t)pid); - if (size() > ZT_PACKET_IDX_PAYLOAD) - outp.append(reinterpret_cast(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD); + if (pkt.size() > ZT_PACKET_IDX_PAYLOAD) + outp.append(reinterpret_cast(pkt.data()) + ZT_PACKET_IDX_PAYLOAD,pkt.size() - ZT_PACKET_IDX_PAYLOAD); outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + 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); + peer->received(tPtr,path,pkt.hops(),pid,pkt.payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doNETWORK_CREDENTIALS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const int64_t now = RR->node->now(); - bool authorized = false; - uint64_t lastNwid = 0; - - // 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(ptr); - if (nwid != lastNwid) { - lastNwid = nwid; - SharedPtr 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(ptr + 14)),peer->address()); - */ - } - - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,0); - return true; -} - -bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) -{ - if (!peer->rateGateCredentialsReceived(RR->node->now())) - return true; - CertificateOfMembership com; Capability cap; Tag tag; @@ -666,8 +498,8 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t SharedPtr network; unsigned int p = ZT_PACKET_IDX_PAYLOAD; - while ((p < size())&&((*this)[p] != 0)) { - p += com.deserialize(*this,p); + while ((p < pkt.size())&&(pkt[p] != 0)) { + p += com.deserialize(pkt,p); if (com) { network = RR->node->network(com.networkId()); if (network) { @@ -678,10 +510,10 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t } ++p; // skip trailing 0 after COMs if present - if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations - const unsigned int numCapabilities = at(p); p += 2; + if (p < pkt.size()) { // older ZeroTier versions do not send capabilities, tags, or revocations + const unsigned int numCapabilities = pkt.at(p); p += 2; for(unsigned int i=0;iid() != cap.networkId())) network = RR->node->network(cap.networkId()); if (network) { @@ -690,11 +522,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t } } - if (p >= size()) return true; + if (p >= pkt.size()) return true; - const unsigned int numTags = at(p); p += 2; + const unsigned int numTags = pkt.at(p); p += 2; for(unsigned int i=0;iid() != tag.networkId())) network = RR->node->network(tag.networkId()); if (network) { @@ -703,11 +535,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t } } - if (p >= size()) return true; + if (p >= pkt.size()) return true; - const unsigned int numRevocations = at(p); p += 2; + const unsigned int numRevocations = pkt.at(p); p += 2; for(unsigned int i=0;iid() != revocation.networkId())) network = RR->node->network(revocation.networkId()); if (network) { @@ -716,11 +548,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t } } - if (p >= size()) return true; + if (p >= pkt.size()) return true; - const unsigned int numCoos = at(p); p += 2; + const unsigned int numCoos = pkt.at(p); p += 2; for(unsigned int i=0;iid() != coo.networkId())) network = RR->node->network(coo.networkId()); if (network) { @@ -730,22 +562,22 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t } } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,(network) ? network->id() : 0); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,(network) ? network->id() : 0); return true; } -bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG_REQUEST(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const uint64_t nwid = at(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(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); + const unsigned int hopCount = pkt.hops(); + const uint64_t requestPacketId = pkt.packetId(); if (RR->localNetworkController) { - const unsigned int metaDataLength = (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN <= size()) ? at(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(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 metaData(metaDataBytes,metaDataLength); - RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : _path->address(),requestPacketId,peer->identity(),metaData); + RR->localNetworkController->request(nwid,(hopCount > 0) ? InetAddress() : path->address(),requestPacketId,peer->identity(),metaData); } else { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); @@ -753,46 +585,46 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); outp.append(nwid); outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } - peer->received(tPtr,_path,hopCount,requestPacketId,payloadLength(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,nwid); + peer->received(tPtr,path,hopCount,requestPacketId,pkt.payloadLength(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,nwid); return true; } -bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const SharedPtr network(RR->node->network(at(ZT_PACKET_IDX_PAYLOAD))); + const SharedPtr network(RR->node->network(pkt.at(ZT_PACKET_IDX_PAYLOAD))); if (network) { - const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD); + const uint64_t configUpdateId = network->handleConfigChunk(tPtr,pkt.packetId(),pkt.source(),pkt,ZT_PACKET_IDX_PAYLOAD); if (configUpdateId) { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK); outp.append((uint8_t)Packet::VERB_ECHO); - outp.append((uint64_t)packetId()); + outp.append((uint64_t)pkt.packetId()); outp.append((uint64_t)network->id()); outp.append((uint64_t)configUpdateId); outp.armor(peer->key(),true); - _path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); + path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now()); } } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,(network) ? network->id() : 0); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,(network) ? network->id() : 0); return true; } -bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doMULTICAST_GATHER(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - const uint64_t nwid = at(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(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI)); - const unsigned int gatherLimit = at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT); + const uint64_t nwid = pkt.at(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(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI)); + const unsigned int gatherLimit = pkt.at(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT); const SharedPtr network(RR->node->network(nwid)); if ((flags & 0x01) != 0) { try { CertificateOfMembership com; - com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM); + com.deserialize(pkt,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM); if ((com)&&(network)) network->addCredential(tPtr,com); } catch ( ... ) {} // discard invalid COMs @@ -800,7 +632,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr if (network) { if (!network->gate(tPtr,peer)) { - _sendErrorNeedCredentials(RR,tPtr,peer,nwid); + _sendErrorNeedCredentials(pkt,RR,tPtr,peer,nwid,path); return false; } } @@ -823,242 +655,148 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr */ } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,nwid); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,nwid); return true; } -bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) -{ - unsigned int offset = ZT_PACKET_IDX_PAYLOAD; - const uint64_t nwid = at(offset); offset += 8; - const unsigned int flags = (*this)[offset]; ++offset; - - const SharedPtr 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(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
recipients; - if ((flags & 0x08) != 0) { - const unsigned int rc = at(offset); offset += 2; - for(unsigned int i=0;iaddress())&&(a != RR->identity.address())) { - recipients.push_back(a); - } - offset += 5; - } - } - const unsigned int afterRecipientsOffset = offset; - - const MulticastGroup to(MAC(field(offset,6),6),at(offset + 6)); offset += 10; - const unsigned int etherType = at(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
anchors = network->config().anchors(); - const bool amAnchor = (std::find(anchors.begin(),anchors.end(),RR->identity.address()) != anchors.end()); - - for(std::list
::iterator ra(recipients.begin());ra!=recipients.end();) { - SharedPtr 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
::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) +ZT_ALWAYS_INLINE bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { const int64_t now = RR->node->now(); - // First, subject this to a rate limit - if (!peer->rateGatePushDirectPaths(now)) { - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0); - return true; - } + if (peer->rateGateInboundPushDirectPaths(now)) { + uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6 + memset(countPerScope,0,sizeof(countPerScope)); - // 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 = pkt.at(ZT_PACKET_IDX_PAYLOAD); + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + uint16_t junk = (uint16_t)Utils::random(); - unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); - unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + while (count--) { + /* unsigned int flags = (*this)[ptr++]; */ ++ptr; + unsigned int extLen = pkt.at(ptr); ptr += 2; + ptr += extLen; // unused right now + unsigned int addrType = pkt[ptr++]; + unsigned int addrLen = pkt[ptr++]; - while (count--) { // if ptr overflows Buffer will throw - /* unsigned int flags = (*this)[ptr++]; */ ++ptr; - unsigned int extLen = at(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(pkt.field(ptr,4),4,pkt.at(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(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; + } - switch(addrType) { - case 4: { - const InetAddress a(field(ptr,4),4,at(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(ptr + 16)); - if ((!peer->hasActivePathTo(now,a)) && // not already known - (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),-1,a)) ) // should use path - { - if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) - peer->sendHELLO(tPtr,-1,a,now); - } - } break; + ptr += addrLen; } - ptr += addrLen; } - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0); + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +ZT_ALWAYS_INLINE bool _doUSER_MESSAGE(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr &peer,const SharedPtr &path) { - if (likely(size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) { + if (likely(pkt.size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) { ZT_UserMessage um; um.origin = peer->address().toInt(); - um.typeId = at(ZT_PACKET_IDX_PAYLOAD); - um.data = reinterpret_cast(reinterpret_cast(data()) + ZT_PACKET_IDX_PAYLOAD + 8); - um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8); + um.typeId = pkt.at(ZT_PACKET_IDX_PAYLOAD); + um.data = reinterpret_cast(reinterpret_cast(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(&um)); } - - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,0); - + peer->received(tPtr,path,pkt.hops(),pkt.packetId(),pkt.payloadLength(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,0); return true; } -bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) +////////////////////////////////////////////////////////////////////////////// +} // anonymous namespace + +bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr) { - ZT_RemoteTrace rt; - const char *ptr = reinterpret_cast(data()) + ZT_PACKET_IDX_PAYLOAD; - const char *const eof = reinterpret_cast(data()) + size(); - rt.origin = peer->address().toInt(); - rt.data = const_cast(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(++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); } + + const SharedPtr 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; + } + } + + 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(*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; } - - peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,0); - - return true; -} - -void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &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()); } } // namespace ZeroTier diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index bca4c13cf..3a72d7cc7 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -14,8 +14,6 @@ #ifndef ZT_INCOMINGPACKET_HPP #define ZT_INCOMINGPACKET_HPP -#include - #include "Packet.hpp" #include "Path.hpp" #include "Utils.hpp" @@ -43,17 +41,10 @@ namespace ZeroTier { class RuntimeEnvironment; class Network; -/** - * Subclass of packet that handles the decoding of it - */ class IncomingPacket : public Packet { public: - ZT_ALWAYS_INLINE IncomingPacket() : - Packet(), - _receiveTime(0) - { - } + ZT_ALWAYS_INLINE IncomingPacket() : Packet(),_receiveTime(0),_path() {} /** * Create a new packet-in-decode @@ -108,30 +99,6 @@ public: ZT_ALWAYS_INLINE uint64_t receiveTime() const { return _receiveTime; } private: - // These are called internally to handle packet contents once it has - // been authenticated, decrypted, decompressed, and classified. - bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated); - bool _doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - bool _doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer); - - void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer,const uint64_t nwid); - uint64_t _receiveTime; SharedPtr _path; }; diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 5a056bec2..1f4a0202d 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -11,11 +11,8 @@ */ /****/ -#include -#include -#include - -#include +#include +#include #include "Constants.hpp" #include "InetAddress.hpp" @@ -33,20 +30,20 @@ InetAddress::IpScope InetAddress::ipScope() const case AF_INET: { const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr); - switch(ip >> 24) { + switch(ip >> 24U) { case 0x00: return IP_SCOPE_NONE; // 0.0.0.0/8 (reserved, never used) case 0x06: return IP_SCOPE_PSEUDOPRIVATE; // 6.0.0.0/8 (US Army) case 0x0a: return IP_SCOPE_PRIVATE; // 10.0.0.0/8 - case 0x0b: return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD) - case 0x15: return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN) - case 0x16: return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA) - case 0x19: return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense) - case 0x1a: return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA) - case 0x1c: return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North) - case 0x1d: return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA) - case 0x1e: return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA) - case 0x33: return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security) - case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD) + case 0x0b: //return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD) + case 0x15: //return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN) + case 0x16: //return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA) + case 0x19: //return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense) + case 0x1a: //return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA) + case 0x1c: //return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North) + case 0x1d: //return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA) + case 0x1e: //return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA) + case 0x33: //return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security) + case 0x37: //return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD) case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service) case 0x64: if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10 @@ -164,7 +161,7 @@ bool InetAddress::fromString(const char *ipSlashPort) unsigned int port = 0; if (*portAt) { *(portAt++) = (char)0; - port = Utils::strToUInt(portAt) & 0xffff; + port = Utils::strToUInt(portAt) & 0xffffU; } if (strchr(buf,':')) { @@ -189,7 +186,7 @@ InetAddress InetAddress::netmask() const InetAddress r(*this); switch(r.ss_family) { case AF_INET: - reinterpret_cast(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits()))); + reinterpret_cast(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits()))); break; case AF_INET6: { uint64_t nm[2]; @@ -211,7 +208,7 @@ InetAddress InetAddress::broadcast() const { if (ss_family == AF_INET) { InetAddress r(*this); - reinterpret_cast(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits())); + reinterpret_cast(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffffU >> netmaskBits())); return r; } return InetAddress(); @@ -222,7 +219,7 @@ InetAddress InetAddress::network() const InetAddress r(*this); switch(r.ss_family) { case AF_INET: - reinterpret_cast(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits()))); + reinterpret_cast(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits()))); break; case AF_INET6: { uint64_t nm[2]; @@ -294,7 +291,7 @@ bool InetAddress::isNetwork() const if (bits >= 32) return false; uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr); - return ((ip & (0xffffffff >> bits)) == 0); + return ((ip & (0xffffffffU >> bits)) == 0); } case AF_INET6: { unsigned int bits = netmaskBits(); @@ -304,7 +301,7 @@ bool InetAddress::isNetwork() const return false; const unsigned char *ip = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); unsigned int p = bits / 8; - if ((ip[p++] & (0xff >> (bits % 8))) != 0) + if ((ip[p++] & (0xffU >> (bits % 8))) != 0) return false; while (p < 16) { if (ip[p++]) @@ -378,48 +375,49 @@ bool InetAddress::operator<(const InetAddress &a) const InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac) { - struct sockaddr_in6 sin6; - sin6.sin6_family = AF_INET6; - sin6.sin6_addr.s6_addr[0] = 0xfe; - sin6.sin6_addr.s6_addr[1] = 0x80; - sin6.sin6_addr.s6_addr[2] = 0x00; - sin6.sin6_addr.s6_addr[3] = 0x00; - sin6.sin6_addr.s6_addr[4] = 0x00; - sin6.sin6_addr.s6_addr[5] = 0x00; - sin6.sin6_addr.s6_addr[6] = 0x00; - sin6.sin6_addr.s6_addr[7] = 0x00; - sin6.sin6_addr.s6_addr[8] = mac[0] & 0xfd; - sin6.sin6_addr.s6_addr[9] = mac[1]; - sin6.sin6_addr.s6_addr[10] = mac[2]; - sin6.sin6_addr.s6_addr[11] = 0xff; - sin6.sin6_addr.s6_addr[12] = 0xfe; - sin6.sin6_addr.s6_addr[13] = mac[3]; - sin6.sin6_addr.s6_addr[14] = mac[4]; - sin6.sin6_addr.s6_addr[15] = mac[5]; - sin6.sin6_port = Utils::hton((uint16_t)64); - return InetAddress(sin6); + InetAddress r; + sockaddr_in6 *const sin6 = reinterpret_cast(&r); + sin6->sin6_family = AF_INET6; + sin6->sin6_addr.s6_addr[0] = 0xfe; + sin6->sin6_addr.s6_addr[1] = 0x80; + sin6->sin6_addr.s6_addr[2] = 0x00; + sin6->sin6_addr.s6_addr[3] = 0x00; + sin6->sin6_addr.s6_addr[4] = 0x00; + sin6->sin6_addr.s6_addr[5] = 0x00; + sin6->sin6_addr.s6_addr[6] = 0x00; + sin6->sin6_addr.s6_addr[7] = 0x00; + sin6->sin6_addr.s6_addr[8] = mac[0] & 0xfdU; + sin6->sin6_addr.s6_addr[9] = mac[1]; + sin6->sin6_addr.s6_addr[10] = mac[2]; + sin6->sin6_addr.s6_addr[11] = 0xff; + sin6->sin6_addr.s6_addr[12] = 0xfe; + sin6->sin6_addr.s6_addr[13] = mac[3]; + sin6->sin6_addr.s6_addr[14] = mac[4]; + sin6->sin6_addr.s6_addr[15] = mac[5]; + sin6->sin6_port = Utils::hton((uint16_t)64); + return r; } InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress) { InetAddress r; - struct sockaddr_in6 *const sin6 = reinterpret_cast(&r); + sockaddr_in6 *const sin6 = reinterpret_cast(&r); sin6->sin6_family = AF_INET6; sin6->sin6_addr.s6_addr[0] = 0xfd; - sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56); - sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48); - sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40); - sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32); - sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24); - sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16); - sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8); + sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56U); + sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48U); + sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40U); + sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32U); + sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24U); + sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16U); + sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8U); sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid; sin6->sin6_addr.s6_addr[9] = 0x99; sin6->sin6_addr.s6_addr[10] = 0x93; - sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32); - sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24); - sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16); - sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8); + sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32U); + sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24U); + sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16U); + sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8U); sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress; sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that return r; @@ -427,19 +425,19 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress) InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress) { - nwid ^= (nwid >> 32); + nwid ^= (nwid >> 32U); InetAddress r; - struct sockaddr_in6 *const sin6 = reinterpret_cast(&r); + sockaddr_in6 *const sin6 = reinterpret_cast(&r); sin6->sin6_family = AF_INET6; sin6->sin6_addr.s6_addr[0] = 0xfc; - sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24); - sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16); - sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8); + sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24U); + sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16U); + sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8U); sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid; - sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32); - sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24); - sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16); - sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8); + sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32U); + sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24U); + sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16U); + sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8U); sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress; sin6->sin6_addr.s6_addr[15] = 0x01; sin6->sin6_port = Utils::hton((uint16_t)40); diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 011327654..414184297 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -14,12 +14,13 @@ #ifndef ZT_INETADDRESS_HPP #define ZT_INETADDRESS_HPP -#include -#include -#include +#include +#include +#include + +#include "../include/ZeroTierOne.h" #include "Constants.hpp" -#include "../include/ZeroTierOne.h" #include "Utils.hpp" #include "MAC.hpp" #include "Buffer.hpp" @@ -41,6 +42,16 @@ namespace ZeroTier { */ struct InetAddress : public sockaddr_storage { +private: + template + ZT_ALWAYS_INLINE void copySockaddrToThis(const SA *sa) + { + memcpy(reinterpret_cast(this),sa,sizeof(SA)); + if (sizeof(SA) < sizeof(InetAddress)) + memset(reinterpret_cast(this) + sizeof(SA),0,sizeof(InetAddress) - sizeof(SA)); + } + +public: /** * Loopback IPv4 address (no port) */ @@ -75,123 +86,86 @@ struct InetAddress : public sockaddr_storage IP_SCOPE_PRIVATE = 7 // 10.x.x.x, 192.168.x.x, etc. }; - // Can be used with the unordered maps and sets in c++11. We don't use C++11 in the core - // but this is safe to put here. - struct Hasher - { - ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); } - }; + // 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(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; } + ZT_ALWAYS_INLINE InetAddress() { memset(reinterpret_cast(this),0,sizeof(InetAddress)); } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage &ss) { *this = ss; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage *ss) { *this = ss; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr &sa) { *this = sa; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr *sa) { *this = sa; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in &sa) { *this = sa; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in *sa) { *this = sa; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 &sa) { *this = sa; } + explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 *sa) { *this = sa; } ZT_ALWAYS_INLINE InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); } ZT_ALWAYS_INLINE InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); } - ZT_ALWAYS_INLINE InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); } + explicit 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; - } - - 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(this),0,sizeof(InetAddress)); } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage &ss) { - if (reinterpret_cast(&ss) != this) - memcpy(this,&ss,sizeof(InetAddress)); + memcpy(reinterpret_cast(this),&ss,sizeof(InetAddress)); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage *ss) { - if (reinterpret_cast(ss) != this) - memcpy(this,ss,sizeof(InetAddress)); + if (ss) + memcpy(reinterpret_cast(this),ss,sizeof(InetAddress)); + else memset(reinterpret_cast(this),0,sizeof(InetAddress)); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in &sa) { - if (reinterpret_cast(&sa) != this) { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in)); - } + copySockaddrToThis(&sa); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in *sa) { - if (reinterpret_cast(sa) != this) { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in)); - } + if (sa) + copySockaddrToThis(sa); + else memset(reinterpret_cast(this),0,sizeof(InetAddress)); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 &sa) { - if (reinterpret_cast(&sa) != this) { - memset(this,0,sizeof(InetAddress)); - memcpy(this,&sa,sizeof(struct sockaddr_in6)); - } + copySockaddrToThis(&sa); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 *sa) { - if (reinterpret_cast(sa) != this) { - memset(this,0,sizeof(InetAddress)); - memcpy(this,sa,sizeof(struct sockaddr_in6)); - } + if (sa) + copySockaddrToThis(sa); + else memset(reinterpret_cast(this),0,sizeof(InetAddress)); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr &sa) { - if (reinterpret_cast(&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(&sa)); + else if (sa.sa_family == AF_INET6) + copySockaddrToThis(reinterpret_cast(&sa)); + else memset(reinterpret_cast(this),0,sizeof(InetAddress)); return *this; } ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr *sa) { - if (reinterpret_cast(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(sa)); + else if (sa->sa_family == AF_INET6) + copySockaddrToThis(reinterpret_cast(sa)); + return *this; } + memset(reinterpret_cast(this),0,sizeof(InetAddress)); return *this; } @@ -468,16 +442,16 @@ struct InetAddress : public sockaddr_storage unsigned long h = 0; switch(ss_family) { case AF_INET: - h = (Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr) & 0xffffff00) >> 8; - h ^= (h >> 14); + h = (Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr) & 0xffffff00U) >> 8U; + h ^= (h >> 14U); break; case AF_INET6: { const uint8_t *ip = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); - h = ((unsigned long)ip[0]); h <<= 1; - h += ((unsigned long)ip[1]); h <<= 1; - h += ((unsigned long)ip[2]); h <<= 1; - h += ((unsigned long)ip[3]); h <<= 1; - h += ((unsigned long)ip[4]); h <<= 1; + h = ((unsigned long)ip[0]); h <<= 1U; + h += ((unsigned long)ip[1]); h <<= 1U; + h += ((unsigned long)ip[2]); h <<= 1U; + h += ((unsigned long)ip[3]); h <<= 1U; + h += ((unsigned long)ip[4]); h <<= 1U; h += ((unsigned long)ip[5]); } break; } @@ -487,10 +461,85 @@ struct InetAddress : public sockaddr_storage /** * @return True if address family is non-zero */ - ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); } + explicit ZT_ALWAYS_INLINE operator bool() const { return (ss_family != 0); } + + // Marshal interface /////////////////////////////////////////////////////// + static ZT_ALWAYS_INLINE int marshalSizeMax() { return 19; } + inline int marshal(uint8_t data[19]) const + { + unsigned int port; + switch(ss_family) { + case AF_INET: + port = Utils::ntoh((uint16_t)reinterpret_cast(this)->sin_port); + data[0] = 4; + data[1] = reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[0]; + data[2] = reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[1]; + data[3] = reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[2]; + data[4] = reinterpret_cast(&(reinterpret_cast(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(this)->sin6_port); + data[0] = 6; + for(int i=0;i<16;++i) + data[i+1] = reinterpret_cast(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(this),0,sizeof(InetAddress)); + reinterpret_cast(this)->sin_family = AF_INET; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[0] = data[1]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[1] = data[2]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[2] = data[3]; + reinterpret_cast(&(reinterpret_cast(this)->sin_addr.s_addr))[3] = data[4]; +#ifdef ZT_NO_TYPE_PUNNING + memcpy(&tmp,data + 5,2); + reinterpret_cast(this)->sin_port = tmp; +#else + reinterpret_cast(this)->sin_port = *((const uint16_t *)(data + 5)); +#endif + return 7; + case 6: + if (len < 19) + return -1; + memset(reinterpret_cast(this),0,sizeof(InetAddress)); + reinterpret_cast(this)->sin6_family = AF_INET6; + for(int i=0;i<16;i++) + (reinterpret_cast(this)->sin6_addr.s6_addr)[i] = data[i+1]; +#ifdef ZT_NO_TYPE_PUNNING + memcpy(&tmp,data + 17,2); + reinterpret_cast(this)->sin_port = tmp; +#else + reinterpret_cast(this)->sin_port = *((const uint16_t *)(data + 17)); +#endif + return 19; + default: + return -1; + } + } + //////////////////////////////////////////////////////////////////////////// template - ZT_ALWAYS_INLINE void serialize(Buffer &b) const + inline void serialize(Buffer &b) const { // This is used in the protocol and must be the same as describe in places // like VERB_HELLO in Packet.hpp. @@ -512,7 +561,7 @@ struct InetAddress : public sockaddr_storage } template - ZT_ALWAYS_INLINE unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) { memset(this,0,sizeof(InetAddress)); unsigned int p = startAt; @@ -607,6 +656,13 @@ struct InetAddress : public sockaddr_storage static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress); }; +static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in *p) { return reinterpret_cast(p); } +static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr_in6 *p) { return reinterpret_cast(p); } +static ZT_ALWAYS_INLINE InetAddress *asInetAddress(sockaddr *p) { return reinterpret_cast(p); } +static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in *p) { return reinterpret_cast(p); } +static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr_in6 *p) { return reinterpret_cast(p); } +static ZT_ALWAYS_INLINE const InetAddress *asInetAddress(const sockaddr *p) { return reinterpret_cast(p); } + } // namespace ZeroTier #endif diff --git a/node/Locator.cpp b/node/Locator.cpp new file mode 100644 index 000000000..dba50b86f --- /dev/null +++ b/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 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 diff --git a/node/Locator.hpp b/node/Locator.hpp index 06c278bfa..e18399884 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -14,367 +14,93 @@ #ifndef ZT_LOCATOR_HPP #define ZT_LOCATOR_HPP -#include "Constants.hpp" -#include "Identity.hpp" -#include "InetAddress.hpp" -#include "Utils.hpp" -#include "Buffer.hpp" -#include "SHA512.hpp" -#include "Str.hpp" -#include "ScopedPtr.hpp" -#include "SharedPtr.hpp" - #include #include +#include -// These are absolute maximums -- real locators are never this big -#define ZT_LOCATOR_MAX_PHYSICAL_ADDRESSES 64 -#define ZT_LOCATOR_MAX_VIRTUAL_ADDRESSES 64 +#include "Constants.hpp" +#include "Endpoint.hpp" +#include "Identity.hpp" + +#define ZT_LOCATOR_MAX_ENDPOINTS 8 +#define ZT_LOCATOR_MARSHAL_SIZE_MAX (8 + 2 + (ZT_ENDPOINT_MARSHAL_SIZE_MAX * ZT_LOCATOR_MAX_ENDPOINTS) + 2 + ZT_SIGNATURE_BUFFER_SIZE) namespace ZeroTier { /** * Signed information about a node's location on the network * - * A locator is a signed record that contains information about where a node - * may be found. It can contain static physical addresses or virtual ZeroTier - * addresses of nodes that can forward to the target node. Locator records - * can be stored in signed DNS TXT record sets, in LF by roots, in caches, - * etc. + * A locator contains long-lived endpoints for a node such as IP/port pairs, + * URLs, or other nodes, and is signed by the node it describes. */ class Locator { - friend class SharedPtr; - public: - ZT_ALWAYS_INLINE Locator() : _ts(0),_signatureLength(0) {} + ZT_ALWAYS_INLINE Locator() { this->clear(); } + /** + * Zero the Locator data structure + */ + ZT_ALWAYS_INLINE void clear() { memset(reinterpret_cast(this),0,sizeof(Locator)); } + + /** + * @return Timestamp (a.k.a. revision number) set by Location signer + */ ZT_ALWAYS_INLINE int64_t timestamp() const { return _ts; } - ZT_ALWAYS_INLINE const Identity &id() const { return _id; } - - ZT_ALWAYS_INLINE const std::vector &phy() const { return _physical; } - ZT_ALWAYS_INLINE const std::vector &virt() const { return _virtual; } /** - * Add a physical address to this locator (call before finish() to build a new Locator) + * @return True if locator is signed */ - ZT_ALWAYS_INLINE void add(const InetAddress &ip) - { - if (_physical.size() < ZT_LOCATOR_MAX_PHYSICAL_ADDRESSES) - _physical.push_back(ip); - } + ZT_ALWAYS_INLINE bool isSigned() const { return (_signatureLength > 0); } /** - * Add a forwarding ZeroTier node to this locator (call before finish() to build a new Locator) - */ - ZT_ALWAYS_INLINE void add(const Identity &zt) - { - if (_virtual.size() < ZT_LOCATOR_MAX_VIRTUAL_ADDRESSES) - _virtual.push_back(zt); - } - - /** - * Method to be called after add() is called for each address or forwarding node + * Add an endpoint to this locator * - * @param id Identity that this locator describes (must contain private key) - * @param ts Current time - * @return True if completion and signature were successful + * This doesn't check for the presence of the endpoint, so take + * care not to add duplicates. + * + * @param ep Endpoint to add + * @return True if endpoint was added (or already present), false if locator is full */ - ZT_ALWAYS_INLINE bool finish(const Identity &id,const int64_t ts) + ZT_ALWAYS_INLINE bool add(const Endpoint &ep) { - _ts = ts; - _id = id; - std::sort(_physical.begin(),_physical.end()); - _physical.erase(std::unique(_physical.begin(),_physical.end()),_physical.end()); - std::sort(_virtual.begin(),_virtual.end()); - _virtual.erase(std::unique(_virtual.begin(),_virtual.end()),_virtual.end()); - try { - ScopedPtr< Buffer<65536> > tmp(new Buffer<65536>()); - serialize(*tmp,true); - _signatureLength = id.sign(tmp->data(),tmp->size(),_signature,ZT_SIGNATURE_BUFFER_SIZE); - return (_signatureLength > 0); - } catch ( ... ) { + if (_endpointCount >= ZT_LOCATOR_MAX_ENDPOINTS) return false; - } + _at[_endpointCount++] = ep; + return true; } /** - * Verify this locator's signature against its embedded signing identity + * Sign this locator + * + * This sets timestamp, sorts endpoints so that the same set of endpoints + * will always produce the same locator, and signs. + * + * @param id Identity that includes private key + * @return True if signature successful */ - ZT_ALWAYS_INLINE bool verify() const - { - if ((_signatureLength == 0)||(_signatureLength > sizeof(_signature))) - return false; - try { - ScopedPtr< Buffer<65536> > tmp(new Buffer<65536>()); - serialize(*tmp,true); - return _id.verify(tmp->data(),tmp->size(),_signature,_signatureLength); - } catch ( ... ) { - return false; - } - } + bool sign(int64_t ts,const Identity &id); /** - * Make a DNS name contiaining a public key that can sign DNS entries + * Verify this Locator's validity and signature * - * This generates the initial fields of a DNS name that contains an - * encoded public key. Users may append any domain suffix to this name. - * - * @return First field(s) of DNS name + * @param id Identity corresponding to hash + * @return True if valid and signature checks out */ - static inline Str makeSecureDnsName(const uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE]) - { - uint8_t tmp[ZT_ECC384_PUBLIC_KEY_SIZE+2]; - memcpy(tmp,p384SigningKeyPublic,ZT_ECC384_PUBLIC_KEY_SIZE); - const uint16_t crc = Utils::crc16(tmp,ZT_ECC384_PUBLIC_KEY_SIZE); - tmp[ZT_ECC384_PUBLIC_KEY_SIZE-2] = (uint8_t)(crc >> 8); - tmp[ZT_ECC384_PUBLIC_KEY_SIZE-1] = (uint8_t)(crc); - Str name; - char b32[128]; - Utils::b32e(tmp,35,b32,sizeof(b32)); - name << "ztl-"; - name << b32; - Utils::b32e(tmp + 35,(ZT_ECC384_PUBLIC_KEY_SIZE+2) - 35,b32,sizeof(b32)); - name << ".ztl-"; - name << b32; - return name; - } + bool verify(const Identity &id) const; - /** - * This searches for an extracts a public key from a DNS name, if one is present. - * - * @return True if a key was found and successfully decoded - */ - static inline bool decodeSecureDnsName(const char *name,uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE]) - { - uint8_t b32[128]; - unsigned int b32ptr = 0; - char tmp[1024]; - Utils::scopy(tmp,sizeof(tmp),name); - bool ok = false; - for(char *saveptr=(char *)0,*p=Utils::stok(tmp,".",&saveptr);p;p=Utils::stok((char *)0,".",&saveptr)) { - if (b32ptr >= sizeof(b32)) - break; - if ((strlen(p) <= 4)||(memcmp(p,"ztl-",4) != 0)) - continue; - int s = Utils::b32d(p + 4,b32 + b32ptr,sizeof(b32) - b32ptr); - if (s > 0) { - b32ptr += (unsigned int)s; - if (b32ptr > 2) { - const uint16_t crc = Utils::crc16(b32,b32ptr); - if ((b32[b32ptr-2] == (uint8_t)(crc >> 8))&&(b32[b32ptr-1] == (uint8_t)(crc & 0xff))) { - ok = true; - break; - } - } - } else break; - } + explicit ZT_ALWAYS_INLINE operator bool() const { return (_ts != 0); } - if (ok) { - if (b32ptr == (ZT_ECC384_PUBLIC_KEY_SIZE + 2)) { - memcpy(p384SigningKeyPublic,b32,ZT_ECC384_PUBLIC_KEY_SIZE); - return true; - } - } - - return false; - } - - /** - * Make DNS TXT records for this locator - * - * DNS TXT records are signed by an entirely separate key that is added along - * with DNS names to nodes to allow them to verify DNS results. It's separate - * from the locator's signature so that a single DNS record can point to more - * than one locator or be served by things like geo-aware DNS. - * - * Right now only NIST P-384 is supported for signing DNS records. NIST EDDSA - * is used here so that FIPS-only nodes can always use DNS to locate roots as - * FIPS-only nodes may be required to disable non-FIPS algorithms. - */ - inline std::vector makeTxtRecords(const uint8_t p384SigningKeyPrivate[ZT_ECC384_PUBLIC_KEY_SIZE]) - { - uint8_t s384[48]; - char enc[256]; - - ScopedPtr< Buffer<65536> > tmp(new Buffer<65536>()); - serialize(*tmp,false); - SHA384(s384,tmp->data(),tmp->size()); - const unsigned int sigLocation = tmp->size(); - tmp->addSize(ZT_ECC384_SIGNATURE_SIZE); - ECC384ECDSASign(p384SigningKeyPrivate,s384,((uint8_t *)tmp->unsafeData()) + sigLocation); - - // Blob must be broken into multiple TXT records that must remain sortable so they are prefixed by a hex value. - // 186-byte chunks yield 248-byte base64 chunks which leaves some margin below the limit of 255. - std::vector txtRecords; - unsigned int txtRecNo = 0; - for(unsigned int p=0;psize();) { - unsigned int chunkSize = tmp->size() - p; - if (chunkSize > 186) chunkSize = 186; - - Utils::b64e(((const uint8_t *)tmp->data()) + p,chunkSize,enc,sizeof(enc)); - p += chunkSize; - - txtRecords.push_back(Str()); - txtRecords.back() << Utils::HEXCHARS[(txtRecNo >> 4) & 0xf] << Utils::HEXCHARS[txtRecNo & 0xf] << enc; - ++txtRecNo; - } - - return txtRecords; - } - - /** - * Decode TXT records - * - * TXT records can be provided as an iterator over std::string, Str, or char * - * values, and TXT records can be provided in any order. Any oversize or empty - * entries will be ignored. - * - * This method checks the decoded locator's signature using the supplied DNS TXT - * record signing public key. False is returned if the TXT records are invalid, - * incomplete, or fail signature check. If true is returned this Locator object - * now contains the contents of the supplied TXT records. - * - * @return True if new Locator is valid - */ - template - inline bool decodeTxtRecords(const Str &dnsName,I start,I end) - { - uint8_t dec[256],s384[48]; - try { - std::vector txtRecords; - while (start != end) { - try { - if (start->length() > 2) - txtRecords.push_back(*start); - } catch ( ... ) {} // skip any records that trigger out of bounds exceptions - ++start; - } - if (txtRecords.empty()) - return false; - std::sort(txtRecords.begin(),txtRecords.end()); - - ScopedPtr< Buffer<65536> > tmp(new Buffer<65536>()); - for(std::vector::const_iterator i(txtRecords.begin());i!=txtRecords.end();++i) - tmp->append(dec,Utils::b64d(i->c_str() + 2,dec,sizeof(dec))); - - uint8_t p384SigningKeyPublic[ZT_ECC384_PUBLIC_KEY_SIZE]; - if (decodeSecureDnsName(dnsName.c_str(),p384SigningKeyPublic)) { - if (tmp->size() <= ZT_ECC384_SIGNATURE_SIZE) - return false; - SHA384(s384,tmp->data(),tmp->size() - ZT_ECC384_SIGNATURE_SIZE); - if (!ECC384ECDSAVerify(p384SigningKeyPublic,s384,((const uint8_t *)tmp->data()) + (tmp->size() - ZT_ECC384_SIGNATURE_SIZE))) - return false; - } - - deserialize(*tmp,0); - - return verify(); - } catch ( ... ) { - return false; - } - } - - inline bool deserialize(const void *data,unsigned int len) - { - ScopedPtr< Buffer<65536> > tmp(new Buffer<65536>()); - tmp->append(data,len); - try { - deserialize(*tmp,0); - return true; - } catch ( ... ) { - return false; - } - } - - template - inline void serialize(Buffer &b,const bool forSign = false) const - { - if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); - - b.append((uint8_t)0); // version/flags, currently 0 - b.append((uint64_t)_ts); - _id.serialize(b,false); - b.append((uint8_t)_physical.size()); - for(std::vector::const_iterator i(_physical.begin());i!=_physical.end();++i) - i->serialize(b); - b.append((uint8_t)_virtual.size()); - for(std::vector::const_iterator i(_virtual.begin());i!=_virtual.end();++i) - i->serialize(b,false); - if (!forSign) { - b.append((uint16_t)_signatureLength); - b.append(_signature,_signatureLength); - } - b.append((uint16_t)0); // length of additional fields, currently 0 - - if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL); - } - - template - inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) - { - unsigned int p = startAt; - - if (b[p++] != 0) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE; - _ts = (int64_t)b.template at(p); p += 8; - p += _id.deserialize(b,p); - const unsigned int physicalCount = b[p++]; - _physical.resize(physicalCount); - for(unsigned int i=0;i(p); p += 2; - if (_signatureLength > ZT_SIGNATURE_BUFFER_SIZE) - throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; - memcpy(_signature,b.field(p,_signatureLength),_signatureLength); - p += _signatureLength; - p += b.template at(p) + 2; - - return (p - startAt); - } - - ZT_ALWAYS_INLINE operator bool() const { return (_id); } - - ZT_ALWAYS_INLINE bool addressesEqual(const Locator &l) const { return ((_physical == l._physical)&&(_virtual == l._virtual)); } - - ZT_ALWAYS_INLINE bool operator==(const Locator &l) const - { - return ( - (_ts == l._ts)&& - (_id == l._id)&& - (_physical == l._physical)&& - (_virtual == l._virtual)&& - (_signatureLength == l._signatureLength)&& - (memcmp(_signature,l._signature,_signatureLength) == 0)); - } - ZT_ALWAYS_INLINE bool operator!=(const Locator &l) const { return (!(*this == l)); } - ZT_ALWAYS_INLINE bool operator<(const Locator &l) const - { - if (_ts < l._ts) return true; else if (_ts > l._ts) return false; - if (_id < l._id) return true; else if (_id > l._id) return false; - if (_physical < l._physical) return true; else if (_physical > l._physical) return false; - if (_virtual < l._virtual) return true; else if (_virtual > l._virtual) return false; - if (_signatureLength < l._signatureLength) return true; - return (_signatureLength == l._signatureLength) ? (memcmp(_signature,l._signature,_signatureLength) < 0) : false; - } - ZT_ALWAYS_INLINE bool operator>(const Locator &l) const { return (l < *this); } - ZT_ALWAYS_INLINE bool operator<=(const Locator &l) const { return (!(l < *this)); } - ZT_ALWAYS_INLINE bool operator>=(const Locator &l) const { return (!(*this < l)); } - - ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)(_id.address().toInt() ^ (uint64_t)_ts); } + static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_LOCATOR_MARSHAL_SIZE_MAX; } + int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const; + int unmarshal(const uint8_t *restrict data,const int len); private: int64_t _ts; - Identity _id; - std::vector _physical; - std::vector _virtual; + unsigned int _endpointCount; unsigned int _signatureLength; + Endpoint _at[ZT_LOCATOR_MAX_ENDPOINTS]; uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE]; - AtomicCounter __refCount; }; } // namespace ZeroTier diff --git a/node/MAC.hpp b/node/MAC.hpp index d200a7cad..9ea4ba8c5 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -14,9 +14,9 @@ #ifndef ZT_MAC_HPP #define ZT_MAC_HPP -#include -#include -#include +#include +#include +#include #include "Constants.hpp" #include "Utils.hpp" @@ -187,7 +187,7 @@ public: ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_m; } - ZT_ALWAYS_INLINE char *toString(char buf[18]) const + inline char *toString(char buf[18]) const { buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf]; buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf]; diff --git a/node/Membership.hpp b/node/Membership.hpp index 506831d1b..260373a4b 100644 --- a/node/Membership.hpp +++ b/node/Membership.hpp @@ -14,10 +14,11 @@ #ifndef ZT_MEMBERSHIP_HPP #define ZT_MEMBERSHIP_HPP -#include +#include + +#include "../include/ZeroTierOne.h" #include "Constants.hpp" -#include "../include/ZeroTierOne.h" #include "Credential.hpp" #include "Hashtable.hpp" #include "CertificateOfMembership.hpp" @@ -69,21 +70,6 @@ public: */ ZT_ALWAYS_INLINE int64_t lastPushedCredentials() const { return _lastPushedCredentials; } - /** - * Check whether we should push MULTICAST_LIKEs to this peer, and update last sent time if true - * - * @param now Current time - * @return True if we should update multicasts - */ - ZT_ALWAYS_INLINE bool multicastLikeGate(const int64_t now) - { - if ((now - _lastUpdatedMulticast) >= ZT_MULTICAST_ANNOUNCE_PERIOD) { - _lastUpdatedMulticast = now; - return true; - } - return false; - } - /** * Check whether the peer represented by this Membership should be allowed on this network at all * @@ -97,11 +83,6 @@ public: return nconf.com.agreesWith(_com); // check timestamp agreement window } - /** - * @return True if this peer has sent us a valid certificate within ZT_PEER_ACTIVITY_TIMEOUT - */ - ZT_ALWAYS_INLINE bool recentlyAssociated(const int64_t now) const { return ((_com)&&((now - _com.timestamp()) < ZT_PEER_ACTIVITY_TIMEOUT)); } - /** * Check whether the peer represented by this Membership owns a given address * @@ -111,7 +92,7 @@ public: * @return True if this peer has a certificate of ownership for the given resource */ template - inline bool peerOwnsAddress(const NetworkConfig &nconf,const T &r) const + ZT_ALWAYS_INLINE bool peerOwnsAddress(const NetworkConfig &nconf,const T &r) const { if (_isUnspoofableAddress(nconf,r)) return true; @@ -132,7 +113,7 @@ public: * @param id Tag ID * @return Pointer to tag or NULL if not found */ - inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const + ZT_ALWAYS_INLINE const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const { const Tag *const t = _remoteTags.get(id); return (((t)&&(_isCredentialTimestampValid(nconf,*t))) ? t : (Tag *)0); @@ -150,7 +131,7 @@ public: * @param now Current time * @param nconf Current network configuration */ - void clean(const int64_t now,const NetworkConfig &nconf); + void clean(int64_t now,const NetworkConfig &nconf); /** * Generates a key for internal use in indexing credentials by type and credential ID @@ -181,8 +162,8 @@ private: // This returns true if a resource is an IPv6 NDP-emulated address. These embed the ZT // address of the peer and therefore cannot be spoofed, causing peerOwnsAddress() to // always return true for them. A certificate is not required for these. - inline bool _isUnspoofableAddress(const NetworkConfig &nconf,const MAC &m) const { return false; } - inline bool _isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const + ZT_ALWAYS_INLINE bool _isUnspoofableAddress(const NetworkConfig &nconf,const MAC &m) const { return false; } + ZT_ALWAYS_INLINE bool _isUnspoofableAddress(const NetworkConfig &nconf,const InetAddress &ip) const { if ((ip.isV6())&&(nconf.ndpEmulation())) { const InetAddress sixpl(InetAddress::makeIpv66plane(nconf.networkId,nconf.issuedTo.toInt())); @@ -223,7 +204,7 @@ private: // This compares the remote credential's timestamp to the timestamp in our network config // plus or minus the permitted maximum timestamp delta. template - inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const + ZT_ALWAYS_INLINE bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &remoteCredential) const { const int64_t ts = remoteCredential.timestamp(); if (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) { diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 7d9579131..e98c2321a 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -14,7 +14,7 @@ #ifndef ZT_MULTICASTGROUP_HPP #define ZT_MULTICASTGROUP_HPP -#include +#include #include "Constants.hpp" #include "MAC.hpp" @@ -79,27 +79,6 @@ public: */ ZT_ALWAYS_INLINE uint32_t adi() const { return _adi; } - /** - * @return 32-bit hash ID of this multicast group - */ - ZT_ALWAYS_INLINE uint32_t id() const - { - uint64_t m = _mac.toInt(); - uint32_t x1 = _adi; - uint32_t x2 = (uint32_t)(m >> 32); - uint32_t x3 = (uint32_t)m; - x1 = ((x1 >> 16) ^ x1) * 0x45d9f3b; - x2 = ((x2 >> 16) ^ x2) * 0x45d9f3b; - x3 = ((x3 >> 16) ^ x3) * 0x45d9f3b; - x1 = ((x1 >> 16) ^ x1) * 0x45d9f3b; - x2 = ((x2 >> 16) ^ x2) * 0x45d9f3b; - x3 = ((x3 >> 16) ^ x3) * 0x45d9f3b; - x1 = (x1 >> 16) ^ x1; - x2 = (x2 >> 16) ^ x2; - x3 = (x3 >> 16) ^ x3; - return (x1 ^ x2 ^ x3); - } - ZT_ALWAYS_INLINE bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); } ZT_ALWAYS_INLINE bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); } ZT_ALWAYS_INLINE bool operator<(const MulticastGroup &g) const diff --git a/node/Mutex.hpp b/node/Mutex.hpp index c631f13d2..2c5370391 100644 --- a/node/Mutex.hpp +++ b/node/Mutex.hpp @@ -16,20 +16,21 @@ #include "Constants.hpp" +#include +#include + #ifdef __UNIX_LIKE__ -#include -#include #include namespace ZeroTier { -// libpthread based mutex lock class Mutex { public: - ZT_ALWAYS_INLINE Mutex() { pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0); } + ZT_ALWAYS_INLINE Mutex() { pthread_mutex_init(&_mh,0); } ZT_ALWAYS_INLINE ~Mutex() { pthread_mutex_destroy(&_mh); } + ZT_ALWAYS_INLINE void lock() const { pthread_mutex_lock(&((const_cast (this))->_mh)); } ZT_ALWAYS_INLINE void unlock() const { pthread_mutex_unlock(&((const_cast (this))->_mh)); } @@ -45,23 +46,59 @@ public: private: ZT_ALWAYS_INLINE Mutex(const Mutex &) {} - const Mutex &operator=(const Mutex &) { return *this; } + ZT_ALWAYS_INLINE const Mutex &operator=(const Mutex &) { return *this; } pthread_mutex_t _mh; }; +class RWMutex +{ +public: + ZT_ALWAYS_INLINE RWMutex() { pthread_rwlock_init(&_mh,0); } + ZT_ALWAYS_INLINE ~RWMutex() { pthread_rwlock_destroy(&_mh); } + + ZT_ALWAYS_INLINE void lock() const { pthread_rwlock_wrlock(&((const_cast (this))->_mh)); } + ZT_ALWAYS_INLINE void rlock() const { pthread_rwlock_rdlock(&((const_cast (this))->_mh)); } + ZT_ALWAYS_INLINE void unlock() const { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } + ZT_ALWAYS_INLINE void runlock() const { pthread_rwlock_unlock(&((const_cast (this))->_mh)); } + + class RLock + { + public: + ZT_ALWAYS_INLINE RLock(RWMutex &m) : _m(&m) { m.rlock(); } + ZT_ALWAYS_INLINE RLock(const RWMutex &m) : _m(const_cast(&m)) { _m->rlock(); } + ZT_ALWAYS_INLINE ~RLock() { _m->runlock(); } + private: + RWMutex *const _m; + }; + + class Lock + { + public: + ZT_ALWAYS_INLINE Lock(RWMutex &m) : _m(&m) { m.lock(); } + ZT_ALWAYS_INLINE Lock(const RWMutex &m) : _m(const_cast(&m)) { _m->lock(); } + ZT_ALWAYS_INLINE ~Lock() { _m->unlock(); } + private: + RWMutex *const _m; + }; + +private: + ZT_ALWAYS_INLINE RWMutex(const RWMutex &) {} + ZT_ALWAYS_INLINE const RWMutex &operator=(const RWMutex &) { return *this; } + + pthread_rwlock_t _mh; +}; + } // namespace ZeroTier #endif #ifdef __WINDOWS__ -#include #include namespace ZeroTier { -// Windows critical section based lock class Mutex { public: @@ -84,7 +121,7 @@ public: private: ZT_ALWAYS_INLINE Mutex(const Mutex &) {} - const Mutex &operator=(const Mutex &) { return *this; } + ZT_ALWAYS_INLINE const Mutex &operator=(const Mutex &) { return *this; } CRITICAL_SECTION _cs; }; diff --git a/node/Network.cpp b/node/Network.cpp index 8e3732833..bc256454b 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -11,10 +11,10 @@ */ /****/ -#include -#include -#include -#include +#include +#include +#include +#include #include "../include/ZeroTierDebug.h" @@ -566,9 +566,9 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1); if (n > 1) { try { - ScopedPtr nconf(new NetworkConfig()); - if (nconf->fromDictionary(*dict)) { - this->setConfiguration(tPtr,*nconf,false); + ScopedPtr nconf2(new NetworkConfig()); + if (nconf2->fromDictionary(*dict)) { + this->setConfiguration(tPtr,*nconf2,false); _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated got = true; } @@ -671,8 +671,8 @@ bool Network::filterOutgoingPacket( } break; case DOZTFILTER_DROP: - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); return false; case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter() @@ -712,17 +712,17 @@ bool Network::filterOutgoingPacket( outp.compress(); RR->sw->send(tPtr,outp,true); - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); return false; // DROP locally, since we redirected } else { - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,1); return true; } } else { - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(localCapabilityIndex >= 0) ? &crrl : (Trace::RuleResultLog *)0,(localCapabilityIndex >= 0) ? &(_config.capabilities[localCapabilityIndex]) : (Capability *)0,ztSource,ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,noTee,false,0); return false; } } @@ -793,8 +793,8 @@ int Network::filterIncomingPacket( } break; case DOZTFILTER_DROP: - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(Trace::RuleResultLog *)0,(Capability *)0,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0); return 0; // DROP case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter() @@ -832,14 +832,14 @@ int Network::filterIncomingPacket( outp.compress(); RR->sw->send(tPtr,outp,true); - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,0); return 0; // DROP locally, since we redirected } } - if (_config.remoteTraceTarget) - RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,accept); + //if (_config.remoteTraceTarget) + // RR->t->networkFilter(tPtr,*this,rrl,(c) ? &crrl : (Trace::RuleResultLog *)0,c,sourcePeer->address(),ztDest,macSource,macDest,frameData,frameLen,etherType,vlanId,false,true,accept); return accept; } @@ -1018,10 +1018,6 @@ bool Network::gate(void *tPtr,const SharedPtr &peer) if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) { if (!m) m = &(_memberships[peer->address()]); - if (m->multicastLikeGate(now)) { - Mutex::Lock l2(_myMulticastGroups_l); - _announceMulticastGroupsTo(tPtr,peer->address(),_allMulticastGroups()); - } return true; } } @@ -1051,6 +1047,8 @@ void Network::doPeriodicTasks(void *tPtr,const int64_t now) { Mutex::Lock l2(_myMulticastGroups_l); + // TODO + /* Hashtable< MulticastGroup,uint64_t >::Iterator i(_multicastGroupsBehindMe); MulticastGroup *mg = (MulticastGroup *)0; uint64_t *ts = (uint64_t *)0; @@ -1060,6 +1058,7 @@ void Network::doPeriodicTasks(void *tPtr,const int64_t now) } _announceMulticastGroups(tPtr,false); + */ } } } @@ -1374,11 +1373,14 @@ void Network::_announceMulticastGroups(void *tPtr,bool force) Membership *m = (Membership *)0; Hashtable::Iterator i(_memberships); while (i.next(a,m)) { + // TODO + /* bool announce = m->multicastLikeGate(now); // force this to be called even if 'force' is true since it updates last push time if ((!announce)&&(force)) announce = true; if ((announce)&&(m->isAllowedOnNetwork(_config))) _announceMulticastGroupsTo(tPtr,*a,groups); + */ } } } diff --git a/node/Network.hpp b/node/Network.hpp index 8d0d5bb5f..43333725f 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -14,7 +14,7 @@ #ifndef ZT_NETWORK_HPP #define ZT_NETWORK_HPP -#include +#include #include "../include/ZeroTierOne.h" @@ -61,7 +61,7 @@ public: /** * Compute primary controller device ID from network ID */ - static ZT_ALWAYS_INLINE Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); } + static inline Address controllerFor(uint64_t nwid) { return Address(nwid >> 24); } /** * Construct a new network @@ -79,14 +79,14 @@ public: ~Network(); - ZT_ALWAYS_INLINE uint64_t id() const { return _id; } - ZT_ALWAYS_INLINE Address controller() const { return Address(_id >> 24); } - ZT_ALWAYS_INLINE bool multicastEnabled() const { return (_config.multicastLimit > 0); } - ZT_ALWAYS_INLINE bool hasConfig() const { return (_config); } - ZT_ALWAYS_INLINE uint64_t lastConfigUpdate() const { return _lastConfigUpdate; } - ZT_ALWAYS_INLINE ZT_VirtualNetworkStatus status() const { return _status(); } - ZT_ALWAYS_INLINE const NetworkConfig &config() const { return _config; } - ZT_ALWAYS_INLINE const MAC &mac() const { return _mac; } + inline uint64_t id() const { return _id; } + inline Address controller() const { return Address(_id >> 24); } + inline bool multicastEnabled() const { return (_config.multicastLimit > 0); } + inline bool hasConfig() const { return (_config); } + inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; } + inline ZT_VirtualNetworkStatus status() const { return _status(); } + inline const NetworkConfig &config() const { return _config; } + inline const MAC &mac() const { return _mac; } /** * Apply filters to an outgoing packet @@ -158,7 +158,7 @@ public: * @param includeBridgedGroups If true, also check groups we've learned via bridging * @return True if this network endpoint / peer is a member */ - ZT_ALWAYS_INLINE bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const + inline bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const { Mutex::Lock l(_myMulticastGroups_l); if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) @@ -174,7 +174,7 @@ public: * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param mg New multicast group */ - ZT_ALWAYS_INLINE void multicastSubscribe(void *tPtr,const MulticastGroup &mg) + inline void multicastSubscribe(void *tPtr,const MulticastGroup &mg) { Mutex::Lock l(_myMulticastGroups_l); if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) { @@ -189,7 +189,7 @@ public: * * @param mg Multicast group */ - ZT_ALWAYS_INLINE void multicastUnsubscribe(const MulticastGroup &mg) + inline void multicastUnsubscribe(const MulticastGroup &mg) { Mutex::Lock l(_myMulticastGroups_l); std::vector::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)); @@ -230,12 +230,12 @@ public: /** * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this */ - ZT_ALWAYS_INLINE void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; } + inline void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; } /** * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this */ - ZT_ALWAYS_INLINE void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; } + inline void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; } /** * Determine whether this peer is permitted to communicate on this network @@ -256,7 +256,7 @@ public: * @param mac MAC address * @return ZeroTier address of bridge to this MAC */ - ZT_ALWAYS_INLINE Address findBridgeTo(const MAC &mac) const + inline Address findBridgeTo(const MAC &mac) const { Mutex::Lock _l(_remoteBridgeRoutes_l); const Address *const br = _remoteBridgeRoutes.get(mac); @@ -283,7 +283,7 @@ public: * @param mg Multicast group * @param now Current time */ - ZT_ALWAYS_INLINE void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now) + inline void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now) { Mutex::Lock l(_myMulticastGroups_l); _multicastGroupsBehindMe.set(mg,now); @@ -292,7 +292,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com) + inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com) { if (com.networkId() != _id) return Membership::ADD_REJECTED; @@ -303,7 +303,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap) + inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap) { if (cap.networkId() != _id) return Membership::ADD_REJECTED; @@ -314,7 +314,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag) + inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag) { if (tag.networkId() != _id) return Membership::ADD_REJECTED; @@ -330,7 +330,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo) + inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo) { if (coo.networkId() != _id) return Membership::ADD_REJECTED; @@ -345,7 +345,7 @@ public: * @param to Destination peer address * @param now Current time */ - ZT_ALWAYS_INLINE void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now) + inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now) { Mutex::Lock _l(_memberships_l); _memberships[to].pushCredentials(RR,tPtr,now,to,_config); @@ -358,7 +358,7 @@ public: * @param to Destination peer address * @param now Current time */ - ZT_ALWAYS_INLINE void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now) + inline void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now) { const int64_t tout = std::min(_config.credentialTimeMaxDelta,(int64_t)ZT_PEER_ACTIVITY_TIMEOUT); Mutex::Lock _l(_memberships_l); @@ -373,7 +373,7 @@ public: * This sets the network to completely remove itself on delete. This also prevents the * call of the normal port shutdown event on delete. */ - ZT_ALWAYS_INLINE void destroy() + inline void destroy() { _memberships_l.lock(); _config_l.lock(); @@ -387,7 +387,7 @@ public: * * @param ec Buffer to fill with externally-visible network configuration */ - ZT_ALWAYS_INLINE void externalConfig(ZT_VirtualNetworkConfig *ec) const + inline void externalConfig(ZT_VirtualNetworkConfig *ec) const { Mutex::Lock _l(_config_l); _externalConfig(ec); @@ -399,7 +399,7 @@ public: * @param f Function of (const Address,const Membership) */ template - ZT_ALWAYS_INLINE void eachMember(F f) + inline void eachMember(F f) { Mutex::Lock ml(_memberships_l); Hashtable::Iterator i(_memberships); @@ -414,7 +414,7 @@ public: /** * @return Externally usable pointer-to-pointer exported via the core API */ - ZT_ALWAYS_INLINE void **userPtr() { return &_uPtr; } + inline void **userPtr() { return &_uPtr; } private: void _requestConfiguration(void *tPtr); @@ -440,7 +440,7 @@ private: struct _IncomingConfigChunk { - ZT_ALWAYS_INLINE _IncomingConfigChunk() : ts(0),updateId(0),haveChunks(0),haveBytes(0),data() {} + inline _IncomingConfigChunk() : ts(0),updateId(0),haveChunks(0),haveBytes(0),data() {} uint64_t ts; uint64_t updateId; uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS]; diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 6b0fe90d4..c992591f0 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -11,7 +11,7 @@ */ /****/ -#include +#include #include @@ -35,7 +35,6 @@ bool NetworkConfig::toDictionary(Dictionary &d,b if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo.toString(tmp2))) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false; - if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TOKEN,this->token)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false; if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false; @@ -138,7 +137,6 @@ bool NetworkConfig::fromDictionary(const Dictionaryflags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0); - this->token = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TOKEN,0); this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE); if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 140efcfc9..efcc4fcff 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -14,9 +14,9 @@ #ifndef ZT_NETWORKCONFIG_HPP #define ZT_NETWORKCONFIG_HPP -#include -#include -#include +#include +#include +#include #include #include @@ -72,11 +72,6 @@ namespace ZeroTier { */ #define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL -/** - * Flag: disable frame compression - */ -#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL - /** * Device can bridge to other Ethernet networks and gets unknown recipient multicasts */ @@ -88,7 +83,7 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR 0x0000040000000000ULL /** - * Device that can probe and receive remote trace info about this network + * Device that is allowed to remotely debug connectivity on this network */ #define ZT_NETWORKCONFIG_SPECIALIST_TYPE_DIAGNOSTICIAN 0x0000080000000000ULL @@ -142,8 +137,6 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f" // integer(hex) #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" -// integer(hex) -#define ZT_NETWORKCONFIG_DICT_KEY_TOKEN "k" // network type (hex) #define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t" // text @@ -177,7 +170,7 @@ namespace ZeroTier { */ struct NetworkConfig { - ZT_ALWAYS_INLINE NetworkConfig() : + inline NetworkConfig() : networkId(0), timestamp(0), credentialTimeMaxDelta(0), @@ -218,44 +211,28 @@ struct NetworkConfig /** * @return True if broadcast (ff:ff:ff:ff:ff:ff) address should work on this network */ - ZT_ALWAYS_INLINE bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); } + inline bool enableBroadcast() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); } /** * @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns */ - ZT_ALWAYS_INLINE bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); } - - /** - * @return True if frames should not be compressed - */ - ZT_ALWAYS_INLINE bool disableCompression() const - { -#ifndef ZT_DISABLE_COMPRESSION - return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); -#else - /* Compression is disabled for libzt builds since it causes non-obvious chaotic - interference with lwIP's TCP congestion algorithm. Compression is also disabled - for some NAS builds due to the usage of low-performance processors in certain - older and budget models. */ - return true; -#endif - } + inline bool ndpEmulation() const { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); } /** * @return Network type is public (no access control) */ - ZT_ALWAYS_INLINE bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); } + inline bool isPublic() const { return (this->type == ZT_NETWORK_TYPE_PUBLIC); } /** * @return Network type is private (certificate access control) */ - ZT_ALWAYS_INLINE bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); } + inline bool isPrivate() const { return (this->type == ZT_NETWORK_TYPE_PRIVATE); } /** * @param fromPeer Peer attempting to bridge other Ethernet peers onto network * @return True if this network allows bridging */ - ZT_ALWAYS_INLINE bool permitsBridging(const Address &fromPeer) const + inline bool permitsBridging(const Address &fromPeer) const { for(unsigned int i=0;i +#include #include "Constants.hpp" #include "Dictionary.hpp" diff --git a/node/Node.cpp b/node/Node.cpp index e64abba85..ab4d00b73 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -11,16 +11,13 @@ */ /****/ -#include -#include -#include -#include -#include +#include +#include +#include #include "Constants.hpp" #include "SharedPtr.hpp" #include "Node.hpp" -#include "RuntimeEnvironment.hpp" #include "NetworkController.hpp" #include "Switch.hpp" #include "Topology.hpp" @@ -40,20 +37,18 @@ namespace ZeroTier { /* Public Node interface (C++, exposed via CAPI bindings) */ /****************************************************************************/ -Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now) : +Node::Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now) : _RR(this), RR(&_RR), - _uPtr(uptr), + _cb(*callbacks), + _uPtr(uPtr), _networks(8), _now(now), _lastPing(0), _lastHousekeepingRun(0), _lastNetworkHousekeepingRun(0), - _lastDynamicRootUpdate(0), _online(false) { - memcpy(&_cb,callbacks,sizeof(ZT_Node_Callbacks)); - memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr)); memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo)); memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification)); @@ -61,7 +56,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 uint64_t idtmp[2]; idtmp[0] = 0; idtmp[1] = 0; char tmp[2048]; - int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,tmp,sizeof(tmp) - 1); + int n = stateObjectGet(tPtr, ZT_STATE_OBJECT_IDENTITY_SECRET, idtmp, tmp, sizeof(tmp) - 1); if (n > 0) { tmp[n] = (char)0; if (RR->identity.fromString(tmp)) { @@ -77,14 +72,14 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 RR->identity.toString(false,RR->publicIdentityStr); RR->identity.toString(true,RR->secretIdentityStr); idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0; - stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,idtmp,RR->secretIdentityStr,(unsigned int)strlen(RR->secretIdentityStr)); - stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_SECRET, idtmp, RR->secretIdentityStr, (unsigned int)strlen(RR->secretIdentityStr)); + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int)strlen(RR->publicIdentityStr)); } else { idtmp[0] = RR->identity.address().toInt(); idtmp[1] = 0; - n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,tmp,sizeof(tmp) - 1); + n = stateObjectGet(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, tmp, sizeof(tmp) - 1); if ((n > 0)&&(n < (int)sizeof(RR->publicIdentityStr))&&(n < (int)sizeof(tmp))) { - if (memcmp(tmp,RR->publicIdentityStr,n)) - stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,idtmp,RR->publicIdentityStr,(unsigned int)strlen(RR->publicIdentityStr)); + if (memcmp(tmp,RR->publicIdentityStr,n) != 0) + stateObjectPut(tPtr, ZT_STATE_OBJECT_IDENTITY_PUBLIC, idtmp, RR->publicIdentityStr, (unsigned int)strlen(RR->publicIdentityStr)); } } @@ -95,11 +90,11 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 const unsigned long topologys = sizeof(Topology) + (((sizeof(Topology) & 0xf) != 0) ? (16 - (sizeof(Topology) & 0xf)) : 0); const unsigned long sas = sizeof(SelfAwareness) + (((sizeof(SelfAwareness) & 0xf) != 0) ? (16 - (sizeof(SelfAwareness) & 0xf)) : 0); - m = reinterpret_cast(::malloc(16 + ts + sws + topologys + sas)); + m = reinterpret_cast(malloc(16 + ts + sws + topologys + sas)); if (!m) throw std::bad_alloc(); RR->rtmem = m; - while (((uintptr_t)m & 0xf) != 0) ++m; + while (((uintptr_t)m & 0xfU) != 0) ++m; RR->t = new (m) Trace(RR); m += ts; @@ -117,7 +112,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 throw; } - postEvent(tptr,ZT_EVENT_UP); + postEvent(tPtr, ZT_EVENT_UP); } Node::~Node() @@ -130,7 +125,7 @@ Node::~Node() if (RR->topology) RR->topology->~Topology(); if (RR->sw) RR->sw->~Switch(); if (RR->t) RR->t->~Trace(); - ::free(RR->rtmem); + free(RR->rtmem); } ZT_ResultCode Node::processWirePacket( @@ -169,74 +164,42 @@ ZT_ResultCode Node::processVirtualNetworkFrame( } } -// This is passed as the argument to the DNS request handler and -// aggregates results. -struct _processBackgroundTasks_dnsResultAccumulator -{ - _processBackgroundTasks_dnsResultAccumulator(const Str &n) : dnsName(n) {} - Str dnsName; - std::vector txtRecords; -}; - -static const ZT_DNSRecordType s_txtRecordType[1] = { ZT_DNS_RECORD_TXT }; - -struct _processBackgroundTasks_eachRootName -{ - ZT_Node_Callbacks *cb; - Node *n; - void *uPtr; - void *tPtr; - bool updateAll; - - ZT_ALWAYS_INLINE bool operator()(const Str &dnsName,const Locator &loc) - { - if ((strchr(dnsName.c_str(),'.'))&&((updateAll)||(!loc))) { - _processBackgroundTasks_dnsResultAccumulator *dnsReq = new _processBackgroundTasks_dnsResultAccumulator(dnsName); - cb->dnsResolver(reinterpret_cast(n),uPtr,tPtr,s_txtRecordType,1,dnsName.c_str(),(uintptr_t)dnsReq); - } - return true; - } -}; - -struct _processBackgroundTasks_ping_eachRoot -{ - Hashtable< void *,bool > roots; - int64_t now; - void *tPtr; - bool online; - - ZT_ALWAYS_INLINE bool operator()(const SharedPtr &peer,const std::vector &addrs) - { - unsigned int v4SendCount = 0,v6SendCount = 0; - peer->ping(tPtr,now,v4SendCount,v6SendCount); - for(std::vector::const_iterator a(addrs.begin());a!=addrs.end();++a) { - if ( ((a->isV4())&&(v4SendCount == 0)) || ((a->isV6())&&(v6SendCount == 0)) ) - peer->sendHELLO(tPtr,-1,*a,now); - } - if (!online) - online = ((now - peer->lastReceive()) <= ((ZT_PEER_PING_PERIOD * 2) + 5000)); - roots.set((void *)peer.ptr(),true); - return true; - } -}; - +// This function object is run past every peer every ZT_PEER_PING_PERIOD. struct _processBackgroundTasks_ping_eachPeer { int64_t now; + Node *parent; void *tPtr; - Hashtable< void *,bool > *roots; - - ZT_ALWAYS_INLINE bool operator()(const SharedPtr &peer) + bool online; + ZT_ALWAYS_INLINE bool operator()(const SharedPtr &peer,const bool isRoot) { - if (!roots->contains((void *)peer.ptr())) { - unsigned int v4SendCount = 0,v6SendCount = 0; - peer->ping(tPtr,now,v4SendCount,v6SendCount); + unsigned int v4SendCount = 0,v6SendCount = 0; + peer->ping(tPtr,now,v4SendCount,v6SendCount,isRoot); + + if (isRoot) { + if ((now - peer->lastReceive()) <= (ZT_PEER_PING_PERIOD + 5000)) + online = true; + + if (v4SendCount == 0) { + InetAddress try4; + parent->externalPathLookup(tPtr,peer->identity(),AF_INET,try4); + if (try4.ss_family == AF_INET) + peer->sendHELLO(tPtr,-1,try4,now); + } + + if (v6SendCount == 0) { + InetAddress try6; + parent->externalPathLookup(tPtr,peer->identity(),AF_INET6,try6); + if (try6.ss_family == AF_INET6) + peer->sendHELLO(tPtr,-1,try6,now); + } } + return true; } }; -ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline) +ZT_ResultCode Node::processBackgroundTasks(void *tPtr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline) { _now = now; Mutex::Lock bl(_backgroundTasksLock); @@ -252,41 +215,19 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 if ((now - _lastPing) >= ZT_PEER_PING_PERIOD) { _lastPing = now; try { - // Periodically refresh locators for dynamic roots from their DNS names. - if (_cb.dnsResolver) { - _processBackgroundTasks_eachRootName cr; - cr.cb = &_cb; - cr.n = this; - cr.uPtr = _uPtr; - cr.tPtr = tptr; - if ((now - _lastDynamicRootUpdate) >= ZT_DYNAMIC_ROOT_UPDATE_PERIOD) { - _lastDynamicRootUpdate = now; - cr.updateAll = true; - } else { - cr.updateAll = false; - } - RR->topology->eachRootName(cr); - } - - // Ping each root explicitly no matter what - _processBackgroundTasks_ping_eachRoot rf; - rf.now = now; - rf.tPtr = tptr; - rf.online = false; - RR->topology->eachRoot(rf); - - // Ping peers that are active and we want to keep alive _processBackgroundTasks_ping_eachPeer pf; pf.now = now; - pf.tPtr = tptr; - pf.roots = &rf.roots; - RR->topology->eachPeer(pf); + pf.parent = this; + pf.tPtr = tPtr; + pf.online = false; + RR->topology->eachPeerWithRoot<_processBackgroundTasks_ping_eachPeer &>(pf); - // Update online status based on whether we can reach a root - if (rf.online != _online) { - _online = rf.online; - postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE); + if (pf.online != _online) { + _online = pf.online; + postEvent(tPtr, _online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE); } + + RR->topology->rankRoots(now); } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } @@ -300,7 +241,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 uint64_t *nwid = (uint64_t *)0; SharedPtr *network = (SharedPtr *)0; while (i.next(nwid,network)) { - (*network)->doPeriodicTasks(tptr,now); + (*network)->doPeriodicTasks(tPtr, now); } } } @@ -332,7 +273,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 } try { - *nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min((unsigned long)ZT_MAX_TIMER_TASK_INTERVAL,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_MIN_TIMER_TASK_INTERVAL); + *nextBackgroundTaskDeadline = now + (int64_t)std::max(std::min((unsigned long)ZT_MAX_TIMER_TASK_INTERVAL,RR->sw->doTimerTasks(tPtr, now)), (unsigned long)ZT_MIN_TIMER_TASK_INTERVAL); } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } @@ -340,30 +281,6 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 return ZT_RESULT_OK; } -void Node::processDNSResult( - void *tptr, - uintptr_t dnsRequestID, - const char *name, - enum ZT_DNSRecordType recordType, - const void *result, - unsigned int resultLength, - int resultIsString) -{ - if (dnsRequestID) { - _processBackgroundTasks_dnsResultAccumulator *const acc = reinterpret_cast<_processBackgroundTasks_dnsResultAccumulator *>(dnsRequestID); - if (recordType == ZT_DNS_RECORD_TXT) { - if (result) - acc->txtRecords.emplace_back(reinterpret_cast(result)); - } else if (recordType == ZT_DNS_RECORD__END_OF_RESULTS) { - Locator loc; - if (loc.decodeTxtRecords(acc->dnsName,acc->txtRecords.begin(),acc->txtRecords.end())) { - RR->topology->setRoot(acc->dnsName,loc); - delete acc; - } - } - } -} - ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr) { Mutex::Lock _l(_networks_m); @@ -380,7 +297,6 @@ ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr) { Mutex::Lock _l(_networks_m); SharedPtr *nw = _networks.get(nwid); - RR->sw->removeNetworkQoSControlBlock(nwid); if (!nw) return ZT_RESULT_OK; if (uptr) @@ -423,44 +339,25 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u } else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; } -ZT_RootList *Node::listRoots(int64_t now) +ZT_ResultCode Node::addRoot(const char *identity) { - return RR->topology->apiRoots(now); -} - -enum ZT_ResultCode Node::setRoot(const char *name,const void *locator,unsigned int locatorSize) -{ - try { - Locator loc; - if ((locator)&&(locatorSize > 0)&&(locatorSize < 65535)) { - ScopedPtr< Buffer<65536> > locbuf(new Buffer<65536>()); - locbuf->append(locator,locatorSize); - loc.deserialize(*locbuf,0); - if (!loc.verify()) - return ZT_RESULT_ERROR_BAD_PARAMETER; - } - Str n; - if ((!name)||(strlen(name) == 0)) { - if (!loc) - return ZT_RESULT_ERROR_BAD_PARAMETER; /* no name and no locator */ - char tmp[16]; - loc.id().address().toString(tmp); - n = tmp; - } else { - n = name; - } - return RR->topology->setRoot(n,loc) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED; - } catch ( ... ) { + if (!identity) return ZT_RESULT_ERROR_BAD_PARAMETER; - } + Identity id; + if (!id.fromString(identity)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + RR->topology->addRoot(id); + return ZT_RESULT_OK; } -enum ZT_ResultCode Node::removeRoot(const char *name) +ZT_ResultCode Node::removeRoot(const char *identity) { - try { - if (name) - RR->topology->removeRoot(Str(name)); - } catch ( ... ) {} + if (!identity) + return ZT_RESULT_ERROR_BAD_PARAMETER; + Identity id; + if (!id.fromString(identity)) + return ZT_RESULT_ERROR_BAD_PARAMETER; + RR->topology->removeRoot(id); return ZT_RESULT_OK; } @@ -472,6 +369,7 @@ uint64_t Node::address() const void Node::status(ZT_NodeStatus *status) const { status->address = RR->identity.address().toInt(); + status->identity = reinterpret_cast(&RR->identity); status->publicIdentity = RR->publicIdentityStr; status->secretIdentity = RR->secretIdentityStr; status->online = _online ? 1 : 0; @@ -485,35 +383,37 @@ ZT_PeerList *Node::peers() const RR->topology->getAllPeers(peers); std::sort(peers.begin(),peers.end(),_sortPeerPtrsByAddress()); - char *buf = (char *)::malloc(sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size())); + char *buf = (char *)::malloc(sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size()) + (sizeof(Identity) * peers.size())); if (!buf) return (ZT_PeerList *)0; ZT_PeerList *pl = (ZT_PeerList *)buf; pl->peers = (ZT_Peer *)(buf + sizeof(ZT_PeerList)); + Identity *identities = (Identity *)(buf + sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size())); + const int64_t now = _now; pl->peerCount = 0; for(std::vector< SharedPtr >::iterator pi(peers.begin());pi!=peers.end();++pi) { - ZT_Peer *p = &(pl->peers[pl->peerCount++]); + ZT_Peer *p = &(pl->peers[pl->peerCount]); + p->address = (*pi)->address().toInt(); - p->hadAggregateLink = 0; + identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted + p->identity = &identities[pl->peerCount]; if ((*pi)->remoteVersionKnown()) { - p->versionMajor = (*pi)->remoteVersionMajor(); - p->versionMinor = (*pi)->remoteVersionMinor(); - p->versionRev = (*pi)->remoteVersionRevision(); + p->versionMajor = (int)(*pi)->remoteVersionMajor(); + p->versionMinor = (int)(*pi)->remoteVersionMinor(); + p->versionRev = (int)(*pi)->remoteVersionRevision(); } else { p->versionMajor = -1; p->versionMinor = -1; p->versionRev = -1; } - p->latency = (*pi)->latency(_now); + p->latency = (int)(*pi)->latency(); if (p->latency >= 0xffff) p->latency = -1; - p->role = RR->topology->isRoot((*pi)->identity()) ? ZT_PEER_ROLE_PLANET : ZT_PEER_ROLE_LEAF; + p->role = RR->topology->isRoot((*pi)->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF; - const int64_t now = _now; - std::vector< SharedPtr > paths((*pi)->paths(_now)); - SharedPtr bestp((*pi)->getAppropriatePath(_now,false)); - p->hadAggregateLink |= (*pi)->hasAggregateLink(); + std::vector< SharedPtr > paths; + (*pi)->getAllPaths(paths); p->pathCount = 0; for(std::vector< SharedPtr >::iterator path(paths.begin());path!=paths.end();++path) { memcpy(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage)); @@ -521,20 +421,11 @@ ZT_PeerList *Node::peers() const p->paths[p->pathCount].lastReceive = (*path)->lastIn(); p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address()); p->paths[p->pathCount].alive = (*path)->alive(now) ? 1 : 0; - p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0; - p->paths[p->pathCount].latency = (float)(*path)->latency(); - p->paths[p->pathCount].packetDelayVariance = (*path)->packetDelayVariance(); - p->paths[p->pathCount].throughputDisturbCoeff = (*path)->throughputDisturbanceCoefficient(); - p->paths[p->pathCount].packetErrorRatio = (*path)->packetErrorRatio(); - p->paths[p->pathCount].packetLossRatio = (*path)->packetLossRatio(); - p->paths[p->pathCount].stability = (*path)->lastComputedStability(); - p->paths[p->pathCount].throughput = (*path)->meanThroughput(); - p->paths[p->pathCount].maxThroughput = (*path)->maxLifetimeThroughput(); - p->paths[p->pathCount].allocation = (float)(*path)->allocation() / (float)255; - p->paths[p->pathCount].ifname = (*path)->getName(); - + p->paths[p->pathCount].preferred = (p->pathCount == 0) ? 1 : 0; ++p->pathCount; } + + ++pl->peerCount; } return pl; @@ -631,33 +522,6 @@ void Node::setController(void *networkControllerInstance) /* Node methods used only within node/ */ /****************************************************************************/ -SharedPtr< const Locator > Node::locator() -{ - Mutex::Lock lck(_locator_m); - if (!_locator) { - Locator *l = new Locator(); - try { - RR->topology->eachRoot([l](const SharedPtr &p,const std::vector &phyAddr) -> bool { - l->add(p->identity()); - return true; - }); - { - Mutex::Lock lck2(_localInterfaceAddresses_m); - for(std::vector< ZT_InterfaceAddress >::const_iterator a(_localInterfaceAddresses.begin());a!=_localInterfaceAddresses.end();++a) { - if (a->permanent != 0) { - l->add(*reinterpret_cast(&(a->address))); - } - } - } - } catch ( ... ) { - delete l; - throw; - } - _locator.set(l); - } - return _locator; -} - bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress) { if (!Path::isAddressValidForPath(remoteAddress)) @@ -679,6 +543,21 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast(&remoteAddress)) != 0) : true); } +bool Node::externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr) +{ + if (_cb.pathLookupFunction) { + return (_cb.pathLookupFunction( + reinterpret_cast(this), + _uPtr, + tPtr, + id.address().toInt(), + reinterpret_cast(&id), + family, + reinterpret_cast(&addr)) == ZT_RESULT_OK); + } + return false; +} + ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork, const ZT_PhysicalPathConfiguration *pathConfig) { RR->topology->setPhysicalPathConfiguration(pathNetwork,pathConfig); @@ -870,21 +749,6 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,int64 } } -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) -{ - try { - reinterpret_cast(node)->processDNSResult(tptr,dnsRequestID,name,recordType,result,resultLength,resultIsString); - } catch ( ... ) {} -} - enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr) { try { @@ -929,19 +793,10 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint } } -ZT_RootList *ZT_Node_listRoots(ZT_Node *node,int64_t now) +enum ZT_ResultCode ZT_Node_addRoot(ZT_Node *node,const char *identity) { try { - return reinterpret_cast(node)->listRoots(now); - } catch ( ... ) { - return nullptr; - } -} - -enum ZT_ResultCode ZT_Node_setRoot(ZT_Node *node,const char *name,const void *locator,unsigned int locatorSize) -{ - try { - return reinterpret_cast(node)->setRoot(name,locator,locatorSize); + return reinterpret_cast(node)->addRoot(identity); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; } catch ( ... ) { @@ -949,10 +804,10 @@ enum ZT_ResultCode ZT_Node_setRoot(ZT_Node *node,const char *name,const void *lo } } -enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *name) +enum ZT_ResultCode ZT_Node_removeRoot(ZT_Node *node,const char *identity) { try { - return reinterpret_cast(node)->removeRoot(name); + return reinterpret_cast(node)->removeRoot(identity); } catch (std::bad_alloc &exc) { return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY; } catch ( ... ) { diff --git a/node/Node.hpp b/node/Node.hpp index 16e6824fc..e09279fb4 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -14,9 +14,9 @@ #ifndef ZT_NODE_HPP #define ZT_NODE_HPP -#include -#include -#include +#include +#include +#include #include #include @@ -51,7 +51,7 @@ class Locator; class Node : public NetworkController::Sender { public: - Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64_t now); + Node(void *uPtr, void *tPtr, const struct ZT_Node_Callbacks *callbacks, int64_t now); virtual ~Node(); // Get rid of alignment warnings on 32-bit Windows and possibly improve performance @@ -81,22 +81,13 @@ public: const void *frameData, unsigned int frameLength, volatile int64_t *nextBackgroundTaskDeadline); - ZT_ResultCode processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline); - void processDNSResult( - void *tptr, - uintptr_t dnsRequestID, - const char *name, - enum ZT_DNSRecordType recordType, - const void *result, - unsigned int resultLength, - int resultIsString); + ZT_ResultCode processBackgroundTasks(void *tPtr, int64_t now, volatile int64_t *nextBackgroundTaskDeadline); ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr); ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr); ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi); - ZT_RootList *listRoots(int64_t now); - enum ZT_ResultCode setRoot(const char *name,const void *locator,unsigned int locatorSize); - enum ZT_ResultCode removeRoot(const char *name); + ZT_ResultCode addRoot(const char *identity); + ZT_ResultCode removeRoot(const char *identity); uint64_t address() const; void status(ZT_NodeStatus *status) const; ZT_PeerList *peers() const; @@ -104,8 +95,7 @@ public: ZT_VirtualNetworkList *networks() const; void setNetworkUserPtr(uint64_t nwid,void *ptr); void freeQueryResult(void *qr); - int addLocalInterfaceAddress(const struct sockaddr_storage *addr); - void clearLocalInterfaceAddresses(); + void setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int addrCount); int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len); void setController(void *networkControllerInstance); @@ -151,33 +141,12 @@ public: return SharedPtr(); } - ZT_ALWAYS_INLINE bool belongsToNetwork(uint64_t nwid) const - { - Mutex::Lock _l(_networks_m); - return _networks.contains(nwid); - } - - ZT_ALWAYS_INLINE std::vector< SharedPtr > allNetworks() const - { - std::vector< SharedPtr > nw; - Mutex::Lock _l(_networks_m); - Hashtable< uint64_t,SharedPtr >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr > * >(&_networks)); - uint64_t *k = (uint64_t *)0; - SharedPtr *v = (SharedPtr *)0; - while (i.next(k,v)) - nw.push_back(*v); - return nw; - } - ZT_ALWAYS_INLINE std::vector directPaths() const { Mutex::Lock _l(_localInterfaceAddresses_m); return _localInterfaceAddresses; } - void setInterfaceAddresses(const ZT_InterfaceAddress *addrs,unsigned int addrCount); - - SharedPtr< const Locator > locator(); ZT_ALWAYS_INLINE void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast(this),_uPtr,tPtr,ev,md); } ZT_ALWAYS_INLINE void configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { _cb.virtualNetworkConfigFunction(reinterpret_cast(this),_uPtr,tPtr,nwid,nuptr,op,nc); } ZT_ALWAYS_INLINE bool online() const { return _online; } @@ -185,7 +154,7 @@ public: ZT_ALWAYS_INLINE void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast(this),_uPtr,tPtr,type,id,data,(int)len); } ZT_ALWAYS_INLINE void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id[2]) { _cb.statePutFunction(reinterpret_cast(this),_uPtr,tPtr,type,id,(const void *)0,-1); } bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress); - ZT_ALWAYS_INLINE bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast(&addr)) != 0) : false ); } + bool externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr); ZT_ResultCode setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig); ZT_ALWAYS_INLINE const Identity &identity() const { return _RR.identity; } @@ -200,7 +169,7 @@ public: */ ZT_ALWAYS_INLINE void expectReplyTo(const uint64_t packetId) { - const unsigned long pid2 = (unsigned long)(packetId >> 32); + const unsigned long pid2 = (unsigned long)(packetId >> 32U); const unsigned long bucket = (unsigned long)(pid2 & ZT_EXPECTING_REPLIES_BUCKET_MASK1); _expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = (uint32_t)pid2; } @@ -247,10 +216,7 @@ public: virtual void ncSendRevocation(const Address &destination,const Revocation &rev); virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode); - ZT_ALWAYS_INLINE void setMultipathMode(uint8_t mode) { _multipathMode = mode; } - ZT_ALWAYS_INLINE uint8_t getMultipathMode() { return _multipathMode; } - - ZT_ALWAYS_INLINE bool localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const + inline bool localControllerHasAuthorized(const int64_t now,const uint64_t nwid,const Address &addr) const { _localControllerAuthorizations_m.lock(); const int64_t *const at = _localControllerAuthorizations.get(_LocalControllerAuth(nwid,addr)); @@ -260,11 +226,14 @@ public: return false; } + inline void setMultipathMode(uint8_t mode) { _multipathMode = mode; } + inline uint8_t getMultipathMode() { return _multipathMode; } + private: RuntimeEnvironment _RR; RuntimeEnvironment *RR; - void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P ZT_Node_Callbacks _cb; + void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P // For tracking packet IDs to filter out OK/ERROR replies to packets we did not send uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1]; @@ -288,12 +257,10 @@ private: }; Hashtable< _LocalControllerAuth,int64_t > _localControllerAuthorizations; Hashtable< uint64_t,SharedPtr > _networks; - SharedPtr< const Locator > _locator; std::vector< ZT_InterfaceAddress > _localInterfaceAddresses; Mutex _localControllerAuthorizations_m; Mutex _networks_m; - Mutex _locator_m; Mutex _localInterfaceAddresses_m; Mutex _backgroundTasksLock; @@ -303,7 +270,6 @@ private: int64_t _lastPing; int64_t _lastHousekeepingRun; int64_t _lastNetworkHousekeepingRun; - int64_t _lastDynamicRootUpdate; bool _online; }; diff --git a/node/OS.hpp b/node/OS.hpp new file mode 100644 index 000000000..025c59d19 --- /dev/null +++ b/node/OS.hpp @@ -0,0 +1,176 @@ +/* + * 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_OS_HPP +#define ZT_OS_HPP + +// +// 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 +#endif + +#ifdef __APPLE__ +#include +#ifndef __UNIX_LIKE__ +#define __UNIX_LIKE__ +#endif +#ifndef __BSD__ +#define __BSD__ +#endif +#include +#endif + +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#ifndef __UNIX_LIKE__ +#define __UNIX_LIKE__ +#endif +#ifndef __BSD__ +#define __BSD__ +#endif +#include +#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 +#include +#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 +#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 __attribute__((always_inline)) +#ifndef restrict +#define restrict __restrict__ +#endif +#ifndef likely +#define likely(x) __builtin_expect((x),1) +#endif +#ifndef unlikely +#define unlikely(x) __builtin_expect((x),0) +#endif +#else /* not GCC-like */ +#ifndef restrict +#define restrict +#endif +#ifndef likely +#define 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 + +#ifndef ZT_ALWAYS_INLINE +#define ZT_ALWAYS_INLINE inline +#endif + +#endif diff --git a/node/Packet.hpp b/node/Packet.hpp index 6595dce3b..b8aeffe3d 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -14,9 +14,9 @@ #ifndef ZT_N_PACKET_HPP #define ZT_N_PACKET_HPP -#include -#include -#include +#include +#include +#include #include #include @@ -53,7 +53,7 @@ * 8 - 1.1.17 ... 1.2.0 * + Multipart network configurations for large network configs * + Tags and Capabilities - * + ZT_ALWAYS_INLINE push of CertificateOfMembership deprecated + * + inline push of CertificateOfMembership deprecated * 9 - 1.2.0 ... 1.2.14 * 10 - 1.4.0 ... 1.6.0 * + Multipath capability and load balancing @@ -305,14 +305,14 @@ public: class Fragment : public Buffer { public: - ZT_ALWAYS_INLINE Fragment() : + inline Fragment() : Buffer() {} template - ZT_ALWAYS_INLINE Fragment(const Buffer &b) : + inline Fragment(const Buffer &b) : Buffer(b) {} - ZT_ALWAYS_INLINE Fragment(const void *data,unsigned int len) : + inline Fragment(const void *data,unsigned int len) : Buffer(data,len) {} /** @@ -324,7 +324,7 @@ public: * @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off) * @param fragTotal Total number of fragments (including 0) */ - ZT_ALWAYS_INLINE Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal) + inline Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal) { init(p,fragStart,fragLen,fragNo,fragTotal); } @@ -338,7 +338,7 @@ public: * @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off) * @param fragTotal Total number of fragments (including 0) */ - ZT_ALWAYS_INLINE void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal) + inline void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal) { if ((fragStart + fragLen) > p.size()) throw ZT_EXCEPTION_OUT_OF_BOUNDS; @@ -359,37 +359,37 @@ public: * * @return Destination ZT address */ - ZT_ALWAYS_INLINE Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } + inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * @return True if fragment is of a valid length */ - ZT_ALWAYS_INLINE bool lengthValid() const { return (size() >= ZT_PACKET_FRAGMENT_IDX_PAYLOAD); } + inline bool lengthValid() const { return (size() >= ZT_PACKET_FRAGMENT_IDX_PAYLOAD); } /** * @return ID of packet this is a fragment of */ - ZT_ALWAYS_INLINE uint64_t packetId() const { return at(ZT_PACKET_FRAGMENT_IDX_PACKET_ID); } + inline uint64_t packetId() const { return at(ZT_PACKET_FRAGMENT_IDX_PACKET_ID); } /** * @return Total number of fragments in packet */ - ZT_ALWAYS_INLINE unsigned int totalFragments() const { return (((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) >> 4) & 0xf); } + inline unsigned int totalFragments() const { return (((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) >> 4) & 0xf); } /** * @return Fragment number of this fragment */ - ZT_ALWAYS_INLINE unsigned int fragmentNumber() const { return ((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) & 0xf); } + inline unsigned int fragmentNumber() const { return ((unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO]) & 0xf); } /** * @return Fragment ZT hop count */ - ZT_ALWAYS_INLINE unsigned int hops() const { return (unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]); } + inline unsigned int hops() const { return (unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]); } /** * Increment this packet's hop count */ - ZT_ALWAYS_INLINE unsigned int incrementHops() + inline unsigned int incrementHops() { return (unsigned int)((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS] = (((*this)[ZT_PACKET_FRAGMENT_IDX_HOPS]) + 1)); } @@ -397,12 +397,12 @@ public: /** * @return Length of payload in bytes */ - ZT_ALWAYS_INLINE unsigned int payloadLength() const { return ((size() > ZT_PACKET_FRAGMENT_IDX_PAYLOAD) ? (size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD) : 0); } + inline unsigned int payloadLength() const { return ((size() > ZT_PACKET_FRAGMENT_IDX_PAYLOAD) ? (size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD) : 0); } /** * @return Raw packet payload */ - ZT_ALWAYS_INLINE const unsigned char *payload() const { return field(ZT_PACKET_FRAGMENT_IDX_PAYLOAD,size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD); } + inline const unsigned char *payload() const { return field(ZT_PACKET_FRAGMENT_IDX_PAYLOAD,size() - ZT_PACKET_FRAGMENT_IDX_PAYLOAD); } }; /** @@ -422,15 +422,32 @@ public: * <[1] software minor version> * <[2] software revision> * <[8] timestamp for determining latency> - * <[...] binary serialized identity (see Identity)> + * <[...] binary serialized identity> * <[...] physical destination address of packet> + * [... begin encrypted region ...] + * <[2] 16-bit reserved (legacy) field, always 0> + * <[2] 16-bit length of meta-data dictionary> + * <[...] meta-data dictionary> + * [... end encrypted region ...] + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> * - * HELLO is sent in the clear as it is how peers share their identity - * public keys. + * HELLO is sent with authentication but without the usual encryption so + * that peers can exchange identities. * * Destination address is the actual wire address to which the packet * was sent. See InetAddress::serialize() for format. * + * Starting at "begin encrypted section" the reset of the packet is + * encrypted with Salsa20/12. This is not the normal packet encryption + * and is technically not necessary as nothing in HELLO is secret. It + * exists merely to shield meta-data info from passive listeners to + * slightly improve privacy, and for backward compatibility with older + * nodes that required it. + * + * HELLO (and its OK response) ends with a large 384-bit HMAC to allow + * identity exchanges to be authenticated with additional strength beyond + * ordinary packet authentication. + * * OK payload: * <[8] HELLO timestamp field echo> * <[1] protocol version> @@ -438,6 +455,10 @@ public: * <[1] software minor version> * <[2] software revision> * <[...] physical destination address of packet> + * <[2] 16-bit reserved (legacy) field, always 0> + * <[2] 16-bit length of meta-data dictionary> + * <[...] meta-data dictionary> + * <[48] HMAC-SHA384 of all fields to this point (as plaintext)> * * With the exception of the timestamp, the other fields pertain to the * respondent who is sending OK and are not echoes. @@ -568,13 +589,16 @@ public: VERB_ECHO = 0x08, /** - * Announce interest in multicast group(s) (DEPRECATED): + * Announce interest in multicast group(s): * <[8] 64-bit network ID> * <[6] multicast Ethernet address> * <[4] multicast additional distinguishing information (ADI)> * [... additional tuples of network/address/adi ...] * - * OK/ERROR are not generated. + * LIKEs may be sent to any peer, though a good implementation should + * restrict them to peers on the same network they're for and to network + * controllers and root servers. In the current network, root servers + * will provide the service of final multicast cache. */ VERB_MULTICAST_LIKE = 0x09, @@ -682,6 +706,73 @@ public: */ VERB_NETWORK_CONFIG = 0x0c, + /** + * Request endpoints for multicast distribution: + * <[8] 64-bit network ID> + * <[1] flags> + * <[6] MAC address of multicast group being queried> + * <[4] 32-bit ADI for multicast group being queried> + * <[4] 32-bit requested max number of multicast peers> + * + * This message asks a peer for additional known endpoints that have + * LIKEd a given multicast group. It's sent when the sender wishes + * to send multicast but does not have the desired number of recipient + * peers. + * + * OK response payload: (multiple OKs can be generated) + * <[8] 64-bit network ID> + * <[6] MAC address of multicast group being queried> + * <[4] 32-bit ADI for multicast group being queried> + * <[4] 32-bit total number of known members in this multicast group> + * <[2] 16-bit number of members enumerated in this packet> + * <[...] series of 5-byte ZeroTier addresses of enumerated members> + * + * ERROR is not generated; queries that return no response are dropped. + */ + VERB_MULTICAST_GATHER = 0x0d, + + /** *** DEPRECATED *** + * Multicast frame: + * <[8] 64-bit network ID> + * <[1] flags> + * [<[4] 32-bit implicit gather limit>] + * [<[6] source MAC>] + * <[6] destination MAC (multicast address)> + * <[4] 32-bit multicast ADI (multicast address extension)> + * <[2] 16-bit ethertype> + * <[...] ethernet payload> + * + * Flags: + * 0x01 - Network certificate of membership attached (DEPRECATED) + * 0x02 - Implicit gather limit field is present + * 0x04 - Source MAC is specified -- otherwise it's computed from sender + * 0x08 - Please replicate (sent to multicast replicators) + * + * OK and ERROR responses are optional. OK may be generated if there are + * implicit gather results or if the recipient wants to send its own + * updated certificate of network membership to the sender. ERROR may be + * generated if a certificate is needed or if multicasts to this group + * are no longer wanted (multicast unsubscribe). + * + * OK response payload: + * <[8] 64-bit network ID> + * <[6] MAC address of multicast group> + * <[4] 32-bit ADI for multicast group> + * <[1] flags> + * [<[...] network certificate of membership (DEPRECATED)>] + * [<[...] implicit gather results if flag 0x01 is set>] + * + * OK flags (same bits as request flags): + * 0x01 - OK includes certificate of network membership (DEPRECATED) + * 0x02 - OK includes implicit gather results + * + * ERROR response payload: + * <[8] 64-bit network ID> + * <[6] multicast group MAC> + * <[4] 32-bit multicast group ADI> + */ + VERB_MULTICAST_FRAME = 0x0e, + /** * Push of potential endpoints for direct communication: * <[2] 16-bit number of paths> @@ -772,110 +863,30 @@ public: VERB_USER_MESSAGE = 0x14, /** - * A trace for remote debugging or diagnostics: - * <[...] null-terminated dictionary containing trace information> - * [<[...] additional null-terminated dictionaries>] - * - * This message contains a remote trace event. Remote trace events can - * be sent to observers configured at the network level for those that - * pertain directly to activity on a network, or to global observers if - * locally configured. + * Encapsulate a ZeroTier packet for multicast distribution: + * [... begin signed portion ...] + * <[1] 8-bit flags> + * <[5] 40-bit ZeroTier address of sender> + * <[2] 16-bit length of inner payload> + * <[1] inner payload verb> + * <[...] inner payload data> + * [... end signed portion ...] + * <[2] 16-bit length of signature or 0 if un-signed> + * [<[...] optional signature of multicast>] + * <[...] address (min prefix) list> */ - VERB_REMOTE_TRACE = 0x15, + VERB_MULTICAST = 0x16, /** - * A signed locator for this node: - * <[8] 64-bit flags> - * <[2] 16-bit length of locator> - * <[...] serialized locator> + * Encapsulate a full ZeroTier packet in another: + * <[...] raw encapsulated packet> * - * This message is sent in response to OK(HELLO) and can be pushed - * opportunitistically. Its payload is a signed Locator object that - * attests to where and how this Node may be reached. A locator can - * contain static IPs/ports or other ZeroTier nodes that can be used - * to reach this one. - * - * These Locator objects can be stored e.g. by roots in LF to publish - * node reachability. Since they're signed any node can verify that - * the originating node approves of their content. + * Encapsulation exists to enable secure relaying as opposed to the usual + * "dumb" relaying. The latter is faster but secure relaying has roles + * where endpoint privacy is desired. Multiply nested ENCAP packets + * could allow ZeroTier to act as an onion router. */ - VERB_SET_LOCATOR = 0x16, - - /** - * A list of peers this node will relay traffic to/from: - * <[2] 16-bit number of peers> - * <[16] 128-bit hash of node public key> - * <[2] 16-bit latency to node or 0 if unspecified> - * <[4] 32-bit max bandwidth in megabits or 0 if unspecified> - * [<[...] additional hash,latency,bandwidth tuples>] - * - * This messages can be pushed to indicate that this peer is willing - * to relay traffic to other peers. It contains a list of 128-bit - * hashes (the first 128 bits of a SHA512) of identity public keys - * of currently reachable and willing-to-relay-for nodes. - * - * This can be used to initiate mesh-like behavior in ZeroTier. The - * peers for which this node is willing to relay are reported as - * hashes of their identity public keys. This prevents this message - * from revealing explicit information about linked peers. The - * receiving peer can only "see" a will-relay entry if it knows the - * identity of the peer it is trying to reach. - */ - VERB_WILL_RELAY = 0x17, - - /** - * Multipurpose VL2 network multicast: - * <[5] start of range of addresses for propagation> - * <[5] end of range of addresses for propagation> - * <[1] 8-bit propagation depth / hops> - * <[1] 8-bit length of bloom filter in 256-byte/2048-bit chunks> - * <[...] propagation bloom filter> - * [... start of signed portion ...] - * <[8] 64-bit timestamp> - * <[8] 64-bit network ID> - * <[5] 40-bit address of sender> - * <[2] 16-bit length of multicast payload> - * [... start multicast payload ...] - * <[1] 8-bit payload type> - * [... end multicast payload and signed portion ...] - * <[2] 16-bit length of signature or 0 if not present> - * <[...] signature of signed portion> - * - * Payload type 0x00: multicast frame: - * <[6] MAC address of multicast group> - * <[4] 32-bit ADI of multicast group> - * <[6] 48-bit source MAC of packet or all 0 if from sender> - * <[2] 16-bit ethertype> - * <[...] ethernet payload> - * - * Payload type 0x01: multicast subscribe: - * <[2] 16-bit number of multicast group IDs to subscribe> - * <[...] series of 32-bit multicast group IDs> - * - * Payload type 0x02: multicast unsubscribe: - * <[2] 16-bit number of multicast group IDs to unsubscribe> - * <[...] series of 32-bit multicast group IDs> - * - * This is the common packet structure for VL2 network-level multicasts - * and is used for multicast frames, multicast group subscribe and - * unsubscribe, and could be used in the future for other purposes such - * as credential propagation or diagnostics. - * - * The header contains an address range, bloom filter, and depth/hop - * counter. The bloom filter tracks which nodes have seen this multicast, - * with bits being set prior to send. The range allows the total set of - * subscribers to be partitioned in the case of huge networks that would - * saturate the bloom filter or have collisions. The propagation depth - * allows propagation to stop at some maximum value, and the value 0xff - * can be used to indicate that further propagation is not desired. - * - * Logic connected to the parsing of the multicast payload will determine - * whether or not and to whom this multicast is propagated. Subscribe and - * unsubscribe messages are propagated to online nodes up to a maximum - * depth, while frames have the added constraint of being propagated only - * to nodes that subscribe to the target multicast group. - */ - VERB_VL2_MULTICAST = 0x18, + VERB_ENCAP = 0x17 // protocol max: 0x1f }; @@ -915,12 +926,12 @@ public: }; template - ZT_ALWAYS_INLINE Packet(const Buffer &b) : + inline Packet(const Buffer &b) : Buffer(b) { } - ZT_ALWAYS_INLINE Packet(const void *data,unsigned int len) : + inline Packet(const void *data,unsigned int len) : Buffer(data,len) { } @@ -932,7 +943,7 @@ public: * Use the header access methods (setDestination() and friends) to fill out * the header. Payload should be appended; initial size is header size. */ - ZT_ALWAYS_INLINE Packet() : + inline Packet() : Buffer(ZT_PROTO_MIN_PACKET_LENGTH) { setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); @@ -948,7 +959,7 @@ public: * @param prototype Prototype packet * @param dest Destination ZeroTier address for new packet */ - ZT_ALWAYS_INLINE Packet(const Packet &prototype,const Address &dest) : + inline Packet(const Packet &prototype,const Address &dest) : Buffer(prototype) { setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); @@ -962,7 +973,7 @@ public: * @param source Source ZT address * @param v Verb */ - ZT_ALWAYS_INLINE Packet(const Address &dest,const Address &source,const Verb v) : + inline Packet(const Address &dest,const Address &source,const Verb v) : Buffer(ZT_PROTO_MIN_PACKET_LENGTH) { setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); @@ -979,7 +990,7 @@ public: * @param source Source ZT address * @param v Verb */ - ZT_ALWAYS_INLINE void reset(const Address &dest,const Address &source,const Verb v) + inline void reset(const Address &dest,const Address &source,const Verb v) { setSize(ZT_PROTO_MIN_PACKET_LENGTH); setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); @@ -996,52 +1007,52 @@ public: * technically different but otherwise identical copies of the same * packet. */ - ZT_ALWAYS_INLINE void newInitializationVector() { setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); } + inline void newInitializationVector() { setAt(ZT_PACKET_IDX_IV,Packet::nextPacketId()); } /** * Set this packet's destination * * @param dest ZeroTier address of destination */ - ZT_ALWAYS_INLINE void setDestination(const Address &dest) { dest.copyTo(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } + inline void setDestination(const Address &dest) { dest.copyTo(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * Set this packet's source * * @param source ZeroTier address of source */ - ZT_ALWAYS_INLINE void setSource(const Address &source) { source.copyTo(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } + inline void setSource(const Address &source) { source.copyTo(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * Get this packet's destination * * @return Destination ZT address */ - ZT_ALWAYS_INLINE Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } + inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * Get this packet's source * * @return Source ZT address */ - ZT_ALWAYS_INLINE Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } + inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); } /** * @return True if packet is of valid length */ - ZT_ALWAYS_INLINE bool lengthValid() const { return (size() >= ZT_PROTO_MIN_PACKET_LENGTH); } + inline bool lengthValid() const { return (size() >= ZT_PROTO_MIN_PACKET_LENGTH); } /** * @return True if packet is fragmented (expect fragments) */ - ZT_ALWAYS_INLINE bool fragmented() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0); } + inline bool fragmented() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0); } /** * Set this packet's fragmented flag * * @param f Fragmented flag value */ - ZT_ALWAYS_INLINE void setFragmented(bool f) + inline void setFragmented(bool f) { if (f) (*this)[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_FRAGMENTED; @@ -1051,17 +1062,17 @@ public: /** * @return True if compressed (result only valid if unencrypted) */ - ZT_ALWAYS_INLINE bool compressed() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_VERB] & ZT_PROTO_VERB_FLAG_COMPRESSED) != 0); } + inline bool compressed() const { return (((unsigned char)(*this)[ZT_PACKET_IDX_VERB] & ZT_PROTO_VERB_FLAG_COMPRESSED) != 0); } /** * @return ZeroTier forwarding hops (0 to 7) */ - ZT_ALWAYS_INLINE unsigned int hops() const { return ((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x07); } + inline unsigned int hops() const { return ((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x07); } /** * Increment this packet's hop count */ - ZT_ALWAYS_INLINE unsigned char incrementHops() + inline unsigned char incrementHops() { unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS]; const unsigned char h = (b + 1) & 0x07; @@ -1072,7 +1083,7 @@ public: /** * @return Cipher suite selector: 0 - 7 (see #defines) */ - ZT_ALWAYS_INLINE unsigned int cipher() const + inline unsigned int cipher() const { return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3); } @@ -1080,7 +1091,7 @@ public: /** * Set this packet's cipher suite */ - ZT_ALWAYS_INLINE void setCipher(unsigned int c) + inline void setCipher(unsigned int c) { unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS]; b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH @@ -1091,14 +1102,14 @@ public: * * @return Trusted path ID (from MAC field) */ - ZT_ALWAYS_INLINE uint64_t trustedPathId() const { return at(ZT_PACKET_IDX_MAC); } + inline uint64_t trustedPathId() const { return at(ZT_PACKET_IDX_MAC); } /** * Set this packet's trusted path ID and set the cipher spec to trusted path * * @param tpid Trusted path ID */ - ZT_ALWAYS_INLINE void setTrusted(const uint64_t tpid) + inline void setTrusted(const uint64_t tpid) { setCipher(ZT_PROTO_CIPHER_SUITE__NONE); setAt(ZT_PACKET_IDX_MAC,tpid); @@ -1115,7 +1126,7 @@ public: * * @return Packet ID */ - ZT_ALWAYS_INLINE uint64_t packetId() const { return at(ZT_PACKET_IDX_IV); } + inline uint64_t packetId() const { return at(ZT_PACKET_IDX_IV); } /** * Set packet verb @@ -1125,22 +1136,22 @@ public: * * @param v New packet verb */ - ZT_ALWAYS_INLINE void setVerb(Verb v) { (*this)[ZT_PACKET_IDX_VERB] = (char)v; } + inline void setVerb(Verb v) { (*this)[ZT_PACKET_IDX_VERB] = (char)v; } /** * @return Packet verb (not including flag bits) */ - ZT_ALWAYS_INLINE Verb verb() const { return (Verb)((*this)[ZT_PACKET_IDX_VERB] & 0x1f); } + inline Verb verb() const { return (Verb)((*this)[ZT_PACKET_IDX_VERB] & 0x1f); } /** * @return Length of packet payload */ - ZT_ALWAYS_INLINE unsigned int payloadLength() const { return ((size() < ZT_PROTO_MIN_PACKET_LENGTH) ? 0 : (size() - ZT_PROTO_MIN_PACKET_LENGTH)); } + inline unsigned int payloadLength() const { return ((size() < ZT_PROTO_MIN_PACKET_LENGTH) ? 0 : (size() - ZT_PROTO_MIN_PACKET_LENGTH)); } /** * @return Raw packet payload */ - ZT_ALWAYS_INLINE const unsigned char *payload() const { return field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD); } + inline const unsigned char *payload() const { return field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD); } /** * Armor packet for transport @@ -1200,7 +1211,7 @@ private: * @param in Input key (32 bytes) * @param out Output buffer (32 bytes) */ - ZT_ALWAYS_INLINE void _salsa20MangleKey(const unsigned char *in,unsigned char *out) const + inline void _salsa20MangleKey(const unsigned char *in,unsigned char *out) const { const unsigned char *d = (const unsigned char *)data(); diff --git a/node/Path.cpp b/node/Path.cpp index e209e9c04..aa420459c 100644 --- a/node/Path.cpp +++ b/node/Path.cpp @@ -26,4 +26,35 @@ bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigne return false; } +bool Path::isAddressValidForPath(const InetAddress &a) +{ + if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) { + switch(a.ipScope()) { + /* Note: we don't do link-local at the moment. Unfortunately these + * cause several issues. The first is that they usually require a + * device qualifier, which we don't handle yet and can't portably + * push in PUSH_DIRECT_PATHS. The second is that some OSes assign + * these very ephemerally or otherwise strangely. So we'll use + * private, pseudo-private, shared (e.g. carrier grade NAT), or + * global IP addresses. */ + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + if (a.ss_family == AF_INET6) { + // TEMPORARY HACK: for now, we are going to blacklist he.net IPv6 + // tunnels due to very spotty performance and low MTU issues over + // these IPv6 tunnel links. + const uint8_t *ipd = reinterpret_cast(reinterpret_cast(&a)->sin6_addr.s6_addr); + if ((ipd[0] == 0x20)&&(ipd[1] == 0x01)&&(ipd[2] == 0x04)&&(ipd[3] == 0x70)) + return false; + } + return true; + default: + return false; + } + } + return false; +} + } // namespace ZeroTier diff --git a/node/Path.hpp b/node/Path.hpp index 0ccffe386..d1c97a4bf 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -14,9 +14,9 @@ #ifndef ZT_PATH_HPP #define ZT_PATH_HPP -#include -#include -#include +#include +#include +#include #include #include @@ -26,13 +26,7 @@ #include "SharedPtr.hpp" #include "AtomicCounter.hpp" #include "Utils.hpp" -#include "RingBuffer.hpp" -#include "Packet.hpp" - -/** - * Maximum return value of preferenceRank() - */ -#define ZT_PATH_MAX_PREFERENCE_RANK ((ZT_INETADDRESS_MAX_SCOPE << 1) | 1) +#include "Mutex.hpp" namespace ZeroTier { @@ -53,7 +47,6 @@ public: { public: ZT_ALWAYS_INLINE HashKey() {} - ZT_ALWAYS_INLINE HashKey(const int64_t l,const InetAddress &r) { if (r.ss_family == AF_INET) { @@ -78,81 +71,15 @@ public: uint64_t _k[3]; }; - ZT_ALWAYS_INLINE Path() : - _lastOut(0), + ZT_ALWAYS_INLINE Path(const int64_t l,const InetAddress &r) : + _localSocket(l), _lastIn(0), - _lastPathQualityComputeTime(0), - _localSocket(-1), - _latency(0xffff), - _addr(), - _ipScope(InetAddress::IP_SCOPE_NONE), - _lastAck(0), - _lastThroughputEstimation(0), - _lastQoSMeasurement(0), - _lastQoSRecordPurge(0), - _unackedBytes(0), - _expectingAckAsOf(0), - _packetsReceivedSinceLastAck(0), - _packetsReceivedSinceLastQoS(0), - _maxLifetimeThroughput(0), - _lastComputedMeanThroughput(0), - _bytesAckedSinceLastThroughputEstimation(0), - _lastComputedMeanLatency(0.0), - _lastComputedPacketDelayVariance(0.0), - _lastComputedPacketErrorRatio(0.0), - _lastComputedPacketLossRatio(0), - _lastComputedStability(0.0), - _lastComputedRelativeQuality(0), - _lastComputedThroughputDistCoeff(0.0), - _lastAllocation(0) - { - memset(_ifname, 0, 16); - memset(_addrString, 0, sizeof(_addrString)); - } - - ZT_ALWAYS_INLINE Path(const int64_t localSocket,const InetAddress &addr) : _lastOut(0), - _lastIn(0), - _lastPathQualityComputeTime(0), - _localSocket(localSocket), - _latency(0xffff), - _addr(addr), - _ipScope(addr.ipScope()), - _lastAck(0), - _lastThroughputEstimation(0), - _lastQoSMeasurement(0), - _lastQoSRecordPurge(0), - _unackedBytes(0), - _expectingAckAsOf(0), - _packetsReceivedSinceLastAck(0), - _packetsReceivedSinceLastQoS(0), - _maxLifetimeThroughput(0), - _lastComputedMeanThroughput(0), - _bytesAckedSinceLastThroughputEstimation(0), - _lastComputedMeanLatency(0.0), - _lastComputedPacketDelayVariance(0.0), - _lastComputedPacketErrorRatio(0.0), - _lastComputedPacketLossRatio(0), - _lastComputedStability(0.0), - _lastComputedRelativeQuality(0), - _lastComputedThroughputDistCoeff(0.0), - _lastAllocation(0) + _addr(r), + __refCount() { - memset(_ifname, 0, 16); - memset(_addrString, 0, sizeof(_addrString)); - if (_localSocket != -1) { - // TODO: add localInterface alongside localSocket - //_phy->getIfName((PhySocket *) ((uintptr_t) _localSocket), _ifname, 16); - } } - /** - * Called when a packet is received from this remote path, regardless of content - * - * @param t Time of receive - */ - ZT_ALWAYS_INLINE void received(const uint64_t t) { _lastIn = t; } - /** * Send a packet via this path (last out time is also updated) * @@ -166,33 +93,25 @@ public: bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,int64_t now); /** - * Manually update last sent time + * Explicitly update last sent time * * @param t Time of send */ - ZT_ALWAYS_INLINE void sent(const int64_t t) { _lastOut = t; } + ZT_ALWAYS_INLINE void sent(const uint64_t t) { _lastOut = t; } /** - * Update path latency with a new measurement + * Called when a packet is received from this remote path, regardless of content * - * @param l Measured latency + * @param t Time of receive */ - ZT_ALWAYS_INLINE void updateLatency(const unsigned int l, int64_t now) - { - unsigned int pl = _latency; - if (pl < 0xffff) { - _latency = (pl + l) / 2; - } - else { - _latency = l; - } - _latencySamples.push(l); - } + ZT_ALWAYS_INLINE void received(const uint64_t t) { _lastIn = t; } /** - * @return Local socket as specified by external code + * Check path aliveness + * + * @param now Current time */ - ZT_ALWAYS_INLINE int64_t localSocket() const { return _localSocket; } + ZT_ALWAYS_INLINE bool alive(const int64_t now) const { return ((now - _lastIn) < ZT_PATH_ACTIVITY_TIMEOUT); } /** * @return Physical address @@ -200,19 +119,19 @@ public: ZT_ALWAYS_INLINE const InetAddress &address() const { return _addr; } /** - * @return IP scope -- faster shortcut for address().ipScope() + * @return Local socket as specified by external code */ - ZT_ALWAYS_INLINE InetAddress::IpScope ipScope() const { return _ipScope; } + ZT_ALWAYS_INLINE int64_t localSocket() const { return _localSocket; } /** - * @return Preference rank, higher == better + * @return Last time we received anything */ - ZT_ALWAYS_INLINE unsigned int preferenceRank() const - { - // This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but - // within each IP scope class to prefer IPv6 over IPv4. - return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) ); - } + ZT_ALWAYS_INLINE int64_t lastIn() const { return _lastIn; } + + /** + * @return Last time we sent something + */ + ZT_ALWAYS_INLINE int64_t lastOut() const { return _lastOut; } /** * Check whether this address is valid for a ZeroTier path @@ -223,443 +142,14 @@ public: * @param a Address to check * @return True if address is good for ZeroTier path use */ - static ZT_ALWAYS_INLINE bool isAddressValidForPath(const InetAddress &a) - { - if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) { - switch(a.ipScope()) { - /* Note: we don't do link-local at the moment. Unfortunately these - * cause several issues. The first is that they usually require a - * device qualifier, which we don't handle yet and can't portably - * push in PUSH_DIRECT_PATHS. The second is that some OSes assign - * these very ephemerally or otherwise strangely. So we'll use - * private, pseudo-private, shared (e.g. carrier grade NAT), or - * global IP addresses. */ - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - if (a.ss_family == AF_INET6) { - // TEMPORARY HACK: for now, we are going to blacklist he.net IPv6 - // tunnels due to very spotty performance and low MTU issues over - // these IPv6 tunnel links. - const uint8_t *ipd = reinterpret_cast(reinterpret_cast(&a)->sin6_addr.s6_addr); - if ((ipd[0] == 0x20)&&(ipd[1] == 0x01)&&(ipd[2] == 0x04)&&(ipd[3] == 0x70)) - return false; - } - return true; - default: - return false; - } - } - return false; - } - - /** - * @return Latency or 0xffff if unknown - */ - ZT_ALWAYS_INLINE unsigned int latency() const { return _latency; } - - /** - * @return Path quality -- lower is better - */ - ZT_ALWAYS_INLINE long quality(const int64_t now) const - { - const long l = (long)_latency; - const long age = (long)std::min((long)(now - _lastIn),(long)(ZT_PEER_PING_PERIOD * 10)); // set an upper sanity limit to avoid overflow - return ( ( (age < (ZT_PEER_PING_PERIOD + 5000)) ? l : (l + 65535 + age) ) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1)); - } - - /** - * Record statistics on outgoing packets. Used later to estimate QoS metrics. - * - * @param now Current time - * @param packetId ID of packet - * @param payloadLength Length of payload - * @param verb Packet verb - */ - ZT_ALWAYS_INLINE void recordOutgoingPacket(int64_t now, int64_t packetId, uint16_t payloadLength, Packet::Verb verb) - { - Mutex::Lock _l(_statistics_m); - if (verb != Packet::VERB_ACK && verb != Packet::VERB_QOS_MEASUREMENT) { - if ((packetId & (ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR - 1)) == 0) { - _unackedBytes += payloadLength; - // Take note that we're expecting a VERB_ACK on this path as of a specific time - _expectingAckAsOf = ackAge(now) > ZT_PATH_ACK_INTERVAL ? _expectingAckAsOf : now; - if (_outQoSRecords.size() < ZT_PATH_MAX_OUTSTANDING_QOS_RECORDS) { - _outQoSRecords[packetId] = now; - } - } - } - } - - /** - * Record statistics on incoming packets. Used later to estimate QoS metrics. - * - * @param now Current time - * @param packetId ID of packet - * @param payloadLength Length of payload - * @param verb Packet verb - */ - ZT_ALWAYS_INLINE void recordIncomingPacket(int64_t now, int64_t packetId, uint16_t payloadLength, Packet::Verb verb) - { - Mutex::Lock _l(_statistics_m); - if (verb != Packet::VERB_ACK && verb != Packet::VERB_QOS_MEASUREMENT) { - if ((packetId & (ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR - 1)) == 0) { - _inACKRecords[packetId] = payloadLength; - _packetsReceivedSinceLastAck++; - _inQoSRecords[packetId] = now; - _packetsReceivedSinceLastQoS++; - } - _packetValiditySamples.push(true); - } - } - - /** - * Record that we've received a VERB_ACK on this path, also compute throughput if required. - * - * @param now Current time - * @param ackedBytes Number of bytes acknowledged by other peer - */ - ZT_ALWAYS_INLINE void receivedAck(int64_t now, int32_t ackedBytes) - { - _expectingAckAsOf = 0; - _unackedBytes = (ackedBytes > _unackedBytes) ? 0 : _unackedBytes - ackedBytes; - int64_t timeSinceThroughputEstimate = (now - _lastThroughputEstimation); - if (timeSinceThroughputEstimate >= ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL) { - uint64_t throughput = (uint64_t)((float)(_bytesAckedSinceLastThroughputEstimation * 8) / ((float)timeSinceThroughputEstimate / (float)1000)); - _throughputSamples.push(throughput); - _maxLifetimeThroughput = throughput > _maxLifetimeThroughput ? throughput : _maxLifetimeThroughput; - _lastThroughputEstimation = now; - _bytesAckedSinceLastThroughputEstimation = 0; - } else { - _bytesAckedSinceLastThroughputEstimation += ackedBytes; - } - } - - /** - * @return Number of bytes this peer is responsible for ACKing since last ACK - */ - inline int32_t bytesToAck() - { - Mutex::Lock _l(_statistics_m); - int32_t bytesToAck = 0; - std::map::iterator it = _inACKRecords.begin(); - while (it != _inACKRecords.end()) { - bytesToAck += it->second; - it++; - } - return bytesToAck; - } - - /** - * @return Number of bytes thus far sent that have not been acknowledged by the remote peer - */ - inline int64_t unackedSentBytes() - { - return _unackedBytes; - } - - /** - * Account for the fact that an ACK was just sent. Reset counters, timers, and clear statistics buffers - * - * @param Current time - */ - inline void sentAck(int64_t now) - { - Mutex::Lock _l(_statistics_m); - _inACKRecords.clear(); - _packetsReceivedSinceLastAck = 0; - _lastAck = now; - } - - /** - * Receive QoS data, match with recorded egress times from this peer, compute latency - * estimates. - * - * @param now Current time - * @param count Number of records - * @param rx_id table of packet IDs - * @param rx_ts table of holding times - */ - inline void receivedQoS(int64_t now, int count, uint64_t *rx_id, uint16_t *rx_ts) - { - Mutex::Lock _l(_statistics_m); - // Look up egress times and compute latency values for each record - std::map::iterator it; - for (int j=0; jsecond); - uint16_t rtt_compensated = rtt - rx_ts[j]; - uint16_t latency = rtt_compensated / 2; - updateLatency(latency, now); - _outQoSRecords.erase(it); - } - } - } - - /** - * Generate the contents of a VERB_QOS_MEASUREMENT packet. - * - * @param now Current time - * @param qosBuffer destination buffer - * @return Size of payload - */ - inline int32_t generateQoSPacket(int64_t now, char *qosBuffer) - { - Mutex::Lock _l(_statistics_m); - int32_t len = 0; - std::map::iterator it = _inQoSRecords.begin(); - int i=0; - while (i<_packetsReceivedSinceLastQoS && it != _inQoSRecords.end()) { - uint64_t id = it->first; - memcpy(qosBuffer, &id, sizeof(uint64_t)); - qosBuffer+=sizeof(uint64_t); - uint16_t holdingTime = (uint16_t)(now - it->second); - memcpy(qosBuffer, &holdingTime, sizeof(uint16_t)); - qosBuffer+=sizeof(uint16_t); - len+=sizeof(uint64_t)+sizeof(uint16_t); - _inQoSRecords.erase(it++); - i++; - } - return len; - } - - /** - * Account for the fact that a VERB_QOS_MEASUREMENT was just sent. Reset timers. - * - * @param Current time - */ - inline void sentQoS(int64_t now) { - _packetsReceivedSinceLastQoS = 0; - _lastQoSMeasurement = now; - } - - /** - * @param now Current time - * @return Whether an ACK (VERB_ACK) packet needs to be emitted at this time - */ - inline bool needsToSendAck(int64_t now) { - return ((now - _lastAck) >= ZT_PATH_ACK_INTERVAL || - (_packetsReceivedSinceLastAck == ZT_PATH_QOS_TABLE_SIZE)) && _packetsReceivedSinceLastAck; - } - - /** - * @param now Current time - * @return Whether a QoS (VERB_QOS_MEASUREMENT) packet needs to be emitted at this time - */ - inline bool needsToSendQoS(int64_t now) { - return ((_packetsReceivedSinceLastQoS >= ZT_PATH_QOS_TABLE_SIZE) || - ((now - _lastQoSMeasurement) > ZT_PATH_QOS_INTERVAL)) && _packetsReceivedSinceLastQoS; - } - - /** - * How much time has elapsed since we've been expecting a VERB_ACK on this path. This value - * is used to determine a more relevant path "age". This lets us penalize paths which are no - * longer ACKing, but not those that simple aren't being used to carry traffic at the - * current time. - */ - inline int64_t ackAge(int64_t now) { return _expectingAckAsOf ? now - _expectingAckAsOf : 0; } - - /** - * The maximum observed throughput (in bits/s) for this path - */ - inline uint64_t maxLifetimeThroughput() { return _maxLifetimeThroughput; } - - /** - * @return The mean throughput (in bits/s) of this link - */ - inline uint64_t meanThroughput() { return _lastComputedMeanThroughput; } - - /** - * Assign a new relative quality value for this path in the aggregate link - * - * @param rq Quality of this path in comparison to other paths available to this peer - */ - inline void updateRelativeQuality(float rq) { _lastComputedRelativeQuality = rq; } - - /** - * @return Quality of this path compared to others in the aggregate link - */ - inline float relativeQuality() { return _lastComputedRelativeQuality; } - - /** - * Assign a new allocation value for this path in the aggregate link - * - * @param allocation Percentage of traffic to be sent over this path to a peer - */ - inline void updateComponentAllocationOfAggregateLink(unsigned char allocation) { _lastAllocation = allocation; } - - /** - * @return Percentage of traffic allocated to this path in the aggregate link - */ - inline unsigned char allocation() { return _lastAllocation; } - - /** - * @return Stability estimates can become expensive to compute, we cache the most recent result. - */ - inline float lastComputedStability() { return _lastComputedStability; } - - /** - * @return A pointer to a cached copy of the human-readable name of the interface this Path's localSocket is bound to - */ - inline char *getName() { return _ifname; } - - /** - * @return Packet delay variance - */ - inline float packetDelayVariance() { return _lastComputedPacketDelayVariance; } - - /** - * @return Previously-computed mean latency - */ - inline float meanLatency() { return _lastComputedMeanLatency; } - - /** - * @return Packet loss rate (PLR) - */ - inline float packetLossRatio() { return _lastComputedPacketLossRatio; } - - /** - * @return Packet error ratio (PER) - */ - inline float packetErrorRatio() { return _lastComputedPacketErrorRatio; } - - /** - * Record an invalid incoming packet. This packet failed MAC/compression/cipher checks and will now - * contribute to a Packet Error Ratio (PER). - */ - inline void recordInvalidPacket() { _packetValiditySamples.push(false); } - - /** - * @return A pointer to a cached copy of the address string for this Path (For debugging only) - */ - inline char *getAddressString() { return _addrString; } - - /** - * @return The current throughput disturbance coefficient - */ - inline float throughputDisturbanceCoefficient() { return _lastComputedThroughputDistCoeff; } - - /** - * Compute and cache stability and performance metrics. The resultant stability coefficient is a measure of how "well behaved" - * this path is. This figure is substantially different from (but required for the estimation of the path's overall "quality". - * - * @param now Current time - */ - inline void processBackgroundPathMeasurements(const int64_t now) - { - if (now - _lastPathQualityComputeTime > ZT_PATH_QUALITY_COMPUTE_INTERVAL) { - Mutex::Lock _l(_statistics_m); - _lastPathQualityComputeTime = now; - address().toString(_addrString); - _lastComputedMeanLatency = _latencySamples.mean(); - _lastComputedPacketDelayVariance = _latencySamples.stddev(); // Similar to "jitter" (SEE: RFC 3393, RFC 4689) - _lastComputedMeanThroughput = (uint64_t)_throughputSamples.mean(); - - // If no packet validity samples, assume PER==0 - _lastComputedPacketErrorRatio = 1 - (_packetValiditySamples.count() ? _packetValiditySamples.mean() : 1); - - // Compute path stability - // Normalize measurements with wildly different ranges into a reasonable range - float normalized_pdv = Utils::normalize(_lastComputedPacketDelayVariance, 0, ZT_PATH_MAX_PDV, 0, 10); - float normalized_la = Utils::normalize(_lastComputedMeanLatency, 0, ZT_PATH_MAX_MEAN_LATENCY, 0, 10); - float throughput_cv = _throughputSamples.mean() > 0 ? _throughputSamples.stddev() / _throughputSamples.mean() : 1; - - // Form an exponential cutoff and apply contribution weights - float pdv_contrib = expf((-1.0f)*normalized_pdv) * (float)ZT_PATH_CONTRIB_PDV; - float latency_contrib = expf((-1.0f)*normalized_la) * (float)ZT_PATH_CONTRIB_LATENCY; - - // Throughput Disturbance Coefficient - float throughput_disturbance_contrib = expf((-1.0f)*throughput_cv) * (float)ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE; - _throughputDisturbanceSamples.push(throughput_cv); - _lastComputedThroughputDistCoeff = _throughputDisturbanceSamples.mean(); - - // Obey user-defined ignored contributions - pdv_contrib = ZT_PATH_CONTRIB_PDV > 0.0 ? pdv_contrib : 1; - latency_contrib = ZT_PATH_CONTRIB_LATENCY > 0.0 ? latency_contrib : 1; - throughput_disturbance_contrib = ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE > 0.0 ? throughput_disturbance_contrib : 1; - - // Stability - _lastComputedStability = pdv_contrib + latency_contrib + throughput_disturbance_contrib; - _lastComputedStability *= 1 - _lastComputedPacketErrorRatio; - - // Prevent QoS records from sticking around for too long - std::map::iterator it = _outQoSRecords.begin(); - while (it != _outQoSRecords.end()) { - // Time since egress of tracked packet - if ((now - it->second) >= ZT_PATH_QOS_TIMEOUT) { - _outQoSRecords.erase(it++); - } else { it++; } - } - } - } - - /** - * @return True if this path is alive (receiving data) - */ - ZT_ALWAYS_INLINE bool alive(const int64_t now) const { return ((now - _lastIn) < ((ZT_PEER_PING_PERIOD * 2) + 5000)); } - - /** - * @return Last time we sent something - */ - ZT_ALWAYS_INLINE int64_t lastOut() const { return _lastOut; } - - /** - * @return Last time we received anything - */ - ZT_ALWAYS_INLINE int64_t lastIn() const { return _lastIn; } + static bool isAddressValidForPath(const InetAddress &a); private: - Mutex _statistics_m; - - volatile int64_t _lastOut; - volatile int64_t _lastIn; - volatile int64_t _lastPathQualityComputeTime; int64_t _localSocket; - volatile unsigned int _latency; + int64_t _lastIn; + int64_t _lastOut; InetAddress _addr; - InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often AtomicCounter __refCount; - - std::map _outQoSRecords; // id:egress_time - std::map _inQoSRecords; // id:now - std::map _inACKRecords; // id:len - - int64_t _lastAck; - int64_t _lastThroughputEstimation; - int64_t _lastQoSMeasurement; - int64_t _lastQoSRecordPurge; - - int64_t _unackedBytes; - int64_t _expectingAckAsOf; - int16_t _packetsReceivedSinceLastAck; - int16_t _packetsReceivedSinceLastQoS; - - uint64_t _maxLifetimeThroughput; - uint64_t _lastComputedMeanThroughput; - uint64_t _bytesAckedSinceLastThroughputEstimation; - - float _lastComputedMeanLatency; - float _lastComputedPacketDelayVariance; - - float _lastComputedPacketErrorRatio; - float _lastComputedPacketLossRatio; - - // cached estimates - float _lastComputedStability; - float _lastComputedRelativeQuality; - float _lastComputedThroughputDistCoeff; - unsigned char _lastAllocation; - - // cached human-readable strings for tracing purposes - char _ifname[16]; - char _addrString[256]; - - RingBuffer _throughputSamples; - RingBuffer _latencySamples; - RingBuffer _packetValiditySamples; - RingBuffer _throughputDisturbanceSamples; }; } // namespace ZeroTier diff --git a/node/Peer.cpp b/node/Peer.cpp index c0085a3c6..0ada9e139 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -20,42 +20,33 @@ #include "Packet.hpp" #include "Trace.hpp" #include "InetAddress.hpp" -#include "RingBuffer.hpp" -#include "Utils.hpp" -#include "ScopedPtr.hpp" namespace ZeroTier { +struct _PathPriorityComparisonOperator +{ + ZT_ALWAYS_INLINE bool operator()(const SharedPtr &a,const SharedPtr &b) const + { + return ( ((a)&&(a->lastIn() > 0)) && ((!b)||(b->lastIn() <= 0)||(a->lastIn() < b->lastIn())) ); + } +}; + Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) : RR(renv), _lastReceive(0), - _lastDirectPathPushSent(0), - _lastDirectPathPushReceive(0), - _lastCredentialRequestSent(0), _lastWhoisRequestReceived(0), _lastEchoRequestReceived(0), - _lastCredentialsReceived(0), - _lastACKWindowReset(0), - _lastQoSWindowReset(0), - _lastMultipathCompatibilityCheck(0), + _lastPushDirectPathsReceived(0), + _lastPushDirectPathsSent(0), _lastTriedStaticPath(0), - _uniqueAlivePathCount(0), - _localMultipathSupported(false), - _remoteMultipathSupported(false), - _canUseMultipath(false), - _freeRandomByte((uint8_t)Utils::random()), + _lastPrioritizedPaths(0), + _latency(0xffff), + _alivePathCount(0), + _id(peerIdentity), _vProto(0), _vMajor(0), _vMinor(0), - _vRevision(0), - _id(peerIdentity), - _directPathPushCutoffCount(0), - _credentialsCutoffCount(0), - _linkIsBalanced(false), - _linkIsRedundant(false), - _remotePeerMultipathEnabled(false), - _lastAggregateStatsReport(0), - _lastAggregateAllocation(0) + _vRevision(0) { if (!myIdentity.agree(peerIdentity,_key)) throw ZT_EXCEPTION_INVALID_ARGUMENT; @@ -73,100 +64,65 @@ void Peer::received( const uint64_t networkId) { const int64_t now = RR->node->now(); - _lastReceive = now; - { - Mutex::Lock _l(_paths_m); - - recordIncomingPacket(tPtr, path, packetId, payloadLength, verb, now); - - if (_canUseMultipath) { - if (path->needsToSendQoS(now)) { - sendQOS_MEASUREMENT(tPtr, path, path->localSocket(), path->address(), now); - } - for(unsigned int i=0;iprocessBackgroundPathMeasurements(now); - } - } - } - } - if (hops == 0) { - // If this is a direct packet (no hops), update existing paths or learn new ones - bool havePath = false; - { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;inode->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) { - Mutex::Lock _l(_paths_m); + if (verb == Packet::VERB_OK) { + RWMutex::Lock l(_paths_l); - // Paths are redundant if they duplicate an alive path to the same IP or - // with the same local socket and address family. - bool redundant = false; - unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS; - for(unsigned int i=0;ialive(now)) && ( ((_paths[i]->localSocket() == path->localSocket())&&(_paths[i]->address().ss_family == path->address().ss_family)) || (_paths[i]->address().ipsEqual2(path->address())) ) ) { - redundant = true; - break; - } - // If the path is the same address and port, simply assume this is a replacement - if ( (_paths[i]->address().ipsEqual2(path->address()))) { - replacePath = i; - break; - } - } else break; - } - - // If the path isn't a duplicate of the same localSocket AND we haven't already determined a replacePath, - // then find the worst path and replace it. - if (!redundant && replacePath == ZT_MAX_PEER_NETWORK_PATHS) { - int replacePathQuality = 0; - for(unsigned int i=0;iaddress().ss_family == path->address().ss_family) && + (_paths[i]->localSocket() == path->localSocket()) && // TODO: should be localInterface when multipath is integrated + (_paths[i]->address().ipsEqual2(path->address()))) { + // If this is another path to the same place, swap it out as the + // one we just received from may replace an old one but don't + // learn it as a new path. + _paths[i] = path; + goto path_check_done; + } else { if (_paths[i]) { - const int q = _paths[i]->quality(now); - if (q > replacePathQuality) { - replacePathQuality = q; - replacePath = i; + if (_paths[i]->lastIn() > lastReceiveTimeMax) { + lastReceiveTimeMax = _paths[i]->lastIn(); + lastReceiveTimeMaxAt = i; } } else { - replacePath = i; - break; + lastReceiveTimeMax = 0x7fffffffffffffffLL; + lastReceiveTimeMaxAt = i; } } } - if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) { - if (verb == Packet::VERB_OK) { - RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId); - _paths[replacePath] = path; - } else { - attemptToContact = true; - } + _lastPrioritizedPaths = now; + _paths[lastReceiveTimeMaxAt] = path; + _prioritizePaths(now); + RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId); + } else { + if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address())) { + sendHELLO(tPtr,path->localSocket(),path->address(),now); + path->sent(now); + RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); } } - - if (attemptToContact) { - sendHELLO(tPtr,path->localSocket(),path->address(),now); - path->sent(now); - RR->t->peerConfirmingUnknownPath(tPtr,networkId,*this,path,packetId,verb); - } } - // Periodically push direct paths to the peer, doing so more often if we do not - // currently have a direct path. +path_check_done: + const int64_t sinceLastPush = now - _lastPushDirectPathsSent; + if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) { + _lastPushDirectPathsReceived = now; + } + + /* const int64_t sinceLastPush = now - _lastDirectPathPushSent; if (sinceLastPush >= ((hops == 0) ? ZT_DIRECT_PATH_PUSH_INTERVAL_HAVEPATH : ZT_DIRECT_PATH_PUSH_INTERVAL)) { _lastDirectPathPushSent = now; @@ -219,452 +175,26 @@ void Peer::received( } } } + */ } -void Peer::recordOutgoingPacket(const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, int64_t now) +bool Peer::shouldTryPath(void *tPtr,int64_t now,const SharedPtr &suggestedBy,const InetAddress &addr) const { - _freeRandomByte += (unsigned char)(packetId >> 8); // grab entropy to use in path selection logic for multipath - if (_canUseMultipath) { - path->recordOutgoingPacket(now, packetId, payloadLength, verb); - } -} - -void Peer::recordIncomingPacket(void *tPtr, const SharedPtr &path, const uint64_t packetId, - uint16_t payloadLength, const Packet::Verb verb, int64_t now) -{ - if (_canUseMultipath) { - if (path->needsToSendAck(now)) { - sendACK(tPtr, path, path->localSocket(), path->address(), now); - } - path->recordIncomingPacket(now, packetId, payloadLength, verb); - } -} - -void Peer::computeAggregateProportionalAllocation(int64_t now) -{ - float maxStability = 0; - float totalRelativeQuality = 0; - float maxThroughput = 1; - float maxScope = 0; - float relStability[ZT_MAX_PEER_NETWORK_PATHS]; - float relThroughput[ZT_MAX_PEER_NETWORK_PATHS]; - memset(&relStability, 0, sizeof(relStability)); - memset(&relThroughput, 0, sizeof(relThroughput)); - // Survey all paths - for(unsigned int i=0;ilastComputedStability(); - relThroughput[i] = (float)_paths[i]->maxLifetimeThroughput(); - maxStability = relStability[i] > maxStability ? relStability[i] : maxStability; - maxThroughput = relThroughput[i] > maxThroughput ? relThroughput[i] : maxThroughput; - maxScope = _paths[i]->ipScope() > maxScope ? _paths[i]->ipScope() : maxScope; - } - } - // Convert to relative values - for(unsigned int i=0;iackAge(now), 0, ZT_PATH_MAX_AGE, 0, 10); - float age_contrib = exp((-1)*normalized_ma); - float relScope = ((float)(_paths[i]->ipScope()+1) / (maxScope + 1)); - float relQuality = - (relStability[i] * (float)ZT_PATH_CONTRIB_STABILITY) - + (fmaxf(1.0f, relThroughput[i]) * (float)ZT_PATH_CONTRIB_THROUGHPUT) - + relScope * (float)ZT_PATH_CONTRIB_SCOPE; - relQuality *= age_contrib; - // Arbitrary cutoffs - relQuality = relQuality > (1.00f / 100.0f) ? relQuality : 0.0f; - relQuality = relQuality < (99.0f / 100.0f) ? relQuality : 1.0f; - totalRelativeQuality += relQuality; - _paths[i]->updateRelativeQuality(relQuality); - } - } - // Convert set of relative performances into an allocation set - for(uint16_t i=0;iupdateComponentAllocationOfAggregateLink((unsigned char)((_paths[i]->relativeQuality() / totalRelativeQuality) * 255)); - } - } -} - -int Peer::computeAggregateLinkPacketDelayVariance() -{ - float pdv = 0.0; - for(unsigned int i=0;irelativeQuality() * _paths[i]->packetDelayVariance(); - } - } - return (int)pdv; -} - -int Peer::computeAggregateLinkMeanLatency() -{ - int ml = 0; - int pathCount = 0; - for(unsigned int i=0;irelativeQuality() * _paths[i]->meanLatency()); - } - } - return ml / pathCount; -} - -int Peer::aggregateLinkPhysicalPathCount() -{ - std::map ifnamemap; - int pathCount = 0; - int64_t now = RR->node->now(); - for(unsigned int i=0;ialive(now)) { - if (!ifnamemap[_paths[i]->getName()]) { - ifnamemap[_paths[i]->getName()] = true; - pathCount++; - } - } - } - return pathCount; -} - -int Peer::aggregateLinkLogicalPathCount() -{ - int pathCount = 0; - int64_t now = RR->node->now(); - for(unsigned int i=0;ialive(now)) { - pathCount++; - } - } - return pathCount; -} - -SharedPtr Peer::getAppropriatePath(int64_t now, bool includeExpired) -{ - Mutex::Lock _l(_paths_m); - unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS; - - /** - * Send traffic across the highest quality path only. This algorithm will still - * use the old path quality metric from protocol version 9. - */ - if (!_canUseMultipath) { - long bestPathQuality = 2147483647; - for(unsigned int i=0;ialive(now))) { - const long q = _paths[i]->quality(now); - if (q <= bestPathQuality) { - bestPathQuality = q; - bestPath = i; - } - } - } else break; - } - if (bestPath != ZT_MAX_PEER_NETWORK_PATHS) { - return _paths[bestPath]; - } - return SharedPtr(); - } + if (_paths[i]->address().ipsEqual2(addr)) + return false; - for(unsigned int i=0;iprocessBackgroundPathMeasurements(now); - } - } - - /** - * Randomly distribute traffic across all paths - */ - int numAlivePaths = 0; - int numStalePaths = 0; - if (RR->node->getMultipathMode() == ZT_MULTIPATH_RANDOM) { - int alivePaths[ZT_MAX_PEER_NETWORK_PATHS]; - int stalePaths[ZT_MAX_PEER_NETWORK_PATHS]; - memset(&alivePaths, -1, sizeof(alivePaths)); - memset(&stalePaths, -1, sizeof(stalePaths)); - for(unsigned int i=0;ialive(now)) { - alivePaths[numAlivePaths] = i; - numAlivePaths++; - } - else { - stalePaths[numStalePaths] = i; - numStalePaths++; - } - } - } - unsigned int r = _freeRandomByte; - if (numAlivePaths > 0) { - int rf = r % numAlivePaths; - return _paths[alivePaths[rf]]; - } - else if(numStalePaths > 0) { - // Resort to trying any non-expired path - int rf = r % numStalePaths; - return _paths[stalePaths[rf]]; - } - } - - /** - * Proportionally allocate traffic according to dynamic path quality measurements - */ - if (RR->node->getMultipathMode() == ZT_MULTIPATH_PROPORTIONALLY_BALANCED) { - if ((now - _lastAggregateAllocation) >= ZT_PATH_QUALITY_COMPUTE_INTERVAL) { - _lastAggregateAllocation = now; - computeAggregateProportionalAllocation(now); - } - // Randomly choose path according to their allocations - float rf = _freeRandomByte; - for(int i=0;iallocation()) { - bestPath = i; - _pathChoiceHist.push(bestPath); // Record which path we chose - break; - } - rf -= _paths[i]->allocation(); - } - } - if (bestPath < ZT_MAX_PEER_NETWORK_PATHS) { - return _paths[bestPath]; - } - } - return SharedPtr(); -} - -char *Peer::interfaceListStr() -{ - std::map ifnamemap; - char tmp[32]; - const int64_t now = RR->node->now(); - char *ptr = _interfaceListStr; - bool imbalanced = false; - memset(_interfaceListStr, 0, sizeof(_interfaceListStr)); - int alivePathCount = aggregateLinkLogicalPathCount(); - for(unsigned int i=0;ialive(now)) { - int ipv = _paths[i]->address().isV4(); - // If this is acting as an aggregate link, check allocations - float targetAllocation = 1.0f / (float)alivePathCount; - float currentAllocation = 1.0f; - if (alivePathCount > 1) { - currentAllocation = (float)_pathChoiceHist.countValue(i) / (float)_pathChoiceHist.count(); - if (fabs(targetAllocation - currentAllocation) > ZT_PATH_IMBALANCE_THRESHOLD) { - imbalanced = true; - } - } - char *ipvStr = ipv ? (char*)"ipv4" : (char*)"ipv6"; - sprintf(tmp, "(%s, %s, %.3f)", _paths[i]->getName(), ipvStr, currentAllocation); - // Prevent duplicates - if(ifnamemap[_paths[i]->getName()] != ipv) { - memcpy(ptr, tmp, strlen(tmp)); - ptr += strlen(tmp); - *ptr = ' '; - ptr++; - ifnamemap[_paths[i]->getName()] = ipv; + int s = (int)_paths[i]->address().ipScope(); + if (s > maxHaveScope) + maxHaveScope = s; } } } - ptr--; // Overwrite trailing space - if (imbalanced) { - sprintf(tmp, ", is asymmetrical"); - memcpy(ptr, tmp, sizeof(tmp)); - } else { - *ptr = '\0'; - } - return _interfaceListStr; -} - -void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr &other) const -{ - unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int myBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long myBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long myBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int theirBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - unsigned int theirBestV6ByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long theirBestV4QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - long theirBestV6QualityByScope[ZT_INETADDRESS_MAX_SCOPE+1]; - for(int i=0;i<=ZT_INETADDRESS_MAX_SCOPE;++i) { - myBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - myBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - myBestV4QualityByScope[i] = 2147483647; - myBestV6QualityByScope[i] = 2147483647; - theirBestV4ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - theirBestV6ByScope[i] = ZT_MAX_PEER_NETWORK_PATHS; - theirBestV4QualityByScope[i] = 2147483647; - theirBestV6QualityByScope[i] = 2147483647; - } - - Mutex::Lock _l1(_paths_m); - - for(unsigned int i=0;iquality(now); - const unsigned int s = (unsigned int)_paths[i]->ipScope(); - switch(_paths[i]->address().ss_family) { - case AF_INET: - if (q <= myBestV4QualityByScope[s]) { - myBestV4QualityByScope[s] = q; - myBestV4ByScope[s] = i; - } - break; - case AF_INET6: - if (q <= myBestV6QualityByScope[s]) { - myBestV6QualityByScope[s] = q; - myBestV6ByScope[s] = i; - } - break; - } - } else break; - } - - Mutex::Lock _l2(other->_paths_m); - - for(unsigned int i=0;i_paths[i]) { - const long q = other->_paths[i]->quality(now); - const unsigned int s = (unsigned int)other->_paths[i]->ipScope(); - switch(other->_paths[i]->address().ss_family) { - case AF_INET: - if (q <= theirBestV4QualityByScope[s]) { - theirBestV4QualityByScope[s] = q; - theirBestV4ByScope[s] = i; - } - break; - case AF_INET6: - if (q <= theirBestV6QualityByScope[s]) { - theirBestV6QualityByScope[s] = q; - theirBestV6ByScope[s] = i; - } - break; - } - } else break; - } - - unsigned int mine = ZT_MAX_PEER_NETWORK_PATHS; - unsigned int theirs = ZT_MAX_PEER_NETWORK_PATHS; - - for(int s=ZT_INETADDRESS_MAX_SCOPE;s>=0;--s) { - if ((myBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV6ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { - mine = myBestV6ByScope[s]; - theirs = theirBestV6ByScope[s]; - break; - } - if ((myBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)&&(theirBestV4ByScope[s] != ZT_MAX_PEER_NETWORK_PATHS)) { - mine = myBestV4ByScope[s]; - theirs = theirBestV4ByScope[s]; - break; - } - } - - if (mine != ZT_MAX_PEER_NETWORK_PATHS) { - unsigned int alt = (unsigned int)Utils::random() & 1; // randomize which hint we send first for black magickal NAT-t reasons - const unsigned int completed = alt + 2; - while (alt != completed) { - if ((alt & 1) == 0) { - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - other->_id.address().appendTo(outp); - outp.append((uint16_t)other->_paths[theirs]->address().port()); - if (other->_paths[theirs]->address().ss_family == AF_INET6) { - outp.append((uint8_t)16); - outp.append(other->_paths[theirs]->address().rawIpData(),16); - } else { - outp.append((uint8_t)4); - outp.append(other->_paths[theirs]->address().rawIpData(),4); - } - outp.armor(_key,true); - _paths[mine]->send(RR,tPtr,outp.data(),outp.size(),now); - } else { - Packet outp(other->_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS); - outp.append((uint8_t)0); - _id.address().appendTo(outp); - outp.append((uint16_t)_paths[mine]->address().port()); - if (_paths[mine]->address().ss_family == AF_INET6) { - outp.append((uint8_t)16); - outp.append(_paths[mine]->address().rawIpData(),16); - } else { - outp.append((uint8_t)4); - outp.append(_paths[mine]->address().rawIpData(),4); - } - outp.armor(other->_key,true); - other->_paths[theirs]->send(RR,tPtr,outp.data(),outp.size(),now); - } - ++alt; - } - } -} - -inline void Peer::processBackgroundPeerTasks(const int64_t now) -{ - // Determine current multipath compatibility with other peer - if ((now - _lastMultipathCompatibilityCheck) >= ZT_PATH_QUALITY_COMPUTE_INTERVAL) { - // - // Cache number of available paths so that we can short-circuit multipath logic elsewhere - // - // We also take notice of duplicate paths (same IP only) because we may have - // recently received a direct path push from a peer and our list might contain - // a dead path which hasn't been fully recognized as such. In this case we - // don't want the duplicate to trigger execution of multipath code prematurely. - // - // This is done to support the behavior of auto multipath enable/disable - // without user intervention. - // - int currAlivePathCount = 0; - int duplicatePathsFound = 0; - for (unsigned int i=0;iaddress().ipsEqual2(_paths[j]->address()) && i != j) { - duplicatePathsFound+=1; - break; - } - } - } - } - _uniqueAlivePathCount = (currAlivePathCount - (duplicatePathsFound / 2)); - _lastMultipathCompatibilityCheck = now; - _localMultipathSupported = ((RR->node->getMultipathMode() != ZT_MULTIPATH_NONE) && (ZT_PROTO_VERSION > 9)); - _remoteMultipathSupported = _vProto > 9; - // If both peers support multipath and more than one path exist, we can use multipath logic - _canUseMultipath = _localMultipathSupported && _remoteMultipathSupported && (_uniqueAlivePathCount > 1); - } -} - -void Peer::sendACK(void *tPtr,const SharedPtr &path,const int64_t localSocket,const InetAddress &atAddress,int64_t now) -{ - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ACK); - uint32_t bytesToAck = path->bytesToAck(); - outp.append(bytesToAck); - if (atAddress) { - outp.armor(_key,false); - RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size()); - } else { - RR->sw->send(tPtr,outp,false); - } - path->sentAck(now); -} - -void Peer::sendQOS_MEASUREMENT(void *tPtr,const SharedPtr &path,const int64_t localSocket,const InetAddress &atAddress,int64_t now) -{ - const int64_t _now = RR->node->now(); - Packet outp(_id.address(),RR->identity.address(),Packet::VERB_QOS_MEASUREMENT); - char qosData[ZT_PATH_MAX_QOS_PACKET_SZ]; - int16_t len = path->generateQoSPacket(_now,qosData); - outp.append(qosData,len); - if (atAddress) { - outp.armor(_key,false); - RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size()); - } else { - RR->sw->send(tPtr,outp,false); - } - path->sentQoS(now); + return ( ((int)addr.ipScope() > maxHaveScope) && RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),-1,addr) ); } void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now) @@ -689,62 +219,124 @@ void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atA } } -void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount) +void Peer::ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,const bool pingAllAddressTypes) { - v4SendCount = 0; - v6SendCount = 0; + RWMutex::RLock l(_paths_l); - Mutex::Lock _l(_paths_m); + _lastPrioritizedPaths = now; + _prioritizePaths(now); - // Emit traces regarding aggregate link status - if (_canUseMultipath) { - int alivePathCount = aggregateLinkPhysicalPathCount(); - if ((now - _lastAggregateStatsReport) > ZT_PATH_AGGREGATE_STATS_REPORT_INTERVAL) { - _lastAggregateStatsReport = now; - if (alivePathCount) { - RR->t->peerLinkAggregateStatistics(NULL,*this); - } - } if (alivePathCount < 2 && _linkIsRedundant) { - _linkIsRedundant = !_linkIsRedundant; - RR->t->peerLinkNoLongerRedundant(NULL,*this); - } if (alivePathCount > 1 && !_linkIsRedundant) { - _linkIsRedundant = !_linkIsRedundant; - RR->t->peerLinkNowRedundant(NULL,*this); - } - } - - unsigned int j = 0; - for(unsigned int i=0;ialive(now))) { + if (_alivePathCount > 0) { + for (unsigned int i = 0; i < _alivePathCount; ++i) { sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); - _paths[i]->sent(now); + if (_paths[i]->address().isV4()) ++v4SendCount; else if (_paths[i]->address().isV6()) ++v6SendCount; - if (i != j) - _paths[j] = _paths[i]; - ++j; + if (!pingAllAddressTypes) + break; + } + } else { + SharedPtr r(RR->topology->root()); + if (r) { + SharedPtr rp(r->path(now)); + if (rp) { + sendHELLO(tPtr,rp->localSocket(),rp->address(),now); + rp->sent(now); + } } - } - while(j < ZT_MAX_PEER_NETWORK_PATHS) { - _paths[j].zero(); - ++j; } } void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now) { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;iaddress().ss_family == inetAddressFamily)&&(_paths[i]->ipScope() == scope)) { - sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); - _paths[i]->sent(now); - } - } else break; + RWMutex::RLock l(_paths_l); + for(unsigned int i=0; i < _alivePathCount; ++i) { + if ((_paths[i])&&((_paths[i]->address().ss_family == inetAddressFamily)&&(_paths[i]->address().ipScope() == scope))) { + sendHELLO(tPtr,_paths[i]->localSocket(),_paths[i]->address(),now); + _paths[i]->sent(now); + } + } +} + +void Peer::updateLatency(const unsigned int l) +{ + if ((l > 0)&&(l < 0xffff)) { + unsigned int lat = _latency; + if (lat < 0xffff) { + _latency = (l + l + lat) / 3; + } else { + _latency = l; + } + } +} + +bool Peer::sendDirect(void *tPtr,const void *data,const unsigned int len,const int64_t now) +{ + if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) { + _lastPrioritizedPaths = now; + _paths_l.lock(); + _prioritizePaths(now); + if (_alivePathCount == 0) { + _paths_l.unlock(); + return false; + } + const bool r = _paths[0]->send(RR,tPtr,data,len,now); + _paths_l.unlock(); + return r; + } else { + _paths_l.rlock(); + if (_alivePathCount == 0) { + _paths_l.runlock(); + return false; + } + const bool r = _paths[0]->send(RR,tPtr,data,len,now); + _paths_l.runlock(); + return r; + } +} + +SharedPtr Peer::path(const int64_t now) +{ + if ((now - _lastPrioritizedPaths) > ZT_PEER_PRIORITIZE_PATHS_INTERVAL) { + _lastPrioritizedPaths = now; + RWMutex::Lock l(_paths_l); + _prioritizePaths(now); + if (_alivePathCount == 0) + return SharedPtr(); + return _paths[0]; + } else { + RWMutex::RLock l(_paths_l); + if (_alivePathCount == 0) + return SharedPtr(); + return _paths[0]; + } +} + +void Peer::getAllPaths(std::vector< SharedPtr > &paths) +{ + RWMutex::RLock l(_paths_l); + paths.clear(); + paths.assign(_paths,_paths + _alivePathCount); +} + +void Peer::_prioritizePaths(const int64_t now) +{ + // assumes _paths_l is locked for writing + std::sort(_paths,_paths + ZT_MAX_PEER_NETWORK_PATHS,_PathPriorityComparisonOperator()); + + for(int i=0;ialive(now))) { + _alivePathCount = i; + + for(;i - #include "Constants.hpp" #include "RuntimeEnvironment.hpp" #include "Node.hpp" @@ -30,7 +28,7 @@ #include "Hashtable.hpp" #include "Mutex.hpp" -#define ZT_PEER_MAX_SERIALIZED_STATE_SIZE (sizeof(Peer) + 32 + (sizeof(Path) * 2)) +#include namespace ZeroTier { @@ -42,10 +40,10 @@ class Peer friend class SharedPtr; private: - inline Peer() {} // disabled to prevent bugs -- should not be constructed uninitialized + ZT_ALWAYS_INLINE Peer() {} public: - inline ~Peer() { Utils::burn(_key,sizeof(_key)); } + ZT_ALWAYS_INLINE ~Peer() { Utils::burn(_key,sizeof(_key)); } /** * Construct a new peer @@ -85,139 +83,23 @@ public: void received( void *tPtr, const SharedPtr &path, - const unsigned int hops, - const uint64_t packetId, - const unsigned int payloadLength, - const Packet::Verb verb, - const uint64_t inRePacketId, - const Packet::Verb inReVerb, - const uint64_t networkId); + unsigned int hops, + uint64_t packetId, + unsigned int payloadLength, + Packet::Verb verb, + uint64_t inRePacketId, + Packet::Verb inReVerb, + uint64_t networkId); /** - * Check whether we have an active path to this peer via the given address + * Check whether a path to this peer should be tried if received via e.g. RENDEZVOUS OR PUSH_DIRECT_PATHS * * @param now Current time + * @param suggestingPeer Peer suggesting path (may be this peer) * @param addr Remote address * @return True if we have an active path to this destination */ - ZT_ALWAYS_INLINE bool hasActivePathTo(int64_t now,const InetAddress &addr) const - { - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;iaddress() == addr)&&(_paths[i]->alive(now))) - return true; - } else break; - } - return false; - } - - /** - * Send via best direct path - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param data Packet data - * @param len Packet length - * @param now Current time - * @param force If true, send even if path is not alive - * @return True if we actually sent something - */ - ZT_ALWAYS_INLINE bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force) - { - SharedPtr bp(getAppropriatePath(now,force)); - if (bp) - return bp->send(RR,tPtr,data,len,now); - return false; - } - - /** - * Record statistics on outgoing packets - * - * @param path Path over which packet was sent - * @param id Packet ID - * @param len Length of packet payload - * @param verb Packet verb - * @param now Current time - */ - void recordOutgoingPacket(const SharedPtr &path, const uint64_t packetId, uint16_t payloadLength, const Packet::Verb verb, int64_t now); - - /** - * Record statistics on incoming packets - * - * @param path Path over which packet was sent - * @param id Packet ID - * @param len Length of packet payload - * @param verb Packet verb - * @param now Current time - */ - void recordIncomingPacket(void *tPtr, const SharedPtr &path, const uint64_t packetId, uint16_t payloadLength, const Packet::Verb verb, int64_t now); - - /** - * Send an ACK to peer for the most recent packets received - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param localSocket Raw socket the ACK packet will be sent over - * @param atAddress Destination for the ACK packet - * @param now Current time - */ - void sendACK(void *tPtr, const SharedPtr &path, const int64_t localSocket,const InetAddress &atAddress,int64_t now); - - /** - * Send a QoS packet to peer so that it can evaluate the quality of this link - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param localSocket Raw socket the QoS packet will be sent over - * @param atAddress Destination for the QoS packet - * @param now Current time - */ - void sendQOS_MEASUREMENT(void *tPtr, const SharedPtr &path, const int64_t localSocket,const InetAddress &atAddress,int64_t now); - - /** - * Compute relative quality values and allocations for the components of the aggregate link - * - * @param now Current time - */ - void computeAggregateProportionalAllocation(int64_t now); - - /** - * @return The aggregate link Packet Delay Variance (PDV) - */ - int computeAggregateLinkPacketDelayVariance(); - - /** - * @return The aggregate link mean latency - */ - int computeAggregateLinkMeanLatency(); - - /** - * @return The number of currently alive "physical" paths in the aggregate link - */ - int aggregateLinkPhysicalPathCount(); - - /** - * @return The number of currently alive "logical" paths in the aggregate link - */ - int aggregateLinkLogicalPathCount(); - - /** - * Get the most appropriate direct path based on current multipath and QoS configuration - * - * @param now Current time - * @param includeExpired If true, include even expired paths - * @return Best current path or NULL if none - */ - SharedPtr getAppropriatePath(int64_t now, bool includeExpired); - - /** - * Generate a human-readable string of interface names making up the aggregate link, also include - * moving allocation and IP version number for each (for tracing) - */ - char *interfaceListStr(); - - /** - * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path - */ - void introduce(void *const tPtr,const int64_t now,const SharedPtr &other) const; + bool shouldTryPath(void *tPtr,int64_t now,const SharedPtr &suggestedBy,const InetAddress &addr) const; /** * Send a HELLO to this peer at a specified physical address @@ -229,19 +111,18 @@ public: * @param atAddress Destination address * @param now Current time */ - void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now); + void sendHELLO(void *tPtr,int64_t localSocket,const InetAddress &atAddress,int64_t now); /** - * Send pings to active paths - * - * This also cleans up some internal data structures. It's called periodically from Node. + * Send ping to this peer * * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param now Current time * @param v4SendCount Number of IPv4 packets sent (result parameter) * @param v6SendCount Number of IPv6 packets sent (result parameter) + * @param pingAllAddressTypes If true, try to keep a link up for each address type/family */ - void ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount); + void ping(void *tPtr,int64_t now,unsigned int &v4SendCount,unsigned int &v6SendCount,bool pingAllAddressTypes); /** * Reset paths within a given IP scope and address family @@ -258,21 +139,6 @@ public: */ void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,int64_t now); - /** - * @param now Current time - * @return All known paths to this peer - */ - ZT_ALWAYS_INLINE std::vector< SharedPtr > paths(const int64_t now) const - { - std::vector< SharedPtr > pp; - Mutex::Lock _l(_paths_m); - for(unsigned int i=0;i bp(getAppropriatePath(now,false)); - if (bp) - return bp->latency(); - return 0xffff; - } - } + ZT_ALWAYS_INLINE unsigned int latency() const { return _latency; } /** - * This computes a quality score for relays and root servers + * Update peer latency information * - * If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they - * receive the worst possible quality (max unsigned int). Otherwise the - * quality is a product of latency and the number of potential missed - * pings. This causes roots and relays to switch over a bit faster if they - * fail. + * This is called from packet parsing code. * - * @return Relay quality score computed from latency and other factors, lower is better + * @param l New latency measurment (in milliseconds) */ - ZT_ALWAYS_INLINE unsigned int relayQuality(const int64_t now) - { - const uint64_t tsr = now - _lastReceive; - if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT) - return (~(unsigned int)0); - unsigned int l = latency(now); - if (!l) - l = 0xffff; - return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1)); - } + void updateLatency(const unsigned int l); /** * @return 256-bit secret symmetric encryption key @@ -347,73 +190,6 @@ public: ZT_ALWAYS_INLINE unsigned int remoteVersionRevision() const { return _vRevision; } ZT_ALWAYS_INLINE bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } - /** - * Periodically update known multipath activation constraints. This is done so that we know when and when - * not to use multipath logic. Doing this once every few seconds is sufficient. - * - * @param now Current time - */ - void processBackgroundPeerTasks(const int64_t now); - - /** - * Record that the remote peer does have multipath enabled. As is evident by the receipt of a VERB_ACK - * or a VERB_QOS_MEASUREMENT packet at some point in the past. Until this flag is set, the local client - * shall assume that multipath is not enabled and should only use classical Protocol 9 logic. - */ - ZT_ALWAYS_INLINE void inferRemoteMultipathEnabled() { _remotePeerMultipathEnabled = true; } - - /** - * @return Whether the local client supports and is configured to use multipath - */ - ZT_ALWAYS_INLINE bool localMultipathSupport() { return _localMultipathSupported; } - - /** - * @return Whether the remote peer supports and is configured to use multipath - */ - ZT_ALWAYS_INLINE bool remoteMultipathSupport() { return _remoteMultipathSupported; } - - /** - * @return Whether this client can use multipath to communicate with this peer. True if both peers are using - * the correct protocol and if both peers have multipath enabled. False if otherwise. - */ - ZT_ALWAYS_INLINE bool canUseMultipath() { return _canUseMultipath; } - - /** - * Rate limit gate for VERB_PUSH_DIRECT_PATHS - */ - ZT_ALWAYS_INLINE bool rateGatePushDirectPaths(const int64_t now) - { - if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME) - ++_directPathPushCutoffCount; - else _directPathPushCutoffCount = 0; - _lastDirectPathPushReceive = now; - return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT); - } - - /** - * Rate limit gate for VERB_NETWORK_CREDENTIALS - */ - ZT_ALWAYS_INLINE bool rateGateCredentialsReceived(const int64_t now) - { - if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME) - ++_credentialsCutoffCount; - else _credentialsCutoffCount = 0; - _lastCredentialsReceived = now; - return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT); - } - - /** - * Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE - */ - ZT_ALWAYS_INLINE bool rateGateRequestCredentials(const int64_t now) - { - if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) { - _lastCredentialRequestSent = now; - return true; - } - return false; - } - /** * Rate limit gate for inbound WHOIS requests */ @@ -426,6 +202,18 @@ public: return false; } + /** + * Rate limit gate for inbound PUSH_DIRECT_PATHS requests + */ + ZT_ALWAYS_INLINE bool rateGateInboundPushDirectPaths(const int64_t now) + { + if ((now - _lastPushDirectPathsReceived) >= ZT_DIRECT_PATH_PUSH_INTERVAL) { + _lastPushDirectPathsReceived = now; + return true; + } + return false; + } + /** * Rate limit gate for inbound ECHO requests */ @@ -438,34 +226,6 @@ public: return false; } - /** - * Rate limit gate for VERB_ACK - */ - ZT_ALWAYS_INLINE bool rateGateACK(const int64_t now) - { - if ((now - _lastACKWindowReset) >= ZT_PATH_QOS_ACK_CUTOFF_TIME) { - _lastACKWindowReset = now; - _ACKCutoffCount = 0; - } else { - ++_ACKCutoffCount; - } - return (_ACKCutoffCount < ZT_PATH_QOS_ACK_CUTOFF_LIMIT); - } - - /** - * Rate limit gate for VERB_QOS_MEASUREMENT - */ - ZT_ALWAYS_INLINE bool rateGateQoS(const int64_t now) - { - if ((now - _lastQoSWindowReset) >= ZT_PATH_QOS_ACK_CUTOFF_TIME) { - _lastQoSWindowReset = now; - _QoSCutoffCount = 0; - } else { - ++_QoSCutoffCount; - } - return (_QoSCutoffCount < ZT_PATH_QOS_ACK_CUTOFF_LIMIT); - } - /** * Rate limit gate for trying externally defined or static path */ @@ -479,72 +239,58 @@ public: } /** - * @return Whether this peer is reachable via an aggregate link + * Send directly if a direct path exists + * + * @param tPtr Thread pointer supplied by user + * @param data Data to send + * @param len Length of data + * @param now Current time + * @return True if packet appears to have been sent, false if no path or send failed */ - ZT_ALWAYS_INLINE bool hasAggregateLink() const - { - return _localMultipathSupported && _remoteMultipathSupported && _remotePeerMultipathEnabled; - } + bool sendDirect(void *tPtr,const void *data,unsigned int len,const int64_t now); + + /** + * @return Current best path + */ + SharedPtr path(int64_t now); + + /** + * Get all paths + * + * @param paths Vector of paths with the first path being the current preferred path + */ + void getAllPaths(std::vector< SharedPtr > &paths); private: + void _prioritizePaths(int64_t now); + uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH]; const RuntimeEnvironment *RR; - int64_t _lastReceive; // direct or indirect - int64_t _lastDirectPathPushSent; - int64_t _lastDirectPathPushReceive; - int64_t _lastCredentialRequestSent; - int64_t _lastWhoisRequestReceived; - int64_t _lastEchoRequestReceived; - int64_t _lastCredentialsReceived; - int64_t _lastACKWindowReset; - int64_t _lastQoSWindowReset; - int64_t _lastMultipathCompatibilityCheck; - int64_t _lastTriedStaticPath; + volatile int64_t _lastReceive; + volatile int64_t _lastWhoisRequestReceived; + volatile int64_t _lastEchoRequestReceived; + volatile int64_t _lastPushDirectPathsReceived; + volatile int64_t _lastPushDirectPathsSent; + volatile int64_t _lastTriedStaticPath; + volatile int64_t _lastPrioritizedPaths; + volatile unsigned int _latency; - int _uniqueAlivePathCount; + AtomicCounter __refCount; - bool _localMultipathSupported; - bool _remoteMultipathSupported; - bool _canUseMultipath; - uint8_t _freeRandomByte; + unsigned int _alivePathCount; + SharedPtr _paths[ZT_MAX_PEER_NETWORK_PATHS]; + RWMutex _paths_l; + + Identity _id; uint16_t _vProto; uint16_t _vMajor; uint16_t _vMinor; uint16_t _vRevision; - - SharedPtr _paths[ZT_MAX_PEER_NETWORK_PATHS]; - Mutex _paths_m; - - Identity _id; - - unsigned int _directPathPushCutoffCount; - unsigned int _credentialsCutoffCount; - unsigned int _QoSCutoffCount; - unsigned int _ACKCutoffCount; - - RingBuffer _pathChoiceHist; - - bool _linkIsBalanced; - bool _linkIsRedundant; - bool _remotePeerMultipathEnabled; - - int64_t _lastAggregateStatsReport; - int64_t _lastAggregateAllocation; - - char _interfaceListStr[256]; // 16 characters * 16 paths in a link - - AtomicCounter __refCount; }; } // namespace ZeroTier -// Add a swap() for shared ptr's to peers to speed up peer sorts -namespace std { - template<> - ZT_ALWAYS_INLINE void swap(ZeroTier::SharedPtr &a,ZeroTier::SharedPtr &b) { a.swap(b); } -} - #endif diff --git a/node/Poly1305.cpp b/node/Poly1305.cpp index a4f607629..c4db0f7f0 100644 --- a/node/Poly1305.cpp +++ b/node/Poly1305.cpp @@ -7,10 +7,7 @@ Public domain. #include "Constants.hpp" #include "Poly1305.hpp" -#include -#include -#include -#include +#include #ifdef __WINDOWS__ #pragma warning(disable: 4146) @@ -74,7 +71,7 @@ typedef struct poly1305_state_internal_t { } poly1305_state_internal_t; #if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN) -static ZT_ALWAYS_INLINE unsigned long long U8TO64(const unsigned char *p) +static inline unsigned long long U8TO64(const unsigned char *p) { return (((unsigned long long)(p[0] & 0xff) ) | @@ -91,7 +88,7 @@ static ZT_ALWAYS_INLINE unsigned long long U8TO64(const unsigned char *p) #endif #if defined(ZT_NO_TYPE_PUNNING) || (__BYTE_ORDER != __LITTLE_ENDIAN) -static ZT_ALWAYS_INLINE void U64TO8(unsigned char *p, unsigned long long v) +static inline void U64TO8(unsigned char *p, unsigned long long v) { p[0] = (v ) & 0xff; p[1] = (v >> 8) & 0xff; @@ -106,7 +103,7 @@ static ZT_ALWAYS_INLINE void U64TO8(unsigned char *p, unsigned long long v) #define U64TO8(p,v) ((*reinterpret_cast(p)) = (v)) #endif -static ZT_ALWAYS_INLINE void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) +static inline void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long long t0,t1; @@ -132,7 +129,7 @@ static ZT_ALWAYS_INLINE void poly1305_init(poly1305_context *ctx, const unsigned st->final = 0; } -static ZT_ALWAYS_INLINE void poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) +static inline void poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */ unsigned long long r0,r1,r2; @@ -183,7 +180,7 @@ static ZT_ALWAYS_INLINE void poly1305_blocks(poly1305_state_internal_t *st, cons st->h[2] = h2; } -static ZT_ALWAYS_INLINE void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) +static inline void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long long h0,h1,h2,c; @@ -274,7 +271,7 @@ typedef struct poly1305_state_internal_t { } poly1305_state_internal_t; /* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ -static ZT_ALWAYS_INLINE unsigned long +static inline unsigned long U8TO32(const unsigned char *p) { return (((unsigned long)(p[0] & 0xff) ) | @@ -284,7 +281,7 @@ U8TO32(const unsigned char *p) { } /* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ -static ZT_ALWAYS_INLINE void +static inline void U32TO8(unsigned char *p, unsigned long v) { p[0] = (v ) & 0xff; p[1] = (v >> 8) & 0xff; @@ -292,7 +289,7 @@ U32TO8(unsigned char *p, unsigned long v) { p[3] = (v >> 24) & 0xff; } -static ZT_ALWAYS_INLINE void +static inline void poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; @@ -320,7 +317,7 @@ poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { st->final = 0; } -static ZT_ALWAYS_INLINE void +static inline void poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { const unsigned long hibit = (st->final) ? 0 : (1 << 24); /* 1 << 128 */ unsigned long r0,r1,r2,r3,r4; @@ -379,7 +376,7 @@ poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t by st->h[4] = h4; } -static ZT_ALWAYS_INLINE void +static inline void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long h0,h1,h2,h3,h4,c; @@ -470,7 +467,7 @@ poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { #endif // MSC/GCC or not -static ZT_ALWAYS_INLINE void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { +static inline void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; size_t i; diff --git a/node/Revocation.hpp b/node/Revocation.hpp index bc7545d31..0f4231698 100644 --- a/node/Revocation.hpp +++ b/node/Revocation.hpp @@ -14,10 +14,10 @@ #ifndef ZT_REVOCATION_HPP #define ZT_REVOCATION_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include "Constants.hpp" #include "../include/ZeroTierOne.h" @@ -45,9 +45,9 @@ class Revocation : public Credential friend class Credential; public: - static inline Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; } + static ZT_ALWAYS_INLINE Credential::Type credentialType() { return Credential::CREDENTIAL_TYPE_REVOCATION; } - inline Revocation() : + ZT_ALWAYS_INLINE Revocation() : _id(0), _credentialId(0), _networkId(0), @@ -69,7 +69,7 @@ public: * @param tgt Target node whose credential(s) are being revoked * @param ct Credential type being revoked */ - inline Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) : + ZT_ALWAYS_INLINE Revocation(const uint32_t i,const uint64_t nwid,const uint32_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const Credential::Type ct) : _id(i), _credentialId(cid), _networkId(nwid), @@ -115,7 +115,7 @@ public: * @param RR Runtime environment to provide for peer lookup, etc. * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call */ - inline Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } + ZT_ALWAYS_INLINE Credential::VerifyResult verify(const RuntimeEnvironment *RR,void *tPtr) const { return _verify(RR,tPtr,*this); } template inline void serialize(Buffer &b,const bool forSign = false) const diff --git a/node/RingBuffer.hpp b/node/RingBuffer.hpp index 9a78d10bf..1839bc710 100644 --- a/node/RingBuffer.hpp +++ b/node/RingBuffer.hpp @@ -16,10 +16,9 @@ #include #include -#include -#include +#include +#include #include -#include namespace ZeroTier { @@ -43,7 +42,7 @@ private: bool wrap; public: - ZT_ALWAYS_INLINE RingBuffer() : + inline RingBuffer() : begin(0), end(0), wrap(false) @@ -54,7 +53,7 @@ public: /** * @return A pointer to the underlying buffer */ - ZT_ALWAYS_INLINE T *get_buf() + inline T *get_buf() { return buf + begin; } @@ -64,7 +63,7 @@ public: * @param n Number of elements to copy in * @return Number of elements we copied in */ - ZT_ALWAYS_INLINE size_t produce(size_t n) + inline size_t produce(size_t n) { n = std::min(n, getFree()); if (n == 0) { @@ -86,14 +85,14 @@ public: * Fast erase, O(1). * Merely reset the buffer pointer, doesn't erase contents */ - ZT_ALWAYS_INLINE void reset() { consume(count()); } + inline void reset() { consume(count()); } /** * adjust buffer index pointer as if we copied data out * @param n Number of elements we copied from the buffer * @return Number of elements actually available from the buffer */ - ZT_ALWAYS_INLINE size_t consume(size_t n) + inline size_t consume(size_t n) { n = std::min(n, count()); if (n == 0) { @@ -115,7 +114,7 @@ public: * @param data Buffer that is to be written to the ring * @param n Number of elements to write to the buffer */ - ZT_ALWAYS_INLINE size_t write(const T * data, size_t n) + inline size_t write(const T * data, size_t n) { n = std::min(n, getFree()); if (n == 0) { @@ -140,7 +139,7 @@ public: * * @param value A single value to be placed in the buffer */ - ZT_ALWAYS_INLINE void push(const T value) + inline void push(const T value) { if (count() == S) { consume(1); @@ -156,14 +155,14 @@ public: /** * @return The most recently pushed element on the buffer */ - ZT_ALWAYS_INLINE T get_most_recent() { return *(buf + end); } + inline T get_most_recent() { return *(buf + end); } /** * @param dest Destination buffer * @param n Size (in terms of number of elements) of the destination buffer * @return Number of elements read from the buffer */ - ZT_ALWAYS_INLINE size_t read(T *dest,size_t n) + inline size_t read(T *dest,size_t n) { n = std::min(n, count()); if (n == 0) { @@ -188,7 +187,7 @@ public: * * @return The number of elements in the buffer */ - ZT_ALWAYS_INLINE size_t count() + inline size_t count() { if (end == begin) { return wrap ? S : 0; @@ -204,12 +203,12 @@ public: /** * @return The number of slots that are unused in the buffer */ - ZT_ALWAYS_INLINE size_t getFree() { return S - count(); } + inline size_t getFree() { return S - count(); } /** * @return The arithmetic mean of the contents of the buffer */ - ZT_ALWAYS_INLINE float mean() + inline float mean() { size_t iterator = begin; float subtotal = 0; @@ -224,7 +223,7 @@ public: /** * @return The arithmetic mean of the most recent 'n' elements of the buffer */ - ZT_ALWAYS_INLINE float mean(size_t n) + inline float mean(size_t n) { n = n < S ? n : S; size_t iterator = begin; @@ -240,12 +239,12 @@ public: /** * @return The sample standard deviation of element values */ - ZT_ALWAYS_INLINE float stddev() { return sqrt(variance()); } + inline float stddev() { return sqrt(variance()); } /** * @return The variance of element values */ - ZT_ALWAYS_INLINE float variance() + inline float variance() { size_t iterator = begin; float cached_mean = mean(); @@ -263,7 +262,7 @@ public: /** * @return The number of elements of zero value */ - ZT_ALWAYS_INLINE size_t zeroCount() + inline size_t zeroCount() { size_t iterator = begin; size_t zeros = 0; @@ -281,7 +280,7 @@ public: * @param value Value to match against in buffer * @return The number of values held in the ring buffer which match a given value */ - ZT_ALWAYS_INLINE size_t countValue(T value) + inline size_t countValue(T value) { size_t iterator = begin; size_t cnt = 0; diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index a863c8197..038c37c5a 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -36,7 +36,7 @@ class Trace; class RuntimeEnvironment { public: - ZT_ALWAYS_INLINE RuntimeEnvironment(Node *n) : + inline RuntimeEnvironment(Node *n) : node(n) ,localNetworkController((NetworkController *)0) ,rtmem((void *)0) @@ -48,7 +48,7 @@ public: secretIdentityStr[0] = (char)0; } - ZT_ALWAYS_INLINE ~RuntimeEnvironment() { Utils::burn(secretIdentityStr,sizeof(secretIdentityStr)); } + inline ~RuntimeEnvironment() { Utils::burn(secretIdentityStr,sizeof(secretIdentityStr)); } // Node instance that owns this RuntimeEnvironment Node *const node; diff --git a/node/SHA512.cpp b/node/SHA512.cpp index d60eacec1..2437ba742 100644 --- a/node/SHA512.cpp +++ b/node/SHA512.cpp @@ -1,8 +1,8 @@ // This code is public domain, taken from a PD crypto source file on GitHub. -#include -#include -#include +#include +#include +#include #include "SHA512.hpp" #include "Utils.hpp" @@ -69,7 +69,7 @@ static const uint64_t K[80] = { #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) -static ZT_ALWAYS_INLINE void sha512_compress(sha512_state *const md,uint8_t *const buf) +static inline void sha512_compress(sha512_state *const md,uint8_t *const buf) { uint64_t S[8], W[80], t0, t1; int i; @@ -102,7 +102,7 @@ static ZT_ALWAYS_INLINE void sha512_compress(sha512_state *const md,uint8_t *con md->state[i] = md->state[i] + S[i]; } -static ZT_ALWAYS_INLINE void sha384_init(sha512_state *const md) +static inline void sha384_init(sha512_state *const md) { md->curlen = 0; md->length = 0; @@ -116,7 +116,7 @@ static ZT_ALWAYS_INLINE void sha384_init(sha512_state *const md) md->state[7] = 0x47b5481dbefa4fa4ULL; } -static ZT_ALWAYS_INLINE void sha512_init(sha512_state *const md) +static inline void sha512_init(sha512_state *const md) { md->curlen = 0; md->length = 0; @@ -130,7 +130,7 @@ static ZT_ALWAYS_INLINE void sha512_init(sha512_state *const md) md->state[7] = 0x5be0cd19137e2179ULL; } -static ZT_ALWAYS_INLINE void sha512_process(sha512_state *const md,const uint8_t *in,unsigned long inlen) +static inline void sha512_process(sha512_state *const md,const uint8_t *in,unsigned long inlen) { while (inlen > 0) { if (md->curlen == 0 && inlen >= 128) { @@ -153,7 +153,7 @@ static ZT_ALWAYS_INLINE void sha512_process(sha512_state *const md,const uint8_t } } -static ZT_ALWAYS_INLINE void sha512_done(sha512_state *const md,uint8_t *out) +static inline void sha512_done(sha512_state *const md,uint8_t *out) { int i; diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp index 3b27187f7..047433e5f 100644 --- a/node/Salsa20.hpp +++ b/node/Salsa20.hpp @@ -7,10 +7,10 @@ #ifndef ZT_SALSA20_HPP #define ZT_SALSA20_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include "Constants.hpp" #include "Utils.hpp" diff --git a/node/ScopedPtr.hpp b/node/ScopedPtr.hpp index 5fa97729a..09f408a93 100644 --- a/node/ScopedPtr.hpp +++ b/node/ScopedPtr.hpp @@ -27,14 +27,21 @@ template class ScopedPtr { public: - ZT_ALWAYS_INLINE ScopedPtr(T *const p) : _p(p) {} + explicit ZT_ALWAYS_INLINE ScopedPtr(T *const p) : _p(p) {} ZT_ALWAYS_INLINE ~ScopedPtr() { delete _p; } ZT_ALWAYS_INLINE T *operator->() const { return _p; } ZT_ALWAYS_INLINE T &operator*() const { return *_p; } - ZT_ALWAYS_INLINE operator bool() const { return (_p != (T *)0); } + explicit ZT_ALWAYS_INLINE operator bool() const { return (_p != (T *)0); } ZT_ALWAYS_INLINE T *ptr() const { return _p; } + ZT_ALWAYS_INLINE void swap(const ScopedPtr &p) + { + T *const tmp = _p; + _p = p._p; + p._p = tmp; + } + ZT_ALWAYS_INLINE bool operator==(const ScopedPtr &p) const { return (_p == p._p); } ZT_ALWAYS_INLINE bool operator!=(const ScopedPtr &p) const { return (_p != p._p); } ZT_ALWAYS_INLINE bool operator==(T *const p) const { return (_p == p); } @@ -42,9 +49,16 @@ public: private: ZT_ALWAYS_INLINE ScopedPtr() {} + ZT_ALWAYS_INLINE ScopedPtr(const ScopedPtr &p) : _p(nullptr) {} + ZT_ALWAYS_INLINE ScopedPtr &operator=(const ScopedPtr &p) { return *this; } T *const _p; }; } // namespace ZeroTier +namespace std { +template +ZT_ALWAYS_INLINE void swap(ZeroTier::ScopedPtr &a,ZeroTier::ScopedPtr &b) { a.swap(b); } +} + #endif diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 4e91a3ba6..8cd1399d7 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -11,12 +11,10 @@ */ /****/ -#include -#include -#include +#include +#include #include -#include #include "Constants.hpp" #include "SelfAwareness.hpp" @@ -36,13 +34,13 @@ namespace ZeroTier { class _ResetWithinScope { public: - ZT_ALWAYS_INLINE _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) : + inline _ResetWithinScope(void *tPtr,int64_t now,int inetAddressFamily,InetAddress::IpScope scope) : _now(now), _tPtr(tPtr), _family(inetAddressFamily), _scope(scope) {} - ZT_ALWAYS_INLINE bool operator()(const SharedPtr &p) + inline bool operator()(const SharedPtr &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); return true; diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp index 882cef07e..f76bcc719 100644 --- a/node/SelfAwareness.hpp +++ b/node/SelfAwareness.hpp @@ -59,13 +59,13 @@ private: InetAddress reporterPhysicalAddress; InetAddress::IpScope scope; - ZT_ALWAYS_INLINE PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {} - ZT_ALWAYS_INLINE PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {} + inline PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {} + inline PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {} - ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); } + inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); } - ZT_ALWAYS_INLINE bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); } - ZT_ALWAYS_INLINE bool operator!=(const PhySurfaceKey &k) const { return (!(*this == k)); } + inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); } + inline bool operator!=(const PhySurfaceKey &k) const { return (!(*this == k)); } }; struct PhySurfaceEntry { @@ -73,8 +73,8 @@ private: uint64_t ts; bool trusted; - ZT_ALWAYS_INLINE PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {} - ZT_ALWAYS_INLINE PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {} + inline PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {} + inline PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {} }; const RuntimeEnvironment *RR; diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp index 2c83ec997..56ecf5ec6 100644 --- a/node/SharedPtr.hpp +++ b/node/SharedPtr.hpp @@ -14,7 +14,6 @@ #ifndef ZT_SHAREDPTR_HPP #define ZT_SHAREDPTR_HPP -#include "Mutex.hpp" #include "AtomicCounter.hpp" namespace ZeroTier { @@ -132,4 +131,9 @@ private: } // namespace ZeroTier +namespace std { +template +ZT_ALWAYS_INLINE void swap(ZeroTier::SharedPtr &a,ZeroTier::SharedPtr &b) { a.swap(b); } +} + #endif diff --git a/node/Str.hpp b/node/Str.hpp index c805c2c7c..d7639af41 100644 --- a/node/Str.hpp +++ b/node/Str.hpp @@ -22,7 +22,7 @@ #include -#define ZT_STR_CAPACITY 254 +#define ZT_STR_CAPACITY 1021 namespace ZeroTier { @@ -150,7 +150,7 @@ public: return ((*this) << a.toString(tmp)); } - ZT_ALWAYS_INLINE Str &append(const char *s,const unsigned int max) + inline Str &append(const char *s,const unsigned int max) { if (likely(s != (const char *)0)) { unsigned long l = _l; @@ -187,35 +187,10 @@ public: ZT_ALWAYS_INLINE bool operator<=(const char *s) const { return (strcmp(_s,s) <= 0); } ZT_ALWAYS_INLINE bool operator>=(const char *s) const { return (strcmp(_s,s) >= 0); } - ZT_ALWAYS_INLINE unsigned long hashCode() const - { - const char *p = _s; - unsigned long h = 0; - char c; - while ((c = *(p++))) - h = (31 * h) + (unsigned long)c; - return h; - } - - template - inline void serialize(Buffer &b,const bool forSign = false) const - { - b.append(_l); - b.append(_s,(unsigned int)_l); - } - - template - inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) - { - unsigned int p = startAt; - _l = (uint8_t)b[p++]; - memcpy(_s,b.field(p,(unsigned int)_l),(unsigned long)_l); - p += (unsigned int)_l; - return (p - startAt); - } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return Utils::hashString(_s,_l); } private: - uint8_t _l; + uint16_t _l; char _s[ZT_STR_CAPACITY+1]; }; diff --git a/node/Switch.cpp b/node/Switch.cpp index 1b76bf082..5c487cc94 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -11,8 +11,8 @@ */ /****/ -#include -#include +#include +#include #include #include @@ -35,8 +35,7 @@ namespace ZeroTier { Switch::Switch(const RuntimeEnvironment *renv) : RR(renv), - _lastCheckedQueues(0), - _lastUniteAttempt(8) // only really used on root servers and upstreams, and it'll grow there just fine + _lastCheckedQueues(0) { } @@ -58,15 +57,11 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre if (destination != RR->identity.address()) { if (fragment.hops() < ZT_RELAY_MAX_HOPS) { fragment.incrementHops(); - - // Note: we don't bother initiating NAT-t for fragments, since heads will set that off. - // It wouldn't hurt anything, just redundant and unnecessary. SharedPtr relayTo = RR->topology->get(destination); - if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) { - // Don't know peer or no direct path -- so relay via someone upstream - relayTo = RR->topology->findRelayTo(now,destination); + if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now))) { + relayTo = RR->topology->root(); if (relayTo) - relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true); + relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now); } } } else { @@ -125,38 +120,30 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre return; if (destination != RR->identity.address()) { + // This packet is not for this node, so possibly relay it ---------- + Packet packet(data,len); if (packet.hops() < ZT_RELAY_MAX_HOPS) { packet.incrementHops(); SharedPtr relayTo = RR->topology->get(destination); - if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) { - if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { - const SharedPtr sourcePeer(RR->topology->get(source)); - if (sourcePeer) - relayTo->introduce(tPtr,now,sourcePeer); - } - } else { - relayTo = RR->topology->findRelayTo(now,destination); - if ((relayTo)&&(relayTo->address() != source)) { - if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) { - const SharedPtr sourcePeer(RR->topology->get(source)); - if (sourcePeer) - relayTo->introduce(tPtr,now,sourcePeer); - } - } + if ((!relayTo)||(!relayTo->sendDirect(tPtr,packet.data(),packet.size(),now))) { + relayTo = RR->topology->root(); + if ((relayTo)&&(relayTo->address() != source)) + relayTo->sendDirect(tPtr,packet.data(),packet.size(),now); } } + } else if ((reinterpret_cast(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) { - // Packet is the head of a fragmented packet series + // Packet is the head of a fragmented packet series ---------------- const uint64_t packetId = ( - (((uint64_t)reinterpret_cast(data)[0]) << 56) | - (((uint64_t)reinterpret_cast(data)[1]) << 48) | - (((uint64_t)reinterpret_cast(data)[2]) << 40) | - (((uint64_t)reinterpret_cast(data)[3]) << 32) | - (((uint64_t)reinterpret_cast(data)[4]) << 24) | - (((uint64_t)reinterpret_cast(data)[5]) << 16) | - (((uint64_t)reinterpret_cast(data)[6]) << 8) | + (((uint64_t)reinterpret_cast(data)[0]) << 56U) | + (((uint64_t)reinterpret_cast(data)[1]) << 48U) | + (((uint64_t)reinterpret_cast(data)[2]) << 40U) | + (((uint64_t)reinterpret_cast(data)[3]) << 32U) | + (((uint64_t)reinterpret_cast(data)[4]) << 24U) | + (((uint64_t)reinterpret_cast(data)[5]) << 16U) | + (((uint64_t)reinterpret_cast(data)[6]) << 8U) | ((uint64_t)reinterpret_cast(data)[7]) ); @@ -172,10 +159,10 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre rq->haveFragments = 1; rq->complete = false; } else if (!(rq->haveFragments & 1)) { - // If we have other fragments but no head, see if we are complete with the head + // Check if packet is complete ----------------------------------- if ((rq->totalFragments > 1)&&(Utils::countBits(rq->haveFragments |= 1) == rq->totalFragments)) { - // We have all fragments -- assemble and process full Packet + // We have all fragments -- assemble and process full Packet --- rq->frag0.init(data,len,path,now); for(unsigned int f=1;ftotalFragments;++f) @@ -186,13 +173,17 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre } else { rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something } + } else { - // Still waiting on more fragments, but keep the head + // Still waiting on more fragments, but keep the head ---------- + rq->frag0.init(data,len,path,now); + } } // else this is a duplicate head, ignore } else { - // Packet is unfragmented, so just process it + + // Packet is unfragmented, so just process it ---------------------- IncomingPacket packet(data,len,path,now); if (!packet.tryDecode(RR,tPtr)) { RXQueueEntry *const rq = _nextRXQueueEntry(); @@ -204,6 +195,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre rq->haveFragments = 1; rq->complete = true; } + } // -------------------------------------------------------------------- @@ -226,7 +218,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const } } - uint8_t qosBucket = ZT_QOS_DEFAULT_BUCKET; + uint8_t qosBucket = 0; if (to.isMulticast()) { MulticastGroup multicastGroup(to,0); @@ -279,8 +271,8 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const break; } } else if (sipNetmaskBits == 40) { // ZT-6PLANE /40 ??? - const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32)) & 0xffffffff); - if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24) & 0xff)) && (my6[2] == (uint8_t)((nwid32 >> 16) & 0xff)) && (my6[3] == (uint8_t)((nwid32 >> 8) & 0xff)) && (my6[4] == (uint8_t)(nwid32 & 0xff))) { + const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32U)) & 0xffffffffU); + if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24U) & 0xffU)) && (my6[2] == (uint8_t)((nwid32 >> 16U) & 0xffU)) && (my6[3] == (uint8_t)((nwid32 >> 8U) & 0xffU)) && (my6[4] == (uint8_t)(nwid32 & 0xffU))) { unsigned int ptr = 0; while (ptr != 5) { if (pkt6[ptr] != my6[ptr]) @@ -320,10 +312,10 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i]; uint32_t checksum = 0; for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]); - while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16); + while ((checksum >> 16U)) checksum = (checksum & 0xffffU) + (checksum >> 16U); checksum = ~checksum; - adv[42] = (checksum >> 8) & 0xff; - adv[43] = checksum & 0xff; + adv[42] = (checksum >> 8U) & 0xffU; + adv[43] = checksum & 0xffU; RR->node->putFrame(tPtr,network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72); return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query. @@ -364,10 +356,12 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const len); */ } else if (to == network->mac()) { - // Destination is this node, so just reinject it + // Destination is this node, so just reinject it ------------------------- + RR->node->putFrame(tPtr,network->id(),network->userPtr(),from,to,etherType,vlanId,data,len); + } else if (to[0] == MAC::firstOctetForNetwork(network->id())) { - // Destination is another ZeroTier peer on the same network + // Destination is another ZeroTier peer on the same network -------------- Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this SharedPtr toPeer(RR->topology->get(toZT)); @@ -387,20 +381,14 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const from.appendTo(outp); outp.append((uint16_t)etherType); outp.append(data,len); - if (!network->config().disableCompression()) - outp.compress(); - aqm_enqueue(tPtr,network,outp,true,qosBucket); } else { Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); outp.append(network->id()); outp.append((uint16_t)etherType); outp.append(data,len); - if (!network->config().disableCompression()) - outp.compress(); - aqm_enqueue(tPtr,network,outp,true,qosBucket); } } else { - // Destination is bridged behind a remote peer + // Destination is bridged behind a remote peer --------------------------- // We filter with a NULL destination ZeroTier address first. Filtrations // for each ZT destination are also done below. This is the same rationale @@ -418,7 +406,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const std::vector
activeBridges; for(unsigned int i=0;iconfig().specialistCount;++i) { if ((network->config().specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) - activeBridges.push_back(network->config().specialists[i]); + activeBridges.push_back(Address(network->config().specialists[i])); } if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->config().permitsBridging(bridges[0]))) { /* We have a known bridge route for this MAC, send it there. */ @@ -455,9 +443,6 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const from.appendTo(outp); outp.append((uint16_t)etherType); outp.append(data,len); - if (!network->config().disableCompression()) - outp.compress(); - aqm_enqueue(tPtr,network,outp,true,qosBucket); } else { RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)"); } @@ -465,263 +450,6 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const } } -void Switch::aqm_enqueue(void *tPtr, const SharedPtr &network, Packet &packet,bool encrypt,int qosBucket) -{ - if(!network->qosEnabled()) { - send(tPtr, packet, encrypt); - return; - } - NetworkQoSControlBlock *nqcb = _netQueueControlBlock[network->id()]; - if (!nqcb) { - // DEBUG_INFO("creating network QoS control block (NQCB) for network %llx", network->id()); - nqcb = new NetworkQoSControlBlock(); - _netQueueControlBlock[network->id()] = nqcb; - // Initialize ZT_QOS_NUM_BUCKETS queues and place them in the INACTIVE list - // These queues will be shuffled between the new/old/inactive lists by the enqueue/dequeue algorithm - for (int i=0; iinactiveQueues.push_back(new ManagedQueue(i)); - } - } - - if (packet.verb() != Packet::VERB_FRAME && packet.verb() != Packet::VERB_EXT_FRAME) { - // DEBUG_INFO("skipping, no QoS for this packet, verb=%x", packet.verb()); - // just send packet normally, no QoS for ZT protocol traffic - send(tPtr, packet, encrypt); - } - - _aqm_m.lock(); - - // Enqueue packet and move queue to appropriate list - - const Address dest(packet.destination()); - TXQueueEntry *txEntry = new TXQueueEntry(dest,RR->node->now(),packet,encrypt); - - ManagedQueue *selectedQueue = nullptr; - for (size_t i=0; ioldQueues.size()) { // search old queues first (I think this is best since old would imply most recent usage of the queue) - if (nqcb->oldQueues[i]->id == qosBucket) { - selectedQueue = nqcb->oldQueues[i]; - } - } if (i < nqcb->newQueues.size()) { // search new queues (this would imply not often-used queues) - if (nqcb->newQueues[i]->id == qosBucket) { - selectedQueue = nqcb->newQueues[i]; - } - } if (i < nqcb->inactiveQueues.size()) { // search inactive queues - if (nqcb->inactiveQueues[i]->id == qosBucket) { - selectedQueue = nqcb->inactiveQueues[i]; - // move queue to end of NEW queue list - selectedQueue->byteCredit = ZT_QOS_QUANTUM; - // DEBUG_INFO("moving q=%p from INACTIVE to NEW list", selectedQueue); - nqcb->newQueues.push_back(selectedQueue); - nqcb->inactiveQueues.erase(nqcb->inactiveQueues.begin() + i); - } - } - } - if (!selectedQueue) { - return; - } - - selectedQueue->q.push_back(txEntry); - selectedQueue->byteLength+=txEntry->packet.payloadLength(); - nqcb->_currEnqueuedPackets++; - - // DEBUG_INFO("nq=%2lu, oq=%2lu, iq=%2lu, nqcb.size()=%3d, bucket=%2d, q=%p", nqcb->newQueues.size(), nqcb->oldQueues.size(), nqcb->inactiveQueues.size(), nqcb->_currEnqueuedPackets, qosBucket, selectedQueue); - - // Drop a packet if necessary - ManagedQueue *selectedQueueToDropFrom = nullptr; - if (nqcb->_currEnqueuedPackets > ZT_QOS_MAX_ENQUEUED_PACKETS) - { - // DEBUG_INFO("too many enqueued packets (%d), finding packet to drop", nqcb->_currEnqueuedPackets); - int maxQueueLength = 0; - for (size_t i=0; ioldQueues.size()) { - if (nqcb->oldQueues[i]->byteLength > maxQueueLength) { - maxQueueLength = nqcb->oldQueues[i]->byteLength; - selectedQueueToDropFrom = nqcb->oldQueues[i]; - } - } if (i < nqcb->newQueues.size()) { - if (nqcb->newQueues[i]->byteLength > maxQueueLength) { - maxQueueLength = nqcb->newQueues[i]->byteLength; - selectedQueueToDropFrom = nqcb->newQueues[i]; - } - } if (i < nqcb->inactiveQueues.size()) { - if (nqcb->inactiveQueues[i]->byteLength > maxQueueLength) { - maxQueueLength = nqcb->inactiveQueues[i]->byteLength; - selectedQueueToDropFrom = nqcb->inactiveQueues[i]; - } - } - } - if (selectedQueueToDropFrom) { - // DEBUG_INFO("dropping packet from head of largest queue (%d payload bytes)", maxQueueLength); - int sizeOfDroppedPacket = selectedQueueToDropFrom->q.front()->packet.payloadLength(); - delete selectedQueueToDropFrom->q.front(); - selectedQueueToDropFrom->q.pop_front(); - selectedQueueToDropFrom->byteLength-=sizeOfDroppedPacket; - nqcb->_currEnqueuedPackets--; - } - } - _aqm_m.unlock(); - aqm_dequeue(tPtr); -} - -uint64_t Switch::control_law(uint64_t t, int count) -{ - return (uint64_t)(t + ZT_QOS_INTERVAL / sqrt(count)); -} - -Switch::dqr Switch::dodequeue(ManagedQueue *q, uint64_t now) -{ - dqr r; - r.ok_to_drop = false; - r.p = q->q.front(); - - if (r.p == NULL) { - q->first_above_time = 0; - return r; - } - uint64_t sojourn_time = now - r.p->creationTime; - if (sojourn_time < ZT_QOS_TARGET || q->byteLength <= ZT_DEFAULT_MTU) { - // went below - stay below for at least interval - q->first_above_time = 0; - } else { - if (q->first_above_time == 0) { - // just went above from below. if still above at - // first_above_time, will say it's ok to drop. - q->first_above_time = now + ZT_QOS_INTERVAL; - } else if (now >= q->first_above_time) { - r.ok_to_drop = true; - } - } - return r; -} - -Switch::TXQueueEntry * Switch::CoDelDequeue(ManagedQueue *q, bool isNew, uint64_t now) -{ - dqr r = dodequeue(q, now); - - if (q->dropping) { - if (!r.ok_to_drop) { - q->dropping = false; - } - while (now >= q->drop_next && q->dropping) { - q->q.pop_front(); // drop - r = dodequeue(q, now); - if (!r.ok_to_drop) { - // leave dropping state - q->dropping = false; - } else { - ++(q->count); - // schedule the next drop. - q->drop_next = control_law(q->drop_next, q->count); - } - } - } else if (r.ok_to_drop) { - q->q.pop_front(); // drop - r = dodequeue(q, now); - q->dropping = true; - q->count = (q->count > 2 && now - q->drop_next < 8*ZT_QOS_INTERVAL)? - q->count - 2 : 1; - q->drop_next = control_law(now, q->count); - } - return r.p; -} - -void Switch::aqm_dequeue(void *tPtr) -{ - // Cycle through network-specific QoS control blocks - for(std::map::iterator nqcb(_netQueueControlBlock.begin());nqcb!=_netQueueControlBlock.end();) { - if (!(*nqcb).second->_currEnqueuedPackets) { - return; - } - - uint64_t now = RR->node->now(); - TXQueueEntry *entryToEmit = nullptr; - std::vector *currQueues = &((*nqcb).second->newQueues); - std::vector *oldQueues = &((*nqcb).second->oldQueues); - std::vector *inactiveQueues = &((*nqcb).second->inactiveQueues); - - _aqm_m.lock(); - - // Attempt dequeue from queues in NEW list - bool examiningNewQueues = true; - while (currQueues->size()) { - ManagedQueue *queueAtFrontOfList = currQueues->front(); - if (queueAtFrontOfList->byteCredit < 0) { - queueAtFrontOfList->byteCredit += ZT_QOS_QUANTUM; - // Move to list of OLD queues - // DEBUG_INFO("moving q=%p from NEW to OLD list", queueAtFrontOfList); - oldQueues->push_back(queueAtFrontOfList); - currQueues->erase(currQueues->begin()); - } else { - entryToEmit = CoDelDequeue(queueAtFrontOfList, examiningNewQueues, now); - if (!entryToEmit) { - // Move to end of list of OLD queues - // DEBUG_INFO("moving q=%p from NEW to OLD list", queueAtFrontOfList); - oldQueues->push_back(queueAtFrontOfList); - currQueues->erase(currQueues->begin()); - } - else { - int len = entryToEmit->packet.payloadLength(); - queueAtFrontOfList->byteLength -= len; - queueAtFrontOfList->byteCredit -= len; - // Send the packet! - queueAtFrontOfList->q.pop_front(); - send(tPtr, entryToEmit->packet, entryToEmit->encrypt); - (*nqcb).second->_currEnqueuedPackets--; - } - if (queueAtFrontOfList) { - //DEBUG_INFO("dequeuing from q=%p, len=%lu in NEW list (byteCredit=%d)", queueAtFrontOfList, queueAtFrontOfList->q.size(), queueAtFrontOfList->byteCredit); - } - break; - } - } - - // Attempt dequeue from queues in OLD list - examiningNewQueues = false; - currQueues = &((*nqcb).second->oldQueues); - while (currQueues->size()) { - ManagedQueue *queueAtFrontOfList = currQueues->front(); - if (queueAtFrontOfList->byteCredit < 0) { - queueAtFrontOfList->byteCredit += ZT_QOS_QUANTUM; - oldQueues->push_back(queueAtFrontOfList); - currQueues->erase(currQueues->begin()); - } else { - entryToEmit = CoDelDequeue(queueAtFrontOfList, examiningNewQueues, now); - if (!entryToEmit) { - //DEBUG_INFO("moving q=%p from OLD to INACTIVE list", queueAtFrontOfList); - // Move to inactive list of queues - inactiveQueues->push_back(queueAtFrontOfList); - currQueues->erase(currQueues->begin()); - } - else { - int len = entryToEmit->packet.payloadLength(); - queueAtFrontOfList->byteLength -= len; - queueAtFrontOfList->byteCredit -= len; - queueAtFrontOfList->q.pop_front(); - send(tPtr, entryToEmit->packet, entryToEmit->encrypt); - (*nqcb).second->_currEnqueuedPackets--; - } - if (queueAtFrontOfList) { - //DEBUG_INFO("dequeuing from q=%p, len=%lu in OLD list (byteCredit=%d)", queueAtFrontOfList, queueAtFrontOfList->q.size(), queueAtFrontOfList->byteCredit); - } - break; - } - } - nqcb++; - _aqm_m.unlock(); - } -} - -void Switch::removeNetworkQoSControlBlock(uint64_t nwid) -{ - NetworkQoSControlBlock *nq = _netQueueControlBlock[nwid]; - if (nq) { - _netQueueControlBlock.erase(nwid); - delete nq; - nq = NULL; - } -} - void Switch::send(void *tPtr,Packet &packet,bool encrypt) { const Address dest(packet.destination()); @@ -753,12 +481,12 @@ void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr) else last = now; } - const SharedPtr root(RR->topology->root(now)); + const SharedPtr root(RR->topology->root()); if (root) { Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS); addr.appendTo(outp); RR->node->expectReplyTo(outp.packetId()); - root->sendDirect(tPtr,outp.data(),outp.size(),now,true); + root->sendDirect(tPtr,outp.data(),outp.size(),now); } } @@ -835,17 +563,6 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now) } } - { - Mutex::Lock _l(_lastUniteAttempt_m); - Hashtable< _LastUniteKey,uint64_t >::Iterator i(_lastUniteAttempt); - _LastUniteKey *k = (_LastUniteKey *)0; - uint64_t *v = (uint64_t *)0; - while (i.next(k,v)) { - if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 8)) - _lastUniteAttempt.erase(*k); - } - } - { Mutex::Lock _l(_lastSentWhoisRequest_m); Hashtable< Address,int64_t >::Iterator i(_lastSentWhoisRequest); @@ -860,17 +577,6 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now) return ZT_WHOIS_RETRY_DELAY; } -bool Switch::_shouldUnite(const int64_t now,const Address &source,const Address &destination) -{ - Mutex::Lock _l(_lastUniteAttempt_m); - uint64_t &ts = _lastUniteAttempt[_LastUniteKey(source,destination)]; - if ((now - ts) >= ZT_MIN_UNITE_INTERVAL) { - ts = now; - return true; - } - return false; -} - bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) { SharedPtr viaPath; @@ -879,23 +585,23 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) const SharedPtr peer(RR->topology->get(destination)); if (peer) { - viaPath = peer->getAppropriatePath(now,false); + viaPath = peer->path(now); if (!viaPath) { if (peer->rateGateTryStaticPath(now)) { InetAddress tryAddr; - bool gotPath = RR->node->externalPathLookup(tPtr,peer->address(),AF_INET6,tryAddr); + bool gotPath = RR->node->externalPathLookup(tPtr,peer->identity(),AF_INET6,tryAddr); if ((gotPath)&&(tryAddr)) { peer->sendHELLO(tPtr,-1,tryAddr,now); } else { - gotPath = RR->node->externalPathLookup(tPtr,peer->address(),AF_INET,tryAddr); + gotPath = RR->node->externalPathLookup(tPtr,peer->identity(),AF_INET,tryAddr); if ((gotPath)&&(tryAddr)) peer->sendHELLO(tPtr,-1,tryAddr,now); } } - const SharedPtr relay(RR->topology->findRelayTo(now,destination)); + const SharedPtr relay(RR->topology->root()); if (relay) { - viaPath = relay->getAppropriatePath(now,true); + viaPath = relay->path(now); if (!viaPath) return false; } @@ -913,8 +619,6 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) unsigned int chunkSize = std::min(packet.size(),mtu); packet.setFragmented(chunkSize < packet.size()); - peer->recordOutgoingPacket(viaPath, packet.packetId(), packet.payloadLength(), packet.verb(), now); - if (trustedPathId) { packet.setTrusted(trustedPathId); } else { diff --git a/node/Switch.hpp b/node/Switch.hpp index 66ea340d8..3bb6c0454 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -31,16 +31,6 @@ #include "IncomingPacket.hpp" #include "Hashtable.hpp" -/* Ethernet frame types that might be relevant to us */ -#define ZT_ETHERTYPE_IPV4 0x0800 -#define ZT_ETHERTYPE_ARP 0x0806 -#define ZT_ETHERTYPE_RARP 0x8035 -#define ZT_ETHERTYPE_ATALK 0x809b -#define ZT_ETHERTYPE_AARP 0x80f3 -#define ZT_ETHERTYPE_IPX_A 0x8137 -#define ZT_ETHERTYPE_IPX_B 0x8138 -#define ZT_ETHERTYPE_IPV6 0x86dd - namespace ZeroTier { class RuntimeEnvironment; @@ -56,16 +46,8 @@ class Peer; */ class Switch { - struct ManagedQueue; - struct TXQueueEntry; - - typedef struct { - TXQueueEntry *p; - bool ok_to_drop; - } dqr; - public: - Switch(const RuntimeEnvironment *renv); + explicit Switch(const RuntimeEnvironment *renv); /** * Called when a packet is received from the real network @@ -92,62 +74,6 @@ public: */ void onLocalEthernet(void *tPtr,const SharedPtr &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); - /** - * Determines the next drop schedule for packets in the TX queue - * - * @param t Current time - * @param count Number of packets dropped this round - */ - uint64_t control_law(uint64_t t, int count); - - /** - * Selects a packet eligible for transmission from a TX queue. According to the control law, multiple packets - * may be intentionally dropped before a packet is returned to the AQM scheduler. - * - * @param q The TX queue that is being dequeued from - * @param now Current time - */ - dqr dodequeue(ManagedQueue *q, uint64_t now); - - /** - * Presents a packet to the AQM scheduler. - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - * @param network Network that the packet shall be sent over - * @param packet Packet to be sent - * @param encrypt Encrypt packet payload? (always true except for HELLO) - * @param qosBucket Which bucket the rule-system determined this packet should fall into - */ - void aqm_enqueue(void *tPtr, const SharedPtr &network, Packet &packet,bool encrypt,int qosBucket); - - /** - * Performs a single AQM cycle and dequeues and transmits all eligible packets on all networks - * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call - */ - void aqm_dequeue(void *tPtr); - - /** - * Calls the dequeue mechanism and adjust queue state variables - * - * @param q The TX queue that is being dequeued from - * @param isNew Whether or not this queue is in the NEW list - * @param now Current time - */ - Switch::TXQueueEntry * CoDelDequeue(ManagedQueue *q, bool isNew, uint64_t now); - - /** - * Removes QoS Queues and flow state variables for a specific network. These queues are created - * automatically upon the transmission of the first packet from this peer to another peer on the - * given network. - * - * The reason for existence of queues and flow state variables specific to each network is so that - * each network's QoS rules function independently. - * - * @param nwid Network ID - */ - void removeNetworkQoSControlBlock(uint64_t nwid); - /** * Send a packet to a ZeroTier address (destination in packet) * @@ -173,7 +99,7 @@ public: * @param now Current time * @param addr Address to look up */ - void requestWhois(void *tPtr,const int64_t now,const Address &addr); + void requestWhois(void *tPtr,int64_t now,const Address &addr); /** * Run any processes that are waiting for this peer's identity @@ -198,7 +124,6 @@ public: unsigned long doTimerTasks(void *tPtr,int64_t now); private: - bool _shouldUnite(const int64_t now,const Address &source,const Address &destination); bool _trySend(void *tPtr,Packet &packet,bool encrypt); // packet is modified if return is true const RuntimeEnvironment *const RR; @@ -211,7 +136,7 @@ private: // Packets waiting for WHOIS replies or other decode info or missing fragments struct RXQueueEntry { - RXQueueEntry() : timestamp(0) {} + ZT_ALWAYS_INLINE RXQueueEntry() : timestamp(0) {} volatile int64_t timestamp; // 0 if entry is not in use volatile uint64_t packetId; IncomingPacket frag0; // head of packet @@ -225,7 +150,7 @@ private: AtomicCounter _rxQueuePtr; // Returns matching or next available RX queue entry - ZT_ALWAYS_INLINE RXQueueEntry *_findRXQueueEntry(uint64_t packetId) + inline RXQueueEntry *_findRXQueueEntry(uint64_t packetId) { const unsigned int current = static_cast(_rxQueuePtr.load()); for(unsigned int k=1;k<=ZT_RX_QUEUE_SIZE;++k) { @@ -238,7 +163,7 @@ private: } // Returns current entry in rx queue ring buffer and increments ring pointer - ZT_ALWAYS_INLINE RXQueueEntry *_nextRXQueueEntry() + inline RXQueueEntry *_nextRXQueueEntry() { return &(_rxQueue[static_cast((++_rxQueuePtr) - 1) % ZT_RX_QUEUE_SIZE]); } @@ -246,8 +171,8 @@ private: // ZeroTier-layer TX queue entry struct TXQueueEntry { - TXQueueEntry() {} - TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) : + ZT_ALWAYS_INLINE TXQueueEntry() {} + ZT_ALWAYS_INLINE TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) : dest(d), creationTime(ct), packet(p), @@ -260,58 +185,6 @@ private: }; std::list< TXQueueEntry > _txQueue; Mutex _txQueue_m; - Mutex _aqm_m; - - // Tracks sending of VERB_RENDEZVOUS to relaying peers - struct _LastUniteKey - { - _LastUniteKey() : x(0),y(0) {} - _LastUniteKey(const Address &a1,const Address &a2) - { - if (a1 > a2) { - x = a2.toInt(); - y = a1.toInt(); - } else { - x = a1.toInt(); - y = a2.toInt(); - } - } - ZT_ALWAYS_INLINE unsigned long hashCode() const { return ((unsigned long)x ^ (unsigned long)y); } - ZT_ALWAYS_INLINE bool operator==(const _LastUniteKey &k) const { return ((x == k.x)&&(y == k.y)); } - ZT_ALWAYS_INLINE bool operator!=(const _LastUniteKey &k) const { return ((x != k.x)||(y != k.y)); } - uint64_t x,y; - }; - Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior - Mutex _lastUniteAttempt_m; - - // Queue with additional flow state variables - struct ManagedQueue - { - ManagedQueue(int id) : - id(id), - byteCredit(ZT_QOS_QUANTUM), - byteLength(0), - dropping(false) - {} - int id; - int byteCredit; - int byteLength; - uint64_t first_above_time; - uint32_t count; - uint64_t drop_next; - bool dropping; - uint64_t drop_next_time; - std::list< TXQueueEntry *> q; - }; - // To implement fq_codel we need to maintain a queue of queues - struct NetworkQoSControlBlock - { - int _currEnqueuedPackets; - std::vector newQueues; - std::vector oldQueues; - std::vector inactiveQueues; - }; - std::map _netQueueControlBlock; }; } // namespace ZeroTier diff --git a/node/Tag.hpp b/node/Tag.hpp index 50f787f43..9fd1a423b 100644 --- a/node/Tag.hpp +++ b/node/Tag.hpp @@ -14,10 +14,10 @@ #ifndef ZT_TAG_HPP #define ZT_TAG_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include "Constants.hpp" #include "Credential.hpp" @@ -96,7 +96,7 @@ public: * @param signer Signing identity, must have private key * @return True if signature was successful */ - ZT_ALWAYS_INLINE bool sign(const Identity &signer) + inline bool sign(const Identity &signer) { if (signer.hasPrivate()) { Buffer tmp; diff --git a/node/Topology.cpp b/node/Topology.cpp new file mode 100644 index 000000000..50a112a0a --- /dev/null +++ b/node/Topology.cpp @@ -0,0 +1,153 @@ +/* + * 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 "Topology.hpp" + +namespace ZeroTier { + +struct _RootSortComparisonOperator +{ + ZT_ALWAYS_INLINE _RootSortComparisonOperator(const int64_t now) : _now(now) {} + ZT_ALWAYS_INLINE bool operator()(const SharedPtr &a,const SharedPtr &b) + { + const int64_t now = _now; + if (a->alive(now)) { + if (b->alive(now)) + return (a->latency() < b->latency()); + return true; + } + return false; + } + const int64_t _now; +}; + +Topology::Topology(const RuntimeEnvironment *renv,const Identity &myId) : + RR(renv), + _myIdentity(myId), + _numConfiguredPhysicalPaths(0), + _peers(128), + _paths(256) +{ +} + +Topology::~Topology() +{ +} + +void Topology::getAllPeers(std::vector< SharedPtr > &allPeers) const +{ + RWMutex::RLock l(_peers_l); + allPeers.clear(); + allPeers.reserve(_peers.size()); + Hashtable< Address,SharedPtr >::Iterator i(*(const_cast > *>(&_peers))); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) + allPeers.push_back(*p); +} + +void Topology::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig) +{ + if (!pathNetwork) { + _numConfiguredPhysicalPaths = 0; + } else { + std::map cpaths; + for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i ZT_MAX_PHYSMTU) + pc.mtu = ZT_MAX_PHYSMTU; + + cpaths[*(reinterpret_cast(pathNetwork))] = pc; + } else { + cpaths.erase(*(reinterpret_cast(pathNetwork))); + } + + unsigned int cnt = 0; + for(std::map::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cntfirst; + _physicalPathConfig[cnt].second = i->second; + ++cnt; + } + _numConfiguredPhysicalPaths = cnt; + } +} + +void Topology::addRoot(const Identity &id) +{ + if (id == _myIdentity) return; // sanity check + RWMutex::Lock l1(_peers_l); + std::pair< std::set::iterator,bool > ir(_roots.insert(id)); + if (ir.second) { + SharedPtr &p = _peers[id.address()]; + if (!p) + p.set(new Peer(RR,_myIdentity,id)); + _rootPeers.push_back(p); + } +} + +bool Topology::removeRoot(const Identity &id) +{ + RWMutex::Lock l1(_peers_l); + std::set::iterator r(_roots.find(id)); + if (r != _roots.end()) { + for(std::vector< SharedPtr >::iterator p(_rootPeers.begin());p!=_rootPeers.end();++p) { + if ((*p)->identity() == id) { + _rootPeers.erase(p); + break; + } + } + _roots.erase(r); + return true; + } + return false; +} + +void Topology::rankRoots(const int64_t now) +{ + RWMutex::Lock l1(_peers_l); + std::sort(_rootPeers.begin(),_rootPeers.end(),_RootSortComparisonOperator(now)); +} + +void Topology::doPeriodicTasks(const int64_t now) +{ + { + RWMutex::Lock l1(_peers_l); + Hashtable< Address,SharedPtr >::Iterator i(_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) { + if ( (!(*p)->alive(now)) && (_roots.count((*p)->identity()) == 0) ) + _peers.erase(*a); + } + } + { + RWMutex::Lock l1(_paths_l); + Hashtable< Path::HashKey,SharedPtr >::Iterator i(_paths); + Path::HashKey *k = (Path::HashKey *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(k,p)) { + if (p->references() <= 1) + _paths.erase(*k); + } + } +} + +} // namespace ZeroTier diff --git a/node/Topology.hpp b/node/Topology.hpp index fc66b665a..cf30b86d0 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -14,11 +14,10 @@ #ifndef ZT_TOPOLOGY_HPP #define ZT_TOPOLOGY_HPP -#include -#include +#include +#include #include -#include #include #include #include @@ -33,7 +32,6 @@ #include "Mutex.hpp" #include "InetAddress.hpp" #include "Hashtable.hpp" -#include "Locator.hpp" #include "SharedPtr.hpp" #include "ScopedPtr.hpp" @@ -46,48 +44,9 @@ class RuntimeEnvironment; */ class Topology { -private: - struct _RootRankingFunction - { - ZT_ALWAYS_INLINE _RootRankingFunction() : bestRoot(),bestRootLatency(0xffff) {} - ZT_ALWAYS_INLINE bool operator()(const SharedPtr &peer,const std::vector &phy) - { - const unsigned int lat = peer->latency(now); - if ((!bestRoot)||((lat <= bestRootLatency)&&(peer->getAppropriatePath(now,false)))) { - bestRoot = peer; - bestRootLatency = lat; - } - return true; - } - int64_t now; - SharedPtr bestRoot; - unsigned int bestRootLatency; - }; - - ZT_ALWAYS_INLINE void _updateRoots() - { - // assumes _roots_l is locked - _rootIdentities.clear(); - Hashtable< Str,Locator >::Iterator i(_roots); - Str *k = (Str *)0; - SharedPtr< const Locator > *v = (SharedPtr< const Locator > *)0; - while (i.next(k,v)) { - if (*v) - _rootIdentities.set(v->id(),true); - } - } - public: - ZT_ALWAYS_INLINE Topology(const RuntimeEnvironment *renv,const Identity &myId) : - RR(renv), - _myIdentity(myId), - _numConfiguredPhysicalPaths(0), - _peers(64), - _paths(128), - _roots(8), - _rootIdentities(8), - _lastUpdatedBestRoot(0) {} - ZT_ALWAYS_INLINE ~Topology() {} + Topology(const RuntimeEnvironment *renv,const Identity &myId); + ~Topology(); /** * Add a peer to database @@ -95,21 +54,16 @@ public: * This will not replace existing peers. In that case the existing peer * record is returned. * - * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param peer Peer to add * @return New or existing peer (should replace 'peer') */ ZT_ALWAYS_INLINE SharedPtr add(const SharedPtr &peer) { - SharedPtr np; - { - Mutex::Lock _l(_peers_l); - SharedPtr &hp = _peers[peer->address()]; - if (!hp) - hp = peer; - np = hp; - } - return np; + RWMutex::Lock _l(_peers_l); + SharedPtr &hp = _peers[peer->address()]; + if (!hp) + hp = peer; + return hp; } /** @@ -119,15 +73,11 @@ public: * @param zta ZeroTier address of peer * @return Peer or NULL if not found */ - ZT_ALWAYS_INLINE SharedPtr get(const Address &zta) + ZT_ALWAYS_INLINE SharedPtr get(const Address &zta) const { - if (zta == _myIdentity.address()) - return SharedPtr(); - Mutex::Lock l1(_peers_l); + RWMutex::RLock l1(_peers_l); const SharedPtr *const ap = _peers.get(zta); - if (ap) - return *ap; - return SharedPtr(); + return (ap) ? *ap : SharedPtr(); } /** @@ -135,12 +85,12 @@ public: * @param zta ZeroTier address of peer * @return Identity or NULL identity if not found */ - ZT_ALWAYS_INLINE Identity getIdentity(void *tPtr,const Address &zta) + ZT_ALWAYS_INLINE Identity getIdentity(void *tPtr,const Address &zta) const { if (zta == _myIdentity.address()) { return _myIdentity; } else { - Mutex::Lock _l(_peers_l); + RWMutex::RLock _l(_peers_l); const SharedPtr *const ap = _peers.get(zta); if (ap) return (*ap)->identity(); @@ -153,72 +103,55 @@ public: * * @param l Local socket * @param r Remote address - * @return Pointer to canonicalized Path object + * @return Pointer to canonicalized Path object or NULL on error */ ZT_ALWAYS_INLINE SharedPtr getPath(const int64_t l,const InetAddress &r) { - Mutex::Lock _l(_paths_l); - SharedPtr &p = _paths[Path::HashKey(l,r)]; - if (!p) - p.set(new Path(l,r)); + const Path::HashKey k(l,r); + + _paths_l.rlock(); + SharedPtr p(_paths[k]); + _paths_l.runlock(); + if (p) + return p; + + _paths_l.lock(); + SharedPtr &p2 = _paths[k]; + if (p2) { + p = p2; + } else { + try { + p.set(new Path(l,r)); + } catch ( ... ) { + _paths_l.unlock(); + return SharedPtr(); + } + p2 = p; + } + _paths_l.unlock(); + return p; } + /** + * @return Current best root server + */ + ZT_ALWAYS_INLINE SharedPtr root() const + { + RWMutex::RLock l(_peers_l); + if (_rootPeers.empty()) + return SharedPtr(); + return _rootPeers.front(); + } + /** * @param id Identity to check * @return True if this identity corresponds to a root */ ZT_ALWAYS_INLINE bool isRoot(const Identity &id) const { - Mutex::Lock l(_roots_l); - return _rootIdentities.contains(id); - } - - /** - * Do periodic tasks such as database cleanup - */ - ZT_ALWAYS_INLINE void doPeriodicTasks(int64_t now) - { - { - Mutex::Lock _l1(_peers_l); - Hashtable< Address,SharedPtr >::Iterator i(_peers); - Address *a = (Address *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { - if (!(*p)->alive(now)) { - _peers.erase(*a); - } - } - } - { - Mutex::Lock _l(_paths_l); - Hashtable< Path::HashKey,SharedPtr >::Iterator i(_paths); - Path::HashKey *k = (Path::HashKey *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(k,p)) { - if (p->references() <= 1) - _paths.erase(*k); - } - } - } - - /** - * @param now Current time - * @return Number of peers with active direct paths - */ - inline unsigned long countActive(int64_t now) const - { - unsigned long cnt = 0; - Mutex::Lock _l(_peers_l); - Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); - Address *a = (Address *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { - const SharedPtr pp((*p)->getAppropriatePath(now,false)); - if (pp) - ++cnt; - } - return cnt; + RWMutex::RLock l(_peers_l); + return (_roots.count(id) > 0); } /** @@ -231,10 +164,10 @@ public: * @tparam F Function or function object type */ template - ZT_ALWAYS_INLINE void eachPeer(F f) + ZT_ALWAYS_INLINE void eachPeer(F f) const { - Mutex::Lock l(_peers_l); - Hashtable< Address,SharedPtr >::Iterator i(_peers); + RWMutex::RLock l(_peers_l); + Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); Address *a = (Address *)0; SharedPtr *p = (SharedPtr *)0; while (i.next(a,p)) { @@ -244,183 +177,37 @@ public: } /** - * Apply a function or function object to all roots + * Apply a function or function object to all peers * - * This locks the root list during execution but other operations - * are fine. + * This locks the peer map during execution, so calls to get() etc. during + * eachPeer() will deadlock. * - * @param f Function to apply f(peer,IPs) - * @tparam F function or function object type + * @param f Function to apply + * @tparam F Function or function object type */ template - ZT_ALWAYS_INLINE void eachRoot(F f) + ZT_ALWAYS_INLINE void eachPeerWithRoot(F f) const { - Mutex::Lock l(_roots_l); - Hashtable< Str,Locator >::Iterator i(_roots); - Str *k = (Str *)0; - Locator *v = (Locator *)0; - while (i.next(k,v)) { - if (*v) { - for(std::vector::const_iterator id(v->virt().begin());id!=v->virt().end();++id) { - const SharedPtr *ap; - { - Mutex::Lock l2(_peers_l); - ap = _peers.get(id->address()); - } - if (ap) { - if (!f(*ap,v->phy())) - return; - } else { - SharedPtr p(new Peer(RR,_myIdentity,*id)); - { - Mutex::Lock l2(_peers_l); - _peers.set(id->address(),p); - } - if (!f(p,v->phy())) - return; - } - } - } - } - } + RWMutex::RLock l(_peers_l); - /** - * @return Current best root (updated automatically each second) - */ - inline SharedPtr root(const int64_t now) - { - Mutex::Lock l(_bestRoot_l); - if ((!_bestRoot)||((now - _lastUpdatedBestRoot) > 1000)) { - _lastUpdatedBestRoot = now; - _RootRankingFunction rrf; - rrf.now = now; - eachRoot(rrf); - _bestRoot = rrf.bestRoot; - } - return _bestRoot; - } + std::vector rootPeerPtrs; + for(std::vector< SharedPtr >::const_iterator i(_rootPeers.begin());i!=_rootPeers.end();++i) + rootPeerPtrs.push_back((uintptr_t)i->ptr()); + std::sort(rootPeerPtrs.begin(),rootPeerPtrs.end()); - /** - * Iterate through all root names - * - * @param f Function of (Str,Locator) - */ - template - ZT_ALWAYS_INLINE void eachRootName(F f) const - { - Mutex::Lock l(_roots_l); - Str *k = (Str *)0; - Locator *v = (Locator *)0; - Hashtable< Str,Locator >::Iterator i(const_cast(this)->_roots); - while (i.next(k,v)) { - if (!f(*k,*v)) + Hashtable< Address,SharedPtr >::Iterator i(const_cast(this)->_peers); + Address *a = (Address *)0; + SharedPtr *p = (SharedPtr *)0; + while (i.next(a,p)) { + if (!f(*((const SharedPtr *)p),std::binary_search(rootPeerPtrs.begin(),rootPeerPtrs.end(),(uintptr_t)p->ptr()))) break; } } - /** - * Set or update dynamic root if new locator is newer - * - * This does not check signatures or internal validity of the locator. - * - * @param name DNS name used to retrive root or simply the address for static roots - * @param latestLocator Latest locator - * @return True if locator is newer or if a new entry was created - */ - inline bool setRoot(const Str &name,const Locator &latestLocator) - { - Mutex::Lock l(_roots_l); - if (latestLocator) { - Locator &ll = _roots[name]; - if (ll.timestamp() < latestLocator.timestamp()) { - ll = latestLocator; - _updateRoots(); - _rootsModified = true; - return true; - } - } else if (!_roots.contains(name)) { - _roots.set(name,Locator()); // no locator known yet, but add name to name list to trigger DNS refreshing - _rootsModified = true; - return true; - } - return false; - } - - /** - * Remove a dynamic root entry - */ - inline void removeRoot(const Str &name) - { - Mutex::Lock l(_roots_l); - if (_roots.erase(name)) { - _updateRoots(); - _rootsModified = true; - } - } - - /** - * @param Current time - * @return ZT_RootList as returned by the external CAPI - */ - inline ZT_RootList *apiRoots(const int64_t now) const - { - ScopedPtr< Buffer<65536> > lbuf(new Buffer<65536>()); - Mutex::Lock l2(_roots_l); - ZT_RootList *rl = (ZT_RootList *)malloc(sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size()) + (256 * _roots.size()) + (65536 * _roots.size())); - if (!rl) - return nullptr; - char *nptr = ((char *)rl) + sizeof(ZT_RootList) + (sizeof(ZT_Root) * _roots.size()); - uint8_t *lptr = ((uint8_t *)nptr) + (256 * _roots.size()); - - unsigned int c = 0; - Str *k = (Str *)0; - Locator *v = (Locator *)0; - Hashtable< Str,Locator >::Iterator i(const_cast(this)->_roots); - while (i.next(k,v)) { - Utils::scopy(nptr,256,k->c_str()); - rl->roots[c].name = nptr; - nptr += 256; - lbuf->clear(); - v->serialize(*lbuf); - memcpy(lptr,lbuf->unsafeData(),lbuf->size()); - rl->roots[c].locator = lptr; - rl->roots[c].locatorSize = lbuf->size(); - lptr += 65536; - ++c; - } - - rl->count = c; - return rl; - } - - /** - * Get the best relay to a given address, which may or may not be a root - * - * @param now Current time - * @param toAddr Destination address - * @return Best current relay or NULL if none - */ - ZT_ALWAYS_INLINE SharedPtr findRelayTo(const int64_t now,const Address &toAddr) - { - // TODO: in the future this will check 'mesh-like' relays and if enabled consult LF for other roots (for if this is a root) - return root(now); - } - /** * @param allPeers vector to fill with all current peers */ - ZT_ALWAYS_INLINE void getAllPeers(std::vector< SharedPtr > &allPeers) const - { - Mutex::Lock l(_peers_l); - allPeers.clear(); - allPeers.reserve(_peers.size()); - Hashtable< Address,SharedPtr >::Iterator i(*(const_cast > *>(&_peers))); - Address *a = (Address *)0; - SharedPtr *p = (SharedPtr *)0; - while (i.next(a,p)) { - allPeers.push_back(*p); - } - } + void getAllPeers(std::vector< SharedPtr > &allPeers) const; /** * Get info about a path @@ -442,21 +229,6 @@ public: } } - /** - * Get the payload MTU for an outbound physical path (returns default if not configured) - * - * @param physicalAddress Physical endpoint address - * @return MTU - */ - ZT_ALWAYS_INLINE unsigned int getOutboundPathMtu(const InetAddress &physicalAddress) - { - for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i cpaths; - for(unsigned int i=0,j=_numConfiguredPhysicalPaths;i ZT_MAX_PHYSMTU) - pc.mtu = ZT_MAX_PHYSMTU; + /** + * Remove a root server's identity from the root server set + * + * @param id Root server identity + * @return True if root found and removed, false if not found + */ + bool removeRoot(const Identity &id); - cpaths[*(reinterpret_cast(pathNetwork))] = pc; - } else { - cpaths.erase(*(reinterpret_cast(pathNetwork))); - } + /** + * Sort roots in asecnding order of apparent latency + * + * @param now Current time + */ + void rankRoots(const int64_t now); - unsigned int cnt = 0; - for(std::map::const_iterator i(cpaths.begin());((i!=cpaths.end())&&(cntfirst; - _physicalPathConfig[cnt].second = i->second; - ++cnt; - } - _numConfiguredPhysicalPaths = cnt; - } - } + /** + * Do periodic tasks such as database cleanup + */ + void doPeriodicTasks(const int64_t now); private: const RuntimeEnvironment *const RR; const Identity _myIdentity; - std::pair _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; + RWMutex _peers_l; + RWMutex _paths_l; + + std::pair< InetAddress,ZT_PhysicalPathConfiguration > _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; unsigned int _numConfiguredPhysicalPaths; Hashtable< Address,SharedPtr > _peers; Hashtable< Path::HashKey,SharedPtr > _paths; - - Hashtable< Str,SharedPtr > _roots; - Hashtable< Identity,bool > _rootIdentities; - bool _rootsModified; - - int64_t _lastUpdatedBestRoot; - SharedPtr _bestRoot; - - Mutex _peers_l; - Mutex _paths_l; - Mutex _roots_l; - Mutex _bestRoot_l; + std::set< Identity > _roots; // locked by _peers_l + std::vector< SharedPtr > _rootPeers; // locked by _peers_l }; } // namespace ZeroTier diff --git a/node/Trace.cpp b/node/Trace.cpp deleted file mode 100644 index def920602..000000000 --- a/node/Trace.cpp +++ /dev/null @@ -1,433 +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. - */ -/****/ - -//#define ZT_TRACE - -#include -#include - -#include "Trace.hpp" -#include "RuntimeEnvironment.hpp" -#include "Switch.hpp" -#include "Node.hpp" -#include "Utils.hpp" -#include "Dictionary.hpp" -#include "CertificateOfMembership.hpp" -#include "CertificateOfOwnership.hpp" -#include "Tag.hpp" -#include "Capability.hpp" -#include "Revocation.hpp" -#include "../include/ZeroTierDebug.h" - -namespace ZeroTier { - -#ifdef ZT_TRACE -static void ZT_LOCAL_TRACE(void *const tPtr,const RuntimeEnvironment *const RR,const char *const fmt,...) -{ - char traceMsgBuf[2048]; - va_list ap; - va_start(ap,fmt); - vsnprintf(traceMsgBuf,sizeof(traceMsgBuf),fmt,ap); - va_end(ap); - traceMsgBuf[sizeof(traceMsgBuf) - 1] = (char)0; - RR->node->postEvent(tPtr,ZT_EVENT_TRACE,traceMsgBuf); -} -#else -#define ZT_LOCAL_TRACE(...) -#endif - -void Trace::resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope) -{ -#ifdef ZT_TRACE - char tmp[128]; - ZT_LOCAL_TRACE(tPtr,RR,"RESET and revalidate paths in scope %d; new phy address %s reported by trusted peer %.10llx",(int)scope,myPhysicalAddress.toIpString(tmp),reporter.toInt()); -#endif -} - -void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &path,const uint64_t packetId,const Packet::Verb verb) -{ - char tmp[128]; - if (!path) return; // sanity check - - ZT_LOCAL_TRACE(tPtr,RR,"trying unknown path %s to %.10llx (packet %.16llx verb %d local socket %lld network %.16llx)",path->address().toString(tmp),peer.address().toInt(),packetId,(double)verb,path->localSocket(),networkId); - - std::pair byn; - if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_NORMAL)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb); - if (networkId) - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address()); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - _send(tPtr,d,byn.first); - } -} - -void Trace::peerLinkNowRedundant(void *const tPtr,Peer &peer) -{ - ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is fully redundant",peer.address().toInt()); -} - -void Trace::peerLinkNoLongerRedundant(void *const tPtr,Peer &peer) -{ - ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is no longer redundant",peer.address().toInt()); -} - -void Trace::peerLinkAggregateStatistics(void *const tPtr,Peer &peer) -{ - ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is composed of (%d) physical paths %s, has packet delay variance (%.0f ms), mean latency (%.0f ms)", - peer.address().toInt(), - peer.aggregateLinkPhysicalPathCount(), - peer.interfaceListStr(), - peer.computeAggregateLinkPacketDelayVariance(), - peer.computeAggregateLinkMeanLatency()); -} - -void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &newPath,const uint64_t packetId) -{ - char tmp[128]; - if (!newPath) return; // sanity check - - ZT_LOCAL_TRACE(tPtr,RR,"learned new path %s to %.10llx (packet %.16llx local socket %lld network %.16llx)",newPath->address().toString(tmp),peer.address().toInt(),packetId,newPath->localSocket(),networkId); - - std::pair byn; - if (networkId) { Mutex::Lock l(_byNet_m); _byNet.get(networkId,byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_NORMAL)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - if (networkId) - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,networkId); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,peer.address()); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,newPath->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,newPath->localSocket()); - _send(tPtr,d,byn.first); - } -} - -void Trace::outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason) -{ -#ifdef ZT_TRACE - char tmp[128],tmp2[128]; -#endif - if (!network) return; // sanity check - - ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROP frame %s -> %s etherType %.4x size %u (%s)",network->id(),sourceMac.toString(tmp),destMac.toString(tmp2),etherType,frameLen,(reason) ? reason : "unknown reason"); - - std::pair byn; - { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id()); - d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt()); - d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt()); - d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType); - d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId); - d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::incomingNetworkAccessDenied(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested) -{ - char tmp[128]; - if (!network) return; // sanity check - - ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DENIED packet from %.10llx(%s) verb %d size %u%s",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength,credentialsRequested ? " (credentials requested)" : " (credentials not requested)"); - - std::pair byn; - { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_NORMAL)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id()); - _send(tPtr,d,byn.first); - } -} - -void Trace::incomingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason) -{ - char tmp[128]; - if (!network) return; // sanity check - - ZT_LOCAL_TRACE(tPtr,RR,"%.16llx DROPPED frame from %.10llx(%s) verb %d size %u",network->id(),source.toInt(),(path) ? (path->address().toString(tmp)) : "???",(int)verb,packetLength); - - std::pair byn; - { Mutex::Lock l(_byNet_m); _byNet.get(network->id(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network->id()); - d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,sourceMac.toInt()); - d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,destMac.toInt()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason) -{ - char tmp[128]; - - ZT_LOCAL_TRACE(tPtr,RR,"MAC failed for packet %.16llx from %.10llx(%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???"); - - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _spamToAllNetworks(tPtr,d,Trace::LEVEL_DEBUG); -} - -void Trace::incomingPacketInvalid(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason) -{ - char tmp[128]; - - ZT_LOCAL_TRACE(tPtr,RR,"INVALID packet %.16llx from %.10llx(%s) (%s)",packetId,source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "unknown reason"); - - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_VERB,(uint64_t)verb); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_HOPS,(uint64_t)hops); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _spamToAllNetworks(tPtr,d,Trace::LEVEL_DEBUG); -} - -void Trace::incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const char *reason) -{ - char tmp[128]; - - ZT_LOCAL_TRACE(tPtr,RR,"DROPPED HELLO from %.10llx(%s) (%s)",source.toInt(),(path) ? path->address().toString(tmp) : "???",(reason) ? reason : "???"); - - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S); - d.add(ZT_REMOTE_TRACE_FIELD__PACKET_ID,packetId); - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR,source); - if (path) { - d.add(ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR,path->address().toString(tmp)); - d.add(ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET,path->localSocket()); - } - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _spamToAllNetworks(tPtr,d,Trace::LEVEL_DEBUG); -} - -void Trace::networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller) -{ - ZT_LOCAL_TRACE(tPtr,RR,"requesting configuration for network %.16llx",network.id()); -} - -void Trace::networkFilter( - void *const tPtr, - const Network &network, - const RuleResultLog &primaryRuleSetLog, - const RuleResultLog *const matchingCapabilityRuleSetLog, - const Capability *const matchingCapability, - const Address &ztSource, - const Address &ztDest, - const MAC &macSource, - const MAC &macDest, - const uint8_t *const frameData, - const unsigned int frameLen, - const unsigned int etherType, - const unsigned int vlanId, - const bool noTee, - const bool inbound, - const int accept) -{ - std::pair byn; - { Mutex::Lock l(_byNet_m); _byNet.get(network.id(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_RULES)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,network.id()); - d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR,ztSource); - d.add(ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR,ztDest); - d.add(ZT_REMOTE_TRACE_FIELD__SOURCE_MAC,macSource.toInt()); - d.add(ZT_REMOTE_TRACE_FIELD__DEST_MAC,macDest.toInt()); - d.add(ZT_REMOTE_TRACE_FIELD__ETHERTYPE,(uint64_t)etherType); - d.add(ZT_REMOTE_TRACE_FIELD__VLAN_ID,(uint64_t)vlanId); - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE,noTee ? "1" : "0"); - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND,inbound ? "1" : "0"); - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_RESULT,(int64_t)accept); - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG,(const char *)primaryRuleSetLog.data(),(int)primaryRuleSetLog.sizeBytes()); - if (matchingCapabilityRuleSetLog) - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG,(const char *)matchingCapabilityRuleSetLog->data(),(int)matchingCapabilityRuleSetLog->sizeBytes()); - if (matchingCapability) - d.add(ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID,(uint64_t)matchingCapability->id()); - d.add(ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH,(uint64_t)frameLen); - if (frameLen > 0) - d.add(ZT_REMOTE_TRACE_FIELD__FRAME_DATA,(const char *)frameData,(frameLen > 256) ? (int)256 : (int)frameLen); - _send(tPtr,d,byn.first); - } -} - -void Trace::credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason) -{ - std::pair byn; - if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason) -{ - std::pair byn; - if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::credentialRejected(void *const tPtr,const Capability &c,const char *reason) -{ - std::pair byn; - if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::credentialRejected(void *const tPtr,const Tag &c,const char *reason) -{ - std::pair byn; - if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP,c.timestamp()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO,c.issuedTo()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO,(uint64_t)c.value()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::credentialRejected(void *const tPtr,const Revocation &c,const char *reason) -{ - std::pair byn; - if (c.networkId()) { Mutex::Lock l(_byNet_m); _byNet.get(c.networkId(),byn); } - - if ((byn.first)&&((int)byn.second >= (int)Trace::LEVEL_VERBOSE)) { - Dictionary d; - d.add(ZT_REMOTE_TRACE_FIELD__EVENT,ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S); - d.add(ZT_REMOTE_TRACE_FIELD__NETWORK_ID,c.networkId()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE,(uint64_t)c.credentialType()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID,(uint64_t)c.id()); - d.add(ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET,c.target()); - if (reason) - d.add(ZT_REMOTE_TRACE_FIELD__REASON,reason); - _send(tPtr,d,byn.first); - } -} - -void Trace::_send(void *const tPtr,const Dictionary &d,const Address &dest) -{ - Packet outp(dest,RR->identity.address(),Packet::VERB_REMOTE_TRACE); - outp.appendCString(d.data()); - outp.compress(); - RR->sw->send(tPtr,outp,true); -} - -void Trace::_spamToAllNetworks(void *const tPtr,const Dictionary &d,const Level level) -{ - Mutex::Lock l(_byNet_m); - Hashtable< uint64_t,std::pair< Address,Trace::Level > >::Iterator i(_byNet); - uint64_t *k = (uint64_t *)0; - std::pair *v = (std::pair *)0; - while (i.next(k,v)) { - if ((v)&&(v->first)&&((int)v->second >= (int)level)) - _send(tPtr,d,v->first); - } -} - -} // namespace ZeroTier diff --git a/node/Trace.hpp b/node/Trace.hpp index a67b2e91e..763d64537 100644 --- a/node/Trace.hpp +++ b/node/Trace.hpp @@ -14,10 +14,10 @@ #ifndef ZT_TRACE_HPP #define ZT_TRACE_HPP -#include -#include -#include -#include +#include +#include +#include +#include #include "../include/ZeroTierOne.h" @@ -52,44 +52,14 @@ struct NetworkConfig; class Trace { public: - /** - * Trace verbosity level - */ - enum Level - { - LEVEL_NORMAL = 0, - LEVEL_VERBOSE = 10, - LEVEL_RULES = 15, - LEVEL_DEBUG = 20 - }; - - /** - * Filter rule evaluation result log - * - * Each rule in a rule set gets a four-bit log entry. A log entry - * of zero means not evaluated. Otherwise each four-bit log entry - * contains two two-bit values of 01 for 'false' and 10 for 'true'. - * As with four-bit rules an 00 value here means this was not - * evaluated or was not relevant. - */ class RuleResultLog { public: - ZT_ALWAYS_INLINE RuleResultLog() {} + ZT_ALWAYS_INLINE RuleResultLog() { this->clear(); } - ZT_ALWAYS_INLINE void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches) - { - _l[rn >> 1] |= ( ((thisRuleMatches + 1) << 2) | (thisSetMatches + 1) ) << ((rn & 1) << 2); - } - ZT_ALWAYS_INLINE void logSkipped(const unsigned int rn,const uint8_t thisSetMatches) - { - _l[rn >> 1] |= (thisSetMatches + 1) << ((rn & 1) << 2); - } - - ZT_ALWAYS_INLINE void clear() - { - memset(_l,0,sizeof(_l)); - } + ZT_ALWAYS_INLINE void log(const unsigned int rn,const uint8_t thisRuleMatches,const uint8_t thisSetMatches) { _l[rn >> 1U] |= ( ((thisRuleMatches + 1U) << 2U) | (thisSetMatches + 1U) ) << ((rn & 1U) << 2U); } + ZT_ALWAYS_INLINE void logSkipped(const unsigned int rn,const uint8_t thisSetMatches) { _l[rn >> 1U] |= (thisSetMatches + 1U) << ((rn & 1U) << 2U); } + ZT_ALWAYS_INLINE void clear() { memset(_l,0,sizeof(_l)); } ZT_ALWAYS_INLINE const uint8_t *data() const { return _l; } ZT_ALWAYS_INLINE unsigned int sizeBytes() const { return (ZT_MAX_NETWORK_RULES / 2); } @@ -98,24 +68,63 @@ public: uint8_t _l[ZT_MAX_NETWORK_RULES / 2]; }; - ZT_ALWAYS_INLINE Trace(const RuntimeEnvironment *renv) : - RR(renv), - _byNet(8) {} + inline Trace(const RuntimeEnvironment *renv) + { + } - void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope); - void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &path,const uint64_t packetId,const Packet::Verb verb); - void peerLinkNowRedundant(void *const tPtr,Peer &peer); - void peerLinkNoLongerRedundant(void *const tPtr,Peer &peer); - void peerLinkAggregateStatistics(void *const tPtr,Peer &peer); - void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &newPath,const uint64_t packetId); - void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason); - void incomingPacketInvalid(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason); - void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const char *reason); - void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason); - void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested); - void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason); - void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller); - void networkFilter( + inline void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope) + { + } + + inline void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &path,const uint64_t packetId,const Packet::Verb verb) + { + } + + inline void peerLinkNowRedundant(void *const tPtr,Peer &peer) + { + } + + inline void peerLinkNoLongerRedundant(void *const tPtr,Peer &peer) + { + } + + inline void peerLinkAggregateStatistics(void *const tPtr,Peer &peer) + { + } + + inline void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr &newPath,const uint64_t packetId) + { + } + + inline void incomingPacketMessageAuthenticationFailure(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const char *reason) + { + } + + inline void incomingPacketInvalid(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const unsigned int hops,const Packet::Verb verb,const char *reason) + { + } + + inline void incomingPacketDroppedHELLO(void *const tPtr,const SharedPtr &path,const uint64_t packetId,const Address &source,const char *reason) + { + } + + inline void outgoingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const MAC &sourceMac,const MAC &destMac,const unsigned int etherType,const unsigned int vlanId,const unsigned int frameLen,const char *reason) + { + } + + inline void incomingNetworkAccessDenied(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,bool credentialsRequested) + { + } + + inline void incomingNetworkFrameDropped(void *const tPtr,const SharedPtr &network,const SharedPtr &path,const uint64_t packetId,const unsigned int packetLength,const Address &source,const Packet::Verb verb,const MAC &sourceMac,const MAC &destMac,const char *reason) + { + } + + inline void networkConfigRequestSent(void *const tPtr,const Network &network,const Address &controller) + { + } + + inline void networkFilter( void *const tPtr, const Network &network, const RuleResultLog &primaryRuleSetLog, @@ -131,21 +140,29 @@ public: const unsigned int vlanId, const bool noTee, const bool inbound, - const int accept); - void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason); - void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason); - void credentialRejected(void *const tPtr,const Capability &c,const char *reason); - void credentialRejected(void *const tPtr,const Tag &c,const char *reason); - void credentialRejected(void *const tPtr,const Revocation &c,const char *reason); + const int accept) + { + } -private: - const RuntimeEnvironment *const RR; + inline void credentialRejected(void *const tPtr,const CertificateOfMembership &c,const char *reason) + { + } - void _send(void *const tPtr,const Dictionary &d,const Address &dest); - void _spamToAllNetworks(void *const tPtr,const Dictionary &d,const Level level); + inline void credentialRejected(void *const tPtr,const CertificateOfOwnership &c,const char *reason) + { + } - Hashtable< uint64_t,std::pair< Address,Trace::Level > > _byNet; - Mutex _byNet_m; + inline void credentialRejected(void *const tPtr,const Capability &c,const char *reason) + { + } + + inline void credentialRejected(void *const tPtr,const Tag &c,const char *reason) + { + } + + inline void credentialRejected(void *const tPtr,const Revocation &c,const char *reason) + { + } }; } // namespace ZeroTier diff --git a/node/Utils.cpp b/node/Utils.cpp index 2de30002f..77790767a 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -11,23 +11,16 @@ */ /****/ -#include -#include -#include -#include -#include -#include +#include +#include +#include #include "Constants.hpp" #ifdef __UNIX_LIKE__ #include -#include #include -#include -#include #include -#include #endif #ifdef __WINDOWS__ @@ -36,34 +29,45 @@ #include "Utils.hpp" #include "Mutex.hpp" -#include "Salsa20.hpp" #include "AES.hpp" #include "SHA512.hpp" namespace ZeroTier { +namespace Utils { + #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) -#include -static bool _zt_rdrand_supported() +CPUIDRegisters::CPUIDRegisters() { #ifdef __WINDOWS__ int regs[4]; __cpuid(regs,1); - return (((regs[2] >> 30) & 1) != 0); + eax = (uint32_t)regs[0]; + ebx = (uint32_t)regs[1]; + ecx = (uint32_t)regs[2]; + edx = (uint32_t)regs[3]; #else - uint32_t eax,ebx,ecx,edx; __asm__ __volatile__ ( "cpuid" : "=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx) : "a"(1),"c"(0) ); - return ((ecx & (1 << 30)) != 0); #endif + rdrand = ((ecx & (1U << 30U)) != 0); + aes = ( ((ecx & (1U << 25U)) != 0) && ((ecx & (1U << 19U)) != 0) && ((ecx & (1U << 1U)) != 0) ); // AES, PCLMUL, SSE4.1 } -static const bool _rdrandSupported = _zt_rdrand_supported(); +CPUIDRegisters CPUID; #endif -const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; +const char HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + +bool secureEq(const void *a,const void *b,unsigned int len) +{ + uint8_t diff = 0; + for(unsigned int i=0;i(a))[i] ^ (reinterpret_cast(b))[i] ); + return (diff == 0); +} // Crazy hack to force memory to be securely zeroed in spite of the best efforts of optimizing compilers. static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len) @@ -72,7 +76,7 @@ static void _Utils_doBurn(volatile uint8_t *ptr,unsigned int len) while (ptr != end) *(ptr++) = (uint8_t)0; } static void (*volatile _Utils_doBurn_ptr)(volatile uint8_t *,unsigned int) = _Utils_doBurn; -void Utils::burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); } +void burn(void *ptr,unsigned int len) { (_Utils_doBurn_ptr)((volatile uint8_t *)ptr,len); } static unsigned long _Utils_itoa(unsigned long n,char *s) { @@ -84,7 +88,7 @@ static unsigned long _Utils_itoa(unsigned long n,char *s) s[pos] = '0' + (char)(n % 10); return pos + 1; } -char *Utils::decimal(unsigned long n,char s[24]) +char *decimal(unsigned long n,char s[24]) { if (n == 0) { s[0] = '0'; @@ -95,81 +99,35 @@ char *Utils::decimal(unsigned long n,char s[24]) return s; } -unsigned short Utils::crc16(const void *buf,unsigned int len) +char *hex10(uint64_t i,char s[11]) { - static const uint16_t crc16tab[256]= { - 0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7, - 0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef, - 0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6, - 0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de, - 0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485, - 0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d, - 0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4, - 0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc, - 0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823, - 0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b, - 0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12, - 0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a, - 0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41, - 0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49, - 0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70, - 0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78, - 0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f, - 0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067, - 0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e, - 0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256, - 0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d, - 0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405, - 0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c, - 0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634, - 0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab, - 0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3, - 0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a, - 0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92, - 0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9, - 0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1, - 0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8, - 0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0 - }; - uint16_t crc = 0; - const uint8_t *p = (const uint8_t *)buf; - for(unsigned int i=0;i> 8) ^ *(p++)) & 0x00ff]; - return crc; + s[0] = HEXCHARS[(i >> 36) & 0xf]; + s[1] = HEXCHARS[(i >> 32) & 0xf]; + s[2] = HEXCHARS[(i >> 28) & 0xf]; + s[3] = HEXCHARS[(i >> 24) & 0xf]; + s[4] = HEXCHARS[(i >> 20) & 0xf]; + s[5] = HEXCHARS[(i >> 16) & 0xf]; + s[6] = HEXCHARS[(i >> 12) & 0xf]; + s[7] = HEXCHARS[(i >> 8) & 0xf]; + s[8] = HEXCHARS[(i >> 4) & 0xf]; + s[9] = HEXCHARS[i & 0xf]; + s[10] = (char)0; + return s; } -unsigned int Utils::unhex(const char *h,void *buf,unsigned int buflen) +char *hex(const void *d,unsigned int l,char *s) { - unsigned int l = 0; - while (l < buflen) { - uint8_t hc = *(reinterpret_cast(h++)); - if (!hc) break; - - uint8_t c = 0; - if ((hc >= 48)&&(hc <= 57)) // 0..9 - c = hc - 48; - else if ((hc >= 97)&&(hc <= 102)) // a..f - c = hc - 87; - else if ((hc >= 65)&&(hc <= 70)) // A..F - c = hc - 55; - - hc = *(reinterpret_cast(h++)); - if (!hc) break; - - c <<= 4; - if ((hc >= 48)&&(hc <= 57)) - c |= hc - 48; - else if ((hc >= 97)&&(hc <= 102)) - c |= hc - 87; - else if ((hc >= 65)&&(hc <= 70)) - c |= hc - 55; - - reinterpret_cast(buf)[l++] = c; + char *const save = s; + for(unsigned int i=0;i(d)[i]; + *(s++) = HEXCHARS[b >> 4]; + *(s++) = HEXCHARS[b & 0xf]; } - return l; + *s = (char)0; + return save; } -unsigned int Utils::unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen) +unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen) { unsigned int l = 0; const char *hend = h + hlen; @@ -203,7 +161,7 @@ unsigned int Utils::unhex(const char *h,unsigned int hlen,void *buf,unsigned int return l; } -void Utils::getSecureRandom(void *buf,unsigned int bytes) +void getSecureRandom(void *buf,unsigned int bytes) { static Mutex globalLock; static bool initialized = false; @@ -257,7 +215,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) randomState[0] ^= (uint64_t)time(nullptr); randomState[1] ^= (uint64_t)((uintptr_t)buf); #if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) - if (_rdrandSupported) { + if (CPUID.rdrand) { uint64_t tmp = 0; _rdrand64_step((unsigned long long *)&tmp); randomState[2] ^= tmp; @@ -281,7 +239,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes) } } -int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize) +int b32e(const uint8_t *data,int length,char *result,int bufSize) { if (length < 0 || length > (1 << 28)) { result[0] = (char)0; @@ -317,7 +275,7 @@ int Utils::b32e(const uint8_t *data,int length,char *result,int bufSize) return -1; } -int Utils::b32d(const char *encoded,uint8_t *result,int bufSize) +int b32d(const char *encoded,uint8_t *result,int bufSize) { int buffer = 0; int bitsLeft = 0; @@ -357,94 +315,22 @@ int Utils::b32d(const char *encoded,uint8_t *result,int bufSize) return count; } -unsigned int Utils::b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen) +static uint64_t _secureRandom64() { - static const char base64en[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' }; - unsigned int i = 0,j = 0; - uint8_t l = 0; - int s = 0; - for (;i= outlen) return 0; - out[j++] = base64en[(c >> 2) & 0x3f]; - break; - case 1: - s = 2; - if (j >= outlen) return 0; - out[j++] = base64en[((l & 0x3) << 4) | ((c >> 4) & 0xf)]; - break; - case 2: - s = 0; - if (j >= outlen) return 0; - out[j++] = base64en[((l & 0xf) << 2) | ((c >> 6) & 0x3)]; - if (j >= outlen) return 0; - out[j++] = base64en[c & 0x3f]; - break; - } - l = c; - } - switch (s) { - case 1: - if (j >= outlen) return 0; - out[j++] = base64en[(l & 0x3) << 4]; - //out[j++] = '='; - //out[j++] = '='; - break; - case 2: - if (j >= outlen) return 0; - out[j++] = base64en[(l & 0xf) << 2]; - //out[j++] = '='; - break; - } - if (j >= outlen) return 0; - out[j] = 0; - return j; -} - -unsigned int Utils::b64d(const char *in,unsigned char *out,unsigned int outlen) -{ - static const uint8_t base64de[256] = { 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,52,53,54,55,56,57,58,59,60,61,255,255,255,255,255,255,255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,255,255,255,255,255 }; - unsigned int i = 0; - unsigned int j = 0; - while ((in[i] != '=')&&(in[i] != 0)) { - if (j >= outlen) - break; - uint8_t c = base64de[(unsigned char)in[i]]; - if (c != 255) { - switch (i & 0x3) { - case 0: - out[j] = (c << 2) & 0xff; - break; - case 1: - out[j++] |= (c >> 4) & 0x3; - out[j] = (c & 0xf) << 4; - break; - case 2: - out[j++] |= (c >> 2) & 0xf; - out[j] = (c & 0x3) << 6; - break; - case 3: - out[j++] |= c; - break; - } - } - ++i; - } - return j; + uint64_t tmp = 0; + getSecureRandom(&tmp,sizeof(tmp)); + return tmp; } #define ROL64(x,k) (((x) << (k)) | ((x) >> (64 - (k)))) -uint64_t Utils::random() +uint64_t random() { // https://en.wikipedia.org/wiki/Xorshift#xoshiro256** static Mutex l; - static uint64_t s0 = Utils::getSecureRandom64(); - static uint64_t s1 = Utils::getSecureRandom64(); - static uint64_t s2 = Utils::getSecureRandom64(); - static uint64_t s3 = Utils::getSecureRandom64(); + static uint64_t s0 = _secureRandom64(); + static uint64_t s1 = _secureRandom64(); + static uint64_t s2 = _secureRandom64(); + static uint64_t s3 = _secureRandom64(); l.lock(); const uint64_t result = ROL64(s1 * 5,7) * 9; @@ -460,4 +346,24 @@ uint64_t Utils::random() return result; } +bool scopy(char *dest,unsigned int len,const char *src) +{ + if (!len) + return false; // sanity check + if (!src) { + *dest = (char)0; + return true; + } + char *const end = dest + len; + while ((*dest++ = *src++)) { + if (dest == end) { + *(--dest) = (char)0; + return false; + } + } + return true; +} + +} // namespace Utils + } // namespace ZeroTier diff --git a/node/Utils.hpp b/node/Utils.hpp index bc4791f03..5afbb789f 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -14,11 +14,17 @@ #ifndef ZT_UTILS_HPP #define ZT_UTILS_HPP -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include + +#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +#include +#include +#include +#endif #include #include @@ -29,355 +35,387 @@ namespace ZeroTier { -/** - * Miscellaneous utility functions and global constants - */ -class Utils +namespace Utils { + +#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__) || defined(_M_X64)) +struct CPUIDRegisters { -public: - /** - * Hexadecimal characters 0-f - */ - static const char HEXCHARS[16]; - - /** - * Perform a time-invariant binary comparison - * - * @param a First binary string - * @param b Second binary string - * @param len Length of strings - * @return True if strings are equal - */ - static ZT_ALWAYS_INLINE bool secureEq(const void *a,const void *b,unsigned int len) - { - uint8_t diff = 0; - for(unsigned int i=0;i(a))[i] ^ (reinterpret_cast(b))[i] ); - return (diff == 0); - } - - /** - * Zero memory, ensuring to avoid any compiler optimizations or other things that may stop this. - */ - static void burn(void *ptr,unsigned int len); - - /** - * @param n Number to convert - * @param s Buffer, at least 24 bytes in size - * @return String containing 'n' in base 10 form - */ - static char *decimal(unsigned long n,char s[24]); - - /** - * Compute CRC16-CCITT - */ - static uint16_t crc16(const void *buf,unsigned int len); - - /** - * Convert an unsigned integer into hex - * - * @param i Any unsigned integer - * @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur. - * @return Pointer to s containing hex string with trailing zero byte - */ - template - static ZT_ALWAYS_INLINE char *hex(I x,char *s) - { - char *const r = s; - for(unsigned int i=0,b=(sizeof(x)*8);i> (b -= 4)) & 0xf]; - *(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf]; - } - *s = (char)0; - return r; - } - - /** - * Convert the least significant 40 bits of a uint64_t to hex - * - * @param i Unsigned 64-bit int - * @param s Buffer of size [11] to receive 10 hex characters - * @return Pointer to buffer - */ - static ZT_ALWAYS_INLINE char *hex10(uint64_t i,char s[11]) - { - s[0] = HEXCHARS[(i >> 36) & 0xf]; - s[1] = HEXCHARS[(i >> 32) & 0xf]; - s[2] = HEXCHARS[(i >> 28) & 0xf]; - s[3] = HEXCHARS[(i >> 24) & 0xf]; - s[4] = HEXCHARS[(i >> 20) & 0xf]; - s[5] = HEXCHARS[(i >> 16) & 0xf]; - s[6] = HEXCHARS[(i >> 12) & 0xf]; - s[7] = HEXCHARS[(i >> 8) & 0xf]; - s[8] = HEXCHARS[(i >> 4) & 0xf]; - s[9] = HEXCHARS[i & 0xf]; - s[10] = (char)0; - return s; - } - - /** - * Convert a byte array into hex - * - * @param d Bytes - * @param l Length of bytes - * @param s String buffer, must be at least (l*2)+1 in size or overflow will occur - * @return Pointer to filled string buffer - */ - static ZT_ALWAYS_INLINE char *hex(const void *d,unsigned int l,char *s) - { - char *const save = s; - for(unsigned int i=0;i(d)[i]; - *(s++) = HEXCHARS[b >> 4]; - *(s++) = HEXCHARS[b & 0xf]; - } - *s = (char)0; - return save; - } - - static unsigned int unhex(const char *h,void *buf,unsigned int buflen); - static unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen); - - /** - * Generate secure random bytes - * - * This will try to use whatever OS sources of entropy are available. It's - * guarded by an internal mutex so it's thread-safe. - * - * @param buf Buffer to fill - * @param bytes Number of random bytes to generate - */ - static void getSecureRandom(void *buf,unsigned int bytes); - - /** - * Get a 64-bit unsigned secure random number - */ - static ZT_ALWAYS_INLINE uint64_t getSecureRandom64() - { - uint64_t x; - getSecureRandom(&x,sizeof(x)); - return x; - } - - static int b32e(const uint8_t *data,int length,char *result,int bufSize); - static int b32d(const char *encoded, uint8_t *result, int bufSize); - - static ZT_ALWAYS_INLINE unsigned int b64MaxEncodedSize(const unsigned int s) { return ((((s + 2) / 3) * 4) + 1); } - static unsigned int b64e(const uint8_t *in,unsigned int inlen,char *out,unsigned int outlen); - static unsigned int b64d(const char *in,uint8_t *out,unsigned int outlen); - - /** - * Get a non-cryptographic random integer - */ - static uint64_t random(); - - static ZT_ALWAYS_INLINE float normalize(float value, int64_t bigMin, int64_t bigMax, int32_t targetMin, int32_t targetMax) - { - int64_t bigSpan = bigMax - bigMin; - int64_t smallSpan = targetMax - targetMin; - float valueScaled = (value - (float)bigMin) / (float)bigSpan; - return (float)targetMin + valueScaled * (float)smallSpan; - } - - /** - * Tokenize a string (alias for strtok_r or strtok_s depending on platform) - * - * @param str String to split - * @param delim Delimiters - * @param saveptr Pointer to a char * for temporary reentrant storage - */ - static ZT_ALWAYS_INLINE char *stok(char *str,const char *delim,char **saveptr) - { -#ifdef __WINDOWS__ - return strtok_s(str,delim,saveptr); -#else - return strtok_r(str,delim,saveptr); + uint32_t eax,ebx,ecx,edx; + bool rdrand; + bool aes; + CPUIDRegisters(); +}; +extern CPUIDRegisters CPUID; #endif - } - static ZT_ALWAYS_INLINE unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); } - static ZT_ALWAYS_INLINE int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); } - static ZT_ALWAYS_INLINE unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); } - static ZT_ALWAYS_INLINE long strToLong(const char *s) { return strtol(s,(char **)0,10); } - static ZT_ALWAYS_INLINE unsigned long long strToU64(const char *s) - { -#ifdef __WINDOWS__ - return (unsigned long long)_strtoui64(s,(char **)0,10); -#else - return strtoull(s,(char **)0,10); -#endif - } - static ZT_ALWAYS_INLINE long long strTo64(const char *s) - { -#ifdef __WINDOWS__ - return (long long)_strtoi64(s,(char **)0,10); -#else - return strtoll(s,(char **)0,10); -#endif - } - static ZT_ALWAYS_INLINE unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); } - static ZT_ALWAYS_INLINE int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); } - static ZT_ALWAYS_INLINE unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); } - static ZT_ALWAYS_INLINE long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); } - static ZT_ALWAYS_INLINE unsigned long long hexStrToU64(const char *s) - { -#ifdef __WINDOWS__ - return (unsigned long long)_strtoui64(s,(char **)0,16); -#else - return strtoull(s,(char **)0,16); -#endif - } - static ZT_ALWAYS_INLINE long long hexStrTo64(const char *s) - { -#ifdef __WINDOWS__ - return (long long)_strtoi64(s,(char **)0,16); -#else - return strtoll(s,(char **)0,16); -#endif - } +/** + * Hexadecimal characters 0-f + */ +extern const char HEXCHARS[16]; - /** - * Perform a safe C string copy, ALWAYS null-terminating the result - * - * This will never ever EVER result in dest[] not being null-terminated - * regardless of any input parameter (other than len==0 which is invalid). - * - * @param dest Destination buffer (must not be NULL) - * @param len Length of dest[] (if zero, false is returned and nothing happens) - * @param src Source string (if NULL, dest will receive a zero-length string and true is returned) - * @return True on success, false on overflow (buffer will still be 0-terminated) - */ - static ZT_ALWAYS_INLINE bool scopy(char *dest,unsigned int len,const char *src) - { - if (!len) - return false; // sanity check - if (!src) { - *dest = (char)0; - return true; - } - char *const end = dest + len; - while ((*dest++ = *src++)) { - if (dest == end) { - *(--dest) = (char)0; - return false; - } - } - return true; +/** + * Perform a time-invariant binary comparison + * + * @param a First binary string + * @param b Second binary string + * @param len Length of strings + * @return True if strings are equal + */ +bool secureEq(const void *a,const void *b,unsigned int len); + +/** + * Zero memory, ensuring to avoid any compiler optimizations or other things that may stop this. + */ +void burn(void *ptr,unsigned int len); + +/** + * @param n Number to convert + * @param s Buffer, at least 24 bytes in size + * @return String containing 'n' in base 10 form + */ +char *decimal(unsigned long n,char s[24]); + +/** + * Convert an unsigned integer into hex + * + * @param i Any unsigned integer + * @param s Buffer to receive hex, must be at least (2*sizeof(i))+1 in size or overflow will occur. + * @return Pointer to s containing hex string with trailing zero byte + */ +template +static inline char *hex(I x,char *s) +{ + char *const r = s; + for(unsigned int i=0,b=(sizeof(x)*8);i> (b -= 4)) & 0xf]; + *(s++) = HEXCHARS[(x >> (b -= 4)) & 0xf]; } + *s = (char)0; + return r; +} + +/** + * Convert the least significant 40 bits of a uint64_t to hex + * + * @param i Unsigned 64-bit int + * @param s Buffer of size [11] to receive 10 hex characters + * @return Pointer to buffer + */ +char *hex10(uint64_t i,char s[11]); + +/** + * Convert a byte array into hex + * + * @param d Bytes + * @param l Length of bytes + * @param s String buffer, must be at least (l*2)+1 in size or overflow will occur + * @return Pointer to filled string buffer + */ +char *hex(const void *d,unsigned int l,char *s); + +/** + * Decode a hex string + * + * @param h Hex C-string (non hex chars are ignored) + * @param hlen Maximum length of string (will stop at terminating zero) + * @param buf Output buffer + * @param buflen Length of output buffer + * @return Number of written bytes + */ +unsigned int unhex(const char *h,unsigned int hlen,void *buf,unsigned int buflen); + +/** + * Generate secure random bytes + * + * This will try to use whatever OS sources of entropy are available. It's + * guarded by an internal mutex so it's thread-safe. + * + * @param buf Buffer to fill + * @param bytes Number of random bytes to generate + */ +void getSecureRandom(void *buf,unsigned int bytes); + +/** + * Encode string to base32 + * + * @param data Binary data to encode + * @param length Length of data in bytes + * @param result Result buffer + * @param bufSize Size of result buffer + * @return Number of bytes written + */ +int b32e(const uint8_t *data,int length,char *result,int bufSize); + +/** + * Decode base32 string + * + * @param encoded C-string in base32 format (non-base32 characters are ignored) + * @param result Result buffer + * @param bufSize Size of result buffer + * @return Number of bytes written or -1 on error + */ +int b32d(const char *encoded, uint8_t *result, int bufSize); + +/** + * Get a non-cryptographic random integer + */ +uint64_t random(); + +/** + * Perform a safe C string copy, ALWAYS null-terminating the result + * + * This will never ever EVER result in dest[] not being null-terminated + * regardless of any input parameter (other than len==0 which is invalid). + * + * @param dest Destination buffer (must not be NULL) + * @param len Length of dest[] (if zero, false is returned and nothing happens) + * @param src Source string (if NULL, dest will receive a zero-length string and true is returned) + * @return True on success, false on overflow (buffer will still be 0-terminated) + */ +bool scopy(char *dest,unsigned int len,const char *src); + +static ZT_ALWAYS_INLINE char *stok(char *str,const char *delim,char **saveptr) +{ +#ifdef __WINDOWS__ + return strtok_s(str,delim,saveptr); +#else + return strtok_r(str,delim,saveptr); +#endif +} + +#if 0 +static ZT_ALWAYS_INLINE int strToInt(const char *s) { return (int)strtol(s,(char **)0,10); } +static ZT_ALWAYS_INLINE unsigned long strToULong(const char *s) { return strtoul(s,(char **)0,10); } +static ZT_ALWAYS_INLINE long strToLong(const char *s) { return strtol(s,(char **)0,10); } +static ZT_ALWAYS_INLINE long long strTo64(const char *s) +{ +#ifdef __WINDOWS__ + return (long long)_strtoi64(s,(char **)0,10); +#else + return strtoll(s,(char **)0,10); +#endif +} +static ZT_ALWAYS_INLINE unsigned int hexStrToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,16); } +static ZT_ALWAYS_INLINE int hexStrToInt(const char *s) { return (int)strtol(s,(char **)0,16); } +static ZT_ALWAYS_INLINE unsigned long hexStrToULong(const char *s) { return strtoul(s,(char **)0,16); } +static ZT_ALWAYS_INLINE long hexStrToLong(const char *s) { return strtol(s,(char **)0,16); } +#endif + +static ZT_ALWAYS_INLINE unsigned int strToUInt(const char *s) { return (unsigned int)strtoul(s,(char **)0,10); } + +static ZT_ALWAYS_INLINE unsigned long long strToU64(const char *s) +{ +#ifdef __WINDOWS__ + return (unsigned long long)_strtoui64(s,(char **)0,10); +#else + return strtoull(s,(char **)0,10); +#endif +} + +static ZT_ALWAYS_INLINE long long hexStrTo64(const char *s) +{ +#ifdef __WINDOWS__ + return (long long)_strtoi64(s,(char **)0,16); +#else + return strtoll(s,(char **)0,16); +#endif +} + +static ZT_ALWAYS_INLINE unsigned long long hexStrToU64(const char *s) +{ +#ifdef __WINDOWS__ + return (unsigned long long)_strtoui64(s,(char **)0,16); +#else + return strtoull(s,(char **)0,16); +#endif +} + +/** + * Calculate a non-cryptographic hash of a byte string + * + * @param key Key to hash + * @param len Length in bytes + * @return Non-cryptographic hash suitable for use in a hash table + */ +static ZT_ALWAYS_INLINE unsigned long hashString(const void *restrict key,const unsigned int len) +{ + const uint8_t *p = reinterpret_cast(key); + unsigned long h = 0; + for (unsigned int i=0;i> 6U); + } + h += (h << 3U); + h ^= (h >> 11U); + h += (h << 15U); + return h; +} #ifdef __GNUC__ - static ZT_ALWAYS_INLINE unsigned int countBits(const uint8_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } - static ZT_ALWAYS_INLINE unsigned int countBits(const uint16_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } - static ZT_ALWAYS_INLINE unsigned int countBits(const uint32_t v) { return (unsigned int)__builtin_popcountl((unsigned long)v); } - static ZT_ALWAYS_INLINE unsigned int countBits(const uint64_t v) { return (unsigned int)__builtin_popcountll((unsigned long long)v); } +static ZT_ALWAYS_INLINE unsigned int countBits(const uint8_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } +static ZT_ALWAYS_INLINE unsigned int countBits(const uint16_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } +static ZT_ALWAYS_INLINE unsigned int countBits(const uint32_t v) { return (unsigned int)__builtin_popcountl((unsigned long)v); } +static ZT_ALWAYS_INLINE unsigned int countBits(const uint64_t v) { return (unsigned int)__builtin_popcountll((unsigned long long)v); } #else - /** - * Count the number of bits set in an integer - * - * @param v Unsigned integer - * @return Number of bits set in this integer (0-bits in integer) - */ - template - static ZT_ALWAYS_INLINE unsigned int countBits(T v) - { - v = v - ((v >> 1) & (T)~(T)0/3); - v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); - v = (v + (v >> 4)) & (T)~(T)0/255*15; - return (unsigned int)((v * ((~((T)0))/((T)255))) >> ((sizeof(T) - 1) * 8)); - } -#endif - -// Byte swappers for big/little endian conversion -#if __BYTE_ORDER == __LITTLE_ENDIAN - static ZT_ALWAYS_INLINE uint8_t hton(uint8_t n) { return n; } - static ZT_ALWAYS_INLINE int8_t hton(int8_t n) { return n; } - static ZT_ALWAYS_INLINE uint16_t hton(uint16_t n) { return htons(n); } - static ZT_ALWAYS_INLINE int16_t hton(int16_t n) { return (int16_t)Utils::hton((uint16_t)n); } - static ZT_ALWAYS_INLINE uint32_t hton(uint32_t n) - { -#if defined(__GNUC__) -#if defined(__FreeBSD__) - return htonl(n); -#elif (!defined(__OpenBSD__)) - return __builtin_bswap32(n); -#endif -#else - return htonl(n); -#endif - } - static ZT_ALWAYS_INLINE int32_t hton(int32_t n) { return (int32_t)Utils::hton((uint32_t)n); } - static ZT_ALWAYS_INLINE uint64_t hton(uint64_t n) - { -#if defined(__GNUC__) -#if defined(__FreeBSD__) - return bswap64(n); -#elif (!defined(__OpenBSD__)) - return __builtin_bswap64(n); -#endif -#else - return ( - ((n & 0x00000000000000FFULL) << 56) | - ((n & 0x000000000000FF00ULL) << 40) | - ((n & 0x0000000000FF0000ULL) << 24) | - ((n & 0x00000000FF000000ULL) << 8) | - ((n & 0x000000FF00000000ULL) >> 8) | - ((n & 0x0000FF0000000000ULL) >> 24) | - ((n & 0x00FF000000000000ULL) >> 40) | - ((n & 0xFF00000000000000ULL) >> 56) - ); -#endif - } - static ZT_ALWAYS_INLINE int64_t hton(int64_t n) { return (int64_t)hton((uint64_t)n); } -#else - template - static ZT_ALWAYS_INLINE T hton(T n) { return n; } +template +static ZT_ALWAYS_INLINE unsigned int countBits(T v) +{ + v = v - ((v >> 1) & (T)~(T)0/3); + v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); + v = (v + (v >> 4)) & (T)~(T)0/255*15; + return (unsigned int)((v * ((~((T)0))/((T)255))) >> ((sizeof(T) - 1) * 8)); +} #endif #if __BYTE_ORDER == __LITTLE_ENDIAN - static ZT_ALWAYS_INLINE uint8_t ntoh(uint8_t n) { return n; } - static ZT_ALWAYS_INLINE int8_t ntoh(int8_t n) { return n; } - static ZT_ALWAYS_INLINE uint16_t ntoh(uint16_t n) { return ntohs(n); } - static ZT_ALWAYS_INLINE int16_t ntoh(int16_t n) { return (int16_t)Utils::ntoh((uint16_t)n); } - static ZT_ALWAYS_INLINE uint32_t ntoh(uint32_t n) - { +static ZT_ALWAYS_INLINE uint8_t hton(uint8_t n) { return n; } +static ZT_ALWAYS_INLINE int8_t hton(int8_t n) { return n; } +static ZT_ALWAYS_INLINE uint16_t hton(uint16_t n) +{ #if defined(__GNUC__) #if defined(__FreeBSD__) - return ntohl(n); + return htons(n); #elif (!defined(__OpenBSD__)) - return __builtin_bswap32(n); + return __builtin_bswap16(n); #endif #else - return ntohl(n); + return htons(n); #endif - } - static ZT_ALWAYS_INLINE int32_t ntoh(int32_t n) { return (int32_t)Utils::ntoh((uint32_t)n); } - static ZT_ALWAYS_INLINE uint64_t ntoh(uint64_t n) - { +} +static ZT_ALWAYS_INLINE int16_t hton(int16_t n) { return (int16_t)Utils::hton((uint16_t)n); } +static ZT_ALWAYS_INLINE uint32_t hton(uint32_t n) +{ #if defined(__GNUC__) #if defined(__FreeBSD__) - return bswap64(n); + return htonl(n); #elif (!defined(__OpenBSD__)) - return __builtin_bswap64(n); + return __builtin_bswap32(n); #endif #else - return ( - ((n & 0x00000000000000FFULL) << 56) | - ((n & 0x000000000000FF00ULL) << 40) | - ((n & 0x0000000000FF0000ULL) << 24) | - ((n & 0x00000000FF000000ULL) << 8) | - ((n & 0x000000FF00000000ULL) >> 8) | - ((n & 0x0000FF0000000000ULL) >> 24) | - ((n & 0x00FF000000000000ULL) >> 40) | - ((n & 0xFF00000000000000ULL) >> 56) - ); + return htonl(n); +#endif +} +static ZT_ALWAYS_INLINE int32_t hton(int32_t n) { return (int32_t)Utils::hton((uint32_t)n); } +static ZT_ALWAYS_INLINE uint64_t hton(uint64_t n) +{ +#if defined(__GNUC__) +#if defined(__FreeBSD__) + return bswap64(n); +#elif (!defined(__OpenBSD__)) + return __builtin_bswap64(n); #endif - } - static ZT_ALWAYS_INLINE int64_t ntoh(int64_t n) { return (int64_t)ntoh((uint64_t)n); } #else - template - static ZT_ALWAYS_INLINE T ntoh(T n) { return n; } + return ( + ((n & 0x00000000000000FFULL) << 56) | + ((n & 0x000000000000FF00ULL) << 40) | + ((n & 0x0000000000FF0000ULL) << 24) | + ((n & 0x00000000FF000000ULL) << 8) | + ((n & 0x000000FF00000000ULL) >> 8) | + ((n & 0x0000FF0000000000ULL) >> 24) | + ((n & 0x00FF000000000000ULL) >> 40) | + ((n & 0xFF00000000000000ULL) >> 56) + ); #endif -}; +} +static ZT_ALWAYS_INLINE int64_t hton(int64_t n) { return (int64_t)hton((uint64_t)n); } +#else +template +static ZT_ALWAYS_INLINE T hton(T n) { return n; } +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +static ZT_ALWAYS_INLINE uint8_t ntoh(uint8_t n) { return n; } +static ZT_ALWAYS_INLINE int8_t ntoh(int8_t n) { return n; } +static ZT_ALWAYS_INLINE uint16_t ntoh(uint16_t n) +{ +#if defined(__GNUC__) +#if defined(__FreeBSD__) + return htons(n); +#elif (!defined(__OpenBSD__)) + return __builtin_bswap16(n); +#endif +#else + return htons(n); +#endif +} +static ZT_ALWAYS_INLINE int16_t ntoh(int16_t n) { return (int16_t)Utils::ntoh((uint16_t)n); } +static ZT_ALWAYS_INLINE uint32_t ntoh(uint32_t n) +{ +#if defined(__GNUC__) +#if defined(__FreeBSD__) + return ntohl(n); +#elif (!defined(__OpenBSD__)) + return __builtin_bswap32(n); +#endif +#else + return ntohl(n); +#endif +} +static ZT_ALWAYS_INLINE int32_t ntoh(int32_t n) { return (int32_t)Utils::ntoh((uint32_t)n); } +static ZT_ALWAYS_INLINE uint64_t ntoh(uint64_t n) +{ +#if defined(__GNUC__) +#if defined(__FreeBSD__) + return bswap64(n); +#elif (!defined(__OpenBSD__)) + return __builtin_bswap64(n); +#endif +#else + return ( + ((n & 0x00000000000000FFULL) << 56) | + ((n & 0x000000000000FF00ULL) << 40) | + ((n & 0x0000000000FF0000ULL) << 24) | + ((n & 0x00000000FF000000ULL) << 8) | + ((n & 0x000000FF00000000ULL) >> 8) | + ((n & 0x0000FF0000000000ULL) >> 24) | + ((n & 0x00FF000000000000ULL) >> 40) | + ((n & 0xFF00000000000000ULL) >> 56) + ); +#endif +} +static ZT_ALWAYS_INLINE int64_t ntoh(int64_t n) { return (int64_t)ntoh((uint64_t)n); } +#else +template +static ZT_ALWAYS_INLINE T ntoh(T n) { return n; } +#endif + +static ZT_ALWAYS_INLINE uint64_t readUInt64(const void *const p) +{ +#ifdef ZT_NO_TYPE_PUNNING + const uint8_t *const b = reinterpret_cast(p); + return ( + ((uint64_t)b[0] << 56) | + ((uint64_t)b[1] << 48) | + ((uint64_t)b[2] << 40) | + ((uint64_t)b[3] << 32) | + ((uint64_t)b[4] << 24) | + ((uint64_t)b[5] << 16) | + ((uint64_t)b[6] << 8) | + (uint64_t)b[7]); +#else + return ntoh(*reinterpret_cast(p)); +#endif +} + +static ZT_ALWAYS_INLINE void putUInt64(void *const p,const uint64_t i) +{ +#ifdef ZT_NO_TYPE_PUNNING + uint8_t *const b = reinterpret_cast(p); + p[0] = (uint8_t)(i << 56); + p[1] = (uint8_t)(i << 48); + p[2] = (uint8_t)(i << 40); + p[3] = (uint8_t)(i << 32); + p[4] = (uint8_t)(i << 24); + p[5] = (uint8_t)(i << 16); + p[6] = (uint8_t)(i << 8); + p[7] = (uint8_t)i; +#else + *reinterpret_cast(p) = Utils::hton(i); +#endif +} + +} // namespace Utils } // namespace ZeroTier diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index 142f7cc12..61305e8ff 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -510,24 +510,6 @@ std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl) return std::string((dfl) ? dfl : ""); } -std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv) -{ - std::string s(jsonString(jv,"")); - if (s.length() > 0) { - unsigned int buflen = (unsigned int)((s.length() / 2) + 1); - char *buf = new char[buflen]; - try { - unsigned int l = Utils::unhex(s.c_str(),buf,buflen); - std::string b(buf,l); - delete [] buf; - return b; - } catch ( ... ) { - delete [] buf; - } - } - return std::string(); -} - #endif // OMIT_JSON_SUPPORT // Used to convert HTTP header names to ASCII lower case diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index 30b34115c..f76c70c97 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -292,7 +292,6 @@ public: static uint64_t jsonIntHex(const nlohmann::json &jv,const uint64_t dfl); static bool jsonBool(const nlohmann::json &jv,const bool dfl); static std::string jsonString(const nlohmann::json &jv,const char *dfl); - static std::string jsonBinFromHex(const nlohmann::json &jv); #endif // OMIT_JSON_SUPPORT private: diff --git a/osdep/Thread.hpp b/osdep/Thread.hpp index 4cd569941..ccfc6d0f3 100644 --- a/osdep/Thread.hpp +++ b/osdep/Thread.hpp @@ -149,7 +149,7 @@ public: pthread_attr_init(&tattr); // This corrects for systems with abnormally small defaults (musl) and also // shrinks the stack on systems with large defaults to save a bit of memory. - pthread_attr_setstacksize(&tattr,ZT_THREAD_MIN_STACK_SIZE); + pthread_attr_setstacksize(&tattr,1048576); if (pthread_create(&t._tid,&tattr,&___zt_threadMain,instance)) { pthread_attr_destroy(&tattr); throw std::runtime_error("pthread_create() failed, unable to create thread"); diff --git a/root/root.cpp b/root/root.cpp index 8db77c5de..33916faa4 100644 --- a/root/root.cpp +++ b/root/root.cpp @@ -230,7 +230,7 @@ static ZT_ALWAYS_INLINE std::array< uint64_t,2 > ip6ToH128(const InetAddress &ip static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip,Packet &pkt) { - char ipstr[128],ipstr2[128],astr[32],astr2[32],tmpstr[256]; + char ipstr[128]; const bool fragment = pkt[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR; const Address source(pkt.source()); const Address dest(pkt.destination()); @@ -249,10 +249,8 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip { std::lock_guard pbi_l(s_peersByIdentity_l); auto pById = s_peersByIdentity.find(id); - if (pById != s_peersByIdentity.end()) { + if (pById != s_peersByIdentity.end()) peer = pById->second; - //printf("%s has %s (known (1))" ZT_EOL_S,ip->toString(ipstr),source().toString(astr)); - } } if (peer) { if (!pkt.dearmor(peer->key)) { @@ -503,13 +501,11 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip bool introduce = false; if (fragment) { if ((hops = (int)reinterpret_cast(&pkt)->incrementHops()) > s_relayMaxHops) { - //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); s_discardedForwardRate.log(now,pkt.size()); return; } } else { if ((hops = (int)pkt.incrementHops()) > s_relayMaxHops) { - //printf("%s refused to forward to %s: max hop count exceeded" ZT_EOL_S,ip->toString(ipstr),dest.toString(astr)); s_discardedForwardRate.log(now,pkt.size()); return; } @@ -552,7 +548,6 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip for(auto a=sources->second.begin();a!=sources->second.end();++a) { for(auto b=toAddrs.begin();b!=toAddrs.end();++b) { if (((*a)->ip6)&&(b->second->ip6)) { - //printf("* introducing %s(%s) to %s(%s)" ZT_EOL_S,ip->toString(ipstr),source.toString(astr),b->second->ip6.toString(ipstr2),dest.toString(astr2)); // Introduce source to destination (V6) Packet outp(source,s_self.address(),Packet::VERB_RENDEZVOUS); @@ -581,7 +576,6 @@ static void handlePacket(const int v4s,const int v6s,const InetAddress *const ip b->second->lastSend = now; } if (((*a)->ip4)&&(b->second->ip4)) { - //printf("* introducing %s(%s) to %s(%s)" ZT_EOL_S,ip->toString(ipstr),source.toString(astr),b->second->ip4.toString(ipstr2),dest.toString(astr2)); // Introduce source to destination (V4) Packet outp(source,s_self.address(),Packet::VERB_RENDEZVOUS); @@ -1134,7 +1128,7 @@ int main(int argc,char **argv) for(auto a=s_multicastSubscriptions.begin();a!=s_multicastSubscriptions.end();) { for(auto b=a->second.begin();b!=a->second.end();) { for(auto c=b->second.begin();c!=b->second.end();) { - if ((now - c->second) > ZT_MULTICAST_LIKE_EXPIRE) + if ((now - c->second) > ZT_PEER_ACTIVITY_TIMEOUT) b->second.erase(c++); else ++c; } diff --git a/selftest.cpp b/selftest.cpp index d2862764a..1ee79ad96 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -11,10 +11,10 @@ */ /****/ -#include -#include -#include -#include +#include +#include +#include +#include #include #include