From 11d367d5ec9d6aacc921e557f3fcf38c93edfd7f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 31 Mar 2021 15:33:31 -0400 Subject: [PATCH] Attic cleaning, some work on certs, etc. --- attic/go/CMakeLists.txt | 346 ------- attic/go/build.bat | 6 - attic/go/cmd/zerotier/cli/cert.go | 190 ---- attic/go/cmd/zerotier/cli/controller.go | 26 - attic/go/cmd/zerotier/cli/help.go | 128 --- attic/go/cmd/zerotier/cli/identity.go | 121 --- attic/go/cmd/zerotier/cli/join.go | 89 -- attic/go/cmd/zerotier/cli/leave.go | 45 - attic/go/cmd/zerotier/cli/locator.go | 82 -- attic/go/cmd/zerotier/cli/misc.go | 286 ------ attic/go/cmd/zerotier/cli/network.go | 188 ---- attic/go/cmd/zerotier/cli/peer.go | 100 -- attic/go/cmd/zerotier/cli/service.go | 49 - attic/go/cmd/zerotier/cli/set.go | 18 - attic/go/cmd/zerotier/cli/status.go | 72 -- attic/go/cmd/zerotier/zerotier.go | 183 ---- attic/go/cmd/zt_service_tests/certificate.go | 142 --- attic/go/cmd/zt_service_tests/locator.go | 84 -- .../cmd/zt_service_tests/zt_service_tests.go | 23 - attic/go/go.mod | 9 - attic/go/go.sum | 17 - attic/go/pkg/zerotier/address.go | 76 -- attic/go/pkg/zerotier/api.go | 638 ------------ attic/go/pkg/zerotier/base32blob.go | 38 - attic/go/pkg/zerotier/certificate.go | 550 ---------- attic/go/pkg/zerotier/endpoint.go | 178 ---- attic/go/pkg/zerotier/errors.go | 49 - attic/go/pkg/zerotier/fingerprint.go | 123 --- attic/go/pkg/zerotier/identity.go | 274 ----- attic/go/pkg/zerotier/inetaddress.go | 202 ---- attic/go/pkg/zerotier/localconfig.go | 130 --- attic/go/pkg/zerotier/locator.go | 159 --- attic/go/pkg/zerotier/mac.go | 93 -- attic/go/pkg/zerotier/misc.go | 304 ------ attic/go/pkg/zerotier/multicastgroup.go | 42 - attic/go/pkg/zerotier/nativetap.go | 193 ---- attic/go/pkg/zerotier/network.go | 366 ------- attic/go/pkg/zerotier/node.go | 955 ------------------ attic/go/pkg/zerotier/osdep-posix.go | 45 - attic/go/pkg/zerotier/osdep-windows.go | 34 - attic/go/pkg/zerotier/path.go | 30 - attic/go/pkg/zerotier/peer.go | 52 - attic/go/pkg/zerotier/route.go | 54 - attic/go/pkg/zerotier/sizelimitwriter.go | 109 -- attic/go/pkg/zerotier/tap.go | 49 - attic/go/serviceiocore/CMakeLists.txt | 18 - attic/go/serviceiocore/GoGlue.cpp | 738 -------------- attic/go/serviceiocore/GoGlue.h | 59 -- attic/go/serviceiocore/README.md | 4 - core/Blob.hpp | 48 - core/CMakeLists.txt | 2 + core/Certificate.cpp | 49 +- core/Certificate.hpp | 10 +- core/InetAddress.hpp | 29 - core/OS.hpp | 28 +- core/Path.hpp | 87 ++ core/Peer.hpp | 12 +- core/SharedPtr.hpp | 8 + core/Topology.cpp | 329 +----- core/Topology.hpp | 66 +- core/TrustStore.cpp | 218 ++++ core/TrustStore.hpp | 138 +++ core/zerotier.h | 5 + 63 files changed, 532 insertions(+), 8263 deletions(-) delete mode 100644 attic/go/CMakeLists.txt delete mode 100644 attic/go/build.bat delete mode 100644 attic/go/cmd/zerotier/cli/cert.go delete mode 100644 attic/go/cmd/zerotier/cli/controller.go delete mode 100644 attic/go/cmd/zerotier/cli/help.go delete mode 100644 attic/go/cmd/zerotier/cli/identity.go delete mode 100644 attic/go/cmd/zerotier/cli/join.go delete mode 100644 attic/go/cmd/zerotier/cli/leave.go delete mode 100644 attic/go/cmd/zerotier/cli/locator.go delete mode 100644 attic/go/cmd/zerotier/cli/misc.go delete mode 100644 attic/go/cmd/zerotier/cli/network.go delete mode 100644 attic/go/cmd/zerotier/cli/peer.go delete mode 100644 attic/go/cmd/zerotier/cli/service.go delete mode 100644 attic/go/cmd/zerotier/cli/set.go delete mode 100644 attic/go/cmd/zerotier/cli/status.go delete mode 100644 attic/go/cmd/zerotier/zerotier.go delete mode 100644 attic/go/cmd/zt_service_tests/certificate.go delete mode 100644 attic/go/cmd/zt_service_tests/locator.go delete mode 100644 attic/go/cmd/zt_service_tests/zt_service_tests.go delete mode 100644 attic/go/go.mod delete mode 100644 attic/go/go.sum delete mode 100644 attic/go/pkg/zerotier/address.go delete mode 100644 attic/go/pkg/zerotier/api.go delete mode 100644 attic/go/pkg/zerotier/base32blob.go delete mode 100644 attic/go/pkg/zerotier/certificate.go delete mode 100644 attic/go/pkg/zerotier/endpoint.go delete mode 100644 attic/go/pkg/zerotier/errors.go delete mode 100644 attic/go/pkg/zerotier/fingerprint.go delete mode 100644 attic/go/pkg/zerotier/identity.go delete mode 100644 attic/go/pkg/zerotier/inetaddress.go delete mode 100644 attic/go/pkg/zerotier/localconfig.go delete mode 100644 attic/go/pkg/zerotier/locator.go delete mode 100644 attic/go/pkg/zerotier/mac.go delete mode 100644 attic/go/pkg/zerotier/misc.go delete mode 100644 attic/go/pkg/zerotier/multicastgroup.go delete mode 100644 attic/go/pkg/zerotier/nativetap.go delete mode 100644 attic/go/pkg/zerotier/network.go delete mode 100644 attic/go/pkg/zerotier/node.go delete mode 100644 attic/go/pkg/zerotier/osdep-posix.go delete mode 100644 attic/go/pkg/zerotier/osdep-windows.go delete mode 100644 attic/go/pkg/zerotier/path.go delete mode 100644 attic/go/pkg/zerotier/peer.go delete mode 100644 attic/go/pkg/zerotier/route.go delete mode 100644 attic/go/pkg/zerotier/sizelimitwriter.go delete mode 100644 attic/go/pkg/zerotier/tap.go delete mode 100644 attic/go/serviceiocore/CMakeLists.txt delete mode 100644 attic/go/serviceiocore/GoGlue.cpp delete mode 100644 attic/go/serviceiocore/GoGlue.h delete mode 100644 attic/go/serviceiocore/README.md create mode 100644 core/TrustStore.cpp create mode 100644 core/TrustStore.hpp diff --git a/attic/go/CMakeLists.txt b/attic/go/CMakeLists.txt deleted file mode 100644 index ff060c5a5..000000000 --- a/attic/go/CMakeLists.txt +++ /dev/null @@ -1,346 +0,0 @@ -cmake_minimum_required (VERSION 3.8) - -cmake_policy(SET CMP0048 NEW) - -if(${CMAKE_VERSION} VERSION_LESS 3.15) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -else() - cmake_policy(VERSION 3.15) -endif() - -set(ZEROTIER_VERSION_MAJOR 1 CACHE INTERNAL "") -set(ZEROTIER_VERSION_MINOR 9 CACHE INTERNAL "") -set(ZEROTIER_VERSION_REVISION 0 CACHE INTERNAL "") -set(ZEROTIER_VERSION_BUILD 0 CACHE INTERNAL "") - -project(zerotier - VERSION ${ZEROTIER_VERSION_MAJOR}.${ZEROTIER_VERSION_MINOR}.${ZEROTIER_VERSION_REVISION}.${ZEROTIER_VERSION_BUILD} - DESCRIPTION "ZeroTier Network Hypervisor" - LANGUAGES CXX C) - -if(NOT PACKAGE_STATIC) - - find_program( - GO go - HINTS "/usr/local/go/bin" "/usr/bin" "/usr/local/bin" "C:/go/bin" - ) - if(NOT GO) - message(FATAL_ERROR "Golang not found") - else(NOT GO) - message(STATUS "Found Golang at ${GO}") - endif(NOT GO) - - set(default_build_type "Release") - - if(WIN32) - set(CMAKE_CXX_STANDARD 17) - set(CMAKE_SYSTEM_VERSION "7" CACHE STRING INTERNAL FORCE) - else(WIN32) - if(APPLE) - set(CMAKE_CXX_STANDARD 17) - else(APPLE) - set(CMAKE_CXX_STANDARD 11) - endif(APPLE) - endif(WIN32) - - 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_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") - endif() - - option(BUILD_CENTRAL_CONTROLLER "Build ZeroTier Central Controller" OFF) - if(BUILD_CENTRAL_CONTROLLER) - find_package(PkgConfig REQUIRED) - if(APPLE) - set(CMAKE_PREFIX_PATH - ${CMAKE_PREFIX_PATH} - /usr/local/opt/libpq - /usr/local/lib - ) - endif(APPLE) - find_package(PostgreSQL REQUIRED) - - pkg_check_modules(hiredis REQUIRED IMPORTED_TARGET hiredis) - - add_subdirectory(controller/thirdparty/redis-plus-plus-1.1.1) - set(redispp_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/controller/thirdparty/redis-plus-plus-1.1.1/src/sw) - set(redispp_STATIC_LIB redispp_static) - endif(BUILD_CENTRAL_CONTROLLER) - - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-DZT_DEBUG) - endif(CMAKE_BUILD_TYPE STREQUAL "Debug") - - if(WIN32) - - message("++ Setting Windows Compiler Flags ${CMAKE_BUILD_TYPE}") - - add_definitions(-DNOMINMAX) - add_compile_options( - -Wall - -Wno-deprecated - -Wno-unused-function - -Wno-format - $<$:-g> - $<$:-O0> - $<$:-O3> - $<$:-ffast-math> - $<$:-O3> - $<$:-g> - ) - - set(GOFLAGS - -a - -trimpath - ) - - if(BUILD_32BIT) - set(CMAKE_SYSTEM_PROCESSOR "x86" CACHE STRING "system processor") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") - set(GOARCH "GOARCH=386" CACHE STRING "go architecture") - add_compile_options( - -m32 - ) - add_link_options( - -m32 - ) - endif(BUILD_32BIT) - - else(WIN32) - - set(GOFLAGS - -trimpath - -buildmode=pie - ) - - if(APPLE) - - message("++ Setting MacOS Compiler Flags ${CMAKE_BUILD_TYPE}") - - set(MACOS_VERSION_MIN "10.12") - - add_compile_options( - -Wall - -Wno-deprecated - -Wno-unused-function - -mmacosx-version-min=${MACOS_VERSION_MIN} - $<$:-g> - $<$:-O0> - $<$:-Ofast> - $<$:-ffast-math> - $<$:-fPIE> - $<$:-flto> - $<$:-O1> - $<$:-fPIE> - $<$:-g> - ) - - add_link_options( - -mmacosx-version-min=${MACOS_VERSION_MIN} - $<$:-flto> - ) - - set(GOFLAGS - ${GOFLAGS} - -a - -ldflags '-w -extldflags \"-Wl,-undefined -Wl,dynamic_lookup\"' - ) - - else(APPLE) - - message("++ Setting Linux/BSD/Posix Compiler Flags (${CMAKE_BUILD_TYPE})") - - add_compile_options( - -Wall - -Wno-deprecated - -Wno-unused-function - -Wno-format - $<$:-g> - $<$:-O0> - $<$:-O3> - $<$:-ffast-math> - $<$:-fPIE> - $<$:-O3> - $<$:-fPIE> - $<$:-g> - ) - - option(BUILD_32BIT "Force building as 32-bit binary" OFF) - option(BUILD_STATIC "Build statically linked executable" OFF) - option(BUILD_ARM_V5 "Build ARMv5" OFF) - option(BUILD_ARM_V6 "Build ARMv6" OFF) - - if(BUILD_ARM_V5 AND BUILD_ARM_V6) - message(FATAL_ERROR "BUILD_ARM_V5 and BUILD_ARM_V6 are mutually exclusive!") - endif(BUILD_ARM_V5 AND BUILD_ARM_V6) - - if(BUILD_32BIT) - set(CMAKE_SYSTEM_PROCESSOR "x86" CACHE STRING "system processor") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") - set(GOARCH "GOARCH=386" CACHE STRING "go architecture") - add_compile_options( - -m32 - ) - endif(BUILD_32BIT) - - if(BUILD_STATIC) - add_link_options( - -static - ) - set(CMAKE_EXE_LINKER_FLAGS "-static ${CMAKE_EXE_LINKER_FLAGS}") - set(GOFLAGS - ${GOFLAGS} - -a - -tags osusergo,netgo - -ldflags '-w -extldflags \"-static -Wl,-unresolved-symbols=ignore-all\"' - ) - else(BUILD_STATIC) - set(GOFLAGS - ${GOFLAGS} - -a - -ldflags '-w -extldflags \"-Wl,-unresolved-symbols=ignore-all\"' - ) - endif(BUILD_STATIC) - - if(BUILD_ARM_V5) - set(GOARM "GOARM=5") - endif(BUILD_ARM_V5) - - if(BUILD_ARM_V6) - set(GOARM "GOARM=6") - endif(BUILD_ARM_V6) - - endif(APPLE) - endif(WIN32) - - if ( - CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "amd64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "X86_64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "x64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "X64" - ) - message("++ Adding flags for processor ${CMAKE_SYSTEM_PROCESSOR}") - add_compile_options(-maes -mrdrnd -mpclmul -msse -msse2) - endif() - - if ( - CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "arm64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64" OR - CMAKE_SYSTEM_PROCESSOR MATCHES "AARCH64" - ) - message("++ Adding flags for processor ${CMAKE_SYSTEM_PROCESSOR}") - add_compile_options(-march=armv8-a+aes+crypto -mtune=generic -mstrict-align) - endif() - - set(GO_BUILD_TAGS) - - if(BUILD_CENTRAL_CONTROLLER) - add_definitions(-DZT_CONTROLLER_USE_LIBPQ=1) - set(GO_BUILD_TAGS -tags central) - endif(BUILD_CENTRAL_CONTROLLER) - - add_subdirectory(core) - add_subdirectory(controller) - add_subdirectory(osdep) - add_subdirectory(serviceiocore) - - file(GLOB go_src - ${CMAKE_SOURCE_DIR}/cmd/*.go - ${CMAKE_SOURCE_DIR}/cmd/cmd/*.go - ${CMAKE_SOURCE_DIR}/pkg/zerotier/*.go - ) - file(GLOB go_zt_service_tests_cmd_src - ${CMAKE_SOURCE_DIR}/cmd/zt_service_tests/*.go - ) - - if(WIN32) - set(GO_EXE_NAME "zerotier.exe") - set(GO_SERVICE_TESTS_EXE_NAME "zt_service_tests.exe") - set(GO_EXTRA_LIBRARIES "-lstdc++ -lwsock32 -lws2_32 -liphlpapi -lole32 -loleaut32 -lrpcrt4 -luuid") - else(WIN32) - set(GO_EXE_NAME "zerotier") - set(GO_SERVICE_TESTS_EXE_NAME "zt_service_tests") - if(CMAKE_SYSTEM_NAME MATCHES "Linux") - set(GO_EXTRA_LIBRARIES "-lstdc++") - if(BUILD_ARM_V5) - set(GO_EXTRA_LIBRARIES - ${GO_EXTRA_LIBRARIES} - "-latomic" - ) - endif(BUILD_ARM_V5) - else() - set(GO_EXTRA_LIBRARIES "-lc++" "-lm") - endif() - endif(WIN32) - - add_custom_target( - zt_service_tests ALL - BYPRODUCTS ${CMAKE_BINARY_DIR}/zt_service_tests - SOURCES ${go_src} ${go_zt_service_tests_cmd_src} - COMMAND ${CMAKE_COMMAND} -E env ${GOARCH} ${GOARM} CGO_ENABLED=1 CGO_CFLAGS=\"-O3\" CGO_LDFLAGS=\"$ $ $ $ ${GO_EXTRA_LIBRARIES}\" ${GO} build -mod=vendor ${GOFLAGS} -o ${CMAKE_BINARY_DIR}/${GO_SERVICE_TESTS_EXE_NAME} ${go_zt_service_tests_cmd_src} - COMMENT "Compiling zt_service_tests (Go/cgo self-tests)..." - ) - add_dependencies(zt_service_tests zt_osdep zt_core zt_controller zt_service_io_core) - - add_custom_target( - zerotier ALL - BYPRODUCTS ${CMAKE_BINARY_DIR}/zerotier - SOURCES ${go_src} - COMMAND ${CMAKE_COMMAND} -E env ${GOARCH} ${GOARM} CGO_ENABLED=1 CGO_CFLAGS=\"-O3\" CGO_LDFLAGS=\"$ $ $ $ ${GO_EXTRA_LIBRARIES}\" ${GO} build -mod=vendor ${GOFLAGS} -o ${CMAKE_BINARY_DIR}/${GO_EXE_NAME} ${CMAKE_SOURCE_DIR}/cmd/zerotier/zerotier.go - COMMENT "Compiling Go Code..." - ) - add_dependencies(zerotier zt_osdep zt_core zt_controller zt_service_io_core) - - install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/zerotier DESTINATION bin) - -else(NOT PACKAGE_STATIC) - - if(BUILD_32BIT) - set(CMAKE_SYSTEM_PROCESSOR "x86" CACHE STRING "system processor") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" CACHE STRING "c++ flags") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32" CACHE STRING "c flags") - add_compile_options( - -m32 - ) - endif(BUILD_32BIT) - - set(STATIC_BINARY ${CMAKE_BINARY_DIR}/zerotier) - set(IMPORTED_LOCATION ${CMAKE_BINARY_DIR}) - add_executable(zerotier IMPORTED GLOBAL) - install(PROGRAMS ${STATIC_BINARY} DESTINATION bin) - -endif(NOT PACKAGE_STATIC) - -# Linux packaging - -if("${CMAKE_SYSTEM_NAME}" EQUAL "Linux") - if(IS_DIRECTORY /lib/systemd/system) - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/debian/zerotier.service - DESTINATION /lib/systemd/system - ) - elseif(IS_DIRECTORY /usr/lib/systemd/system) - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/debian/zerotier.service - DESTINATION /usr/lib/systemd/system - ) - else() - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/debian/zerotier.init - DESTINATION /etc/init.d - ) - endif() -endif() - -if("${ZT_PACKAGE_FORMAT}" MATCHES "DEB") - include(packaging/debian.cmake) -elseif("${ZT_PACKAGE_FORMAT}" MATCHES "RPM") - include(packaging/rpm.cmake) -else() -endif() diff --git a/attic/go/build.bat b/attic/go/build.bat deleted file mode 100644 index d5f34d43a..000000000 --- a/attic/go/build.bat +++ /dev/null @@ -1,6 +0,0 @@ -echo off -mkdir build -cd build -cmake .. -G "MinGW Makefiles" -make -j4 -cd .. diff --git a/attic/go/cmd/zerotier/cli/cert.go b/attic/go/cmd/zerotier/cli/cert.go deleted file mode 100644 index 6d5b4a2c1..000000000 --- a/attic/go/cmd/zerotier/cli/cert.go +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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" - "zerotier/pkg/zerotier" -) - -func interactiveMakeSubject() *zerotier.CertificateSubject { - s := new(zerotier.CertificateSubject) - - - return s -} - -func Cert(basePath string, authTokenGenerator func() string, args []string, jsonOutput bool) int { - if len(args) < 1 { - Help() - return 1 - } - - switch args[0] { - - case "list": - - case "show": - if len(args) != 1 { - Help() - return 1 - } - - case "newsid": - if len(args) > 2 { - Help() - return 1 - } - - uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384) - if err != nil { - pErr("unable to create unique ID and private key: %s", err.Error()) - return 1 - } - - sec, err := json.MarshalIndent(&zerotier.CertificateSubjectUniqueIDSecret{ - UniqueID: uniqueId, - UniqueIDSecret: uniqueIdPrivate, - }, "", " ") - if err != nil { - pErr("unable to create unique ID and private key: %s", err.Error()) - return 1 - } - - if len(args) == 1 { - fmt.Println(string(sec)) - } else { - _ = ioutil.WriteFile(args[1], sec, 0600) - pResult("%s", args[1]) - } - - case "newcsr": - if len(args) != 4 { - Help() - return 1 - } - - var subject zerotier.CertificateSubject - err := readJSONFile(args[1], &subject) - if err != nil { - pErr("unable to read subject from %s: %s", args[1], err.Error()) - return 1 - } - - var uniqueIdSecret zerotier.CertificateSubjectUniqueIDSecret - err = readJSONFile(args[2], &uniqueIdSecret) - if err != nil { - pErr("unable to read unique ID secret from %s: %s", args[2], err.Error()) - return 1 - } - - csr, err := zerotier.NewCertificateCSR(&subject, uniqueIdSecret.UniqueID, uniqueIdSecret.UniqueIDSecret) - if err != nil { - pErr("problem creating CSR: %s", err.Error()) - return 1 - } - - err = ioutil.WriteFile(args[3], csr, 0644) - if err == nil { - pResult("%s", args[3]) - } else { - pErr("unable to write CSR to %s: %s", args[3], err.Error()) - return 1 - } - - case "sign": - if len(args) != 4 { - Help() - return 1 - } - - csrBytes, err := ioutil.ReadFile(args[1]) - if err != nil { - pErr("unable to read CSR from %s: %s", args[1], err.Error()) - return 1 - } - csr, err := zerotier.NewCertificateFromBytes(csrBytes, false) - if err != nil { - pErr("CSR in %s is invalid: %s", args[1], err.Error()) - return 1 - } - - signingIdentity := cliGetIdentityOrFatal(args[2]) - if signingIdentity == nil { - pErr("unable to read identity from %s", args[2]) - return 1 - } - if !signingIdentity.HasPrivate() { - pErr("signing identity in %s lacks private key", args[2]) - return 1 - } - - cert, err := csr.Sign(signingIdentity) - if err != nil { - pErr("error signing CSR or generating certificate: %s", err.Error()) - return 1 - } - - cb, err := cert.Marshal() - if err != nil { - pErr("error marshaling signed certificate: %s", err.Error()) - return 1 - } - - err = ioutil.WriteFile(args[3], cb, 0644) - if err == nil { - pResult("%s", args[3]) - } else { - pErr("unable to write signed certificate to %s: %s", args[3], err.Error()) - return 1 - } - - case "verify", "dump": - if len(args) != 2 { - Help() - return 1 - } - - certBytes, err := ioutil.ReadFile(args[1]) - if err != nil { - pErr("unable to read certificate from %s: %s", args[1], err.Error()) - return 1 - } - - cert, err := zerotier.NewCertificateFromBytes(certBytes, true) - if err != nil { - pErr("certificate in %s invalid: %s", args[1], err.Error()) - return 1 - } - - if args[0] == "dump" { - fmt.Println(cert.JSON()) - } else { - fmt.Println("OK") - } - - case "import": - - case "restore": - - case "export": - - case "delete": - - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/controller.go b/attic/go/cmd/zerotier/cli/controller.go deleted file mode 100644 index 1c9f7ff01..000000000 --- a/attic/go/cmd/zerotier/cli/controller.go +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 - -func Controller(basePath string, authTokenGenerator func() string, args []string, jsonOutput bool) int { - if len(args) < 1 { - Help() - return 1 - } - - switch args[0] { - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/help.go b/attic/go/cmd/zerotier/cli/help.go deleted file mode 100644 index 3a54d1f07..000000000 --- a/attic/go/cmd/zerotier/cli/help.go +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "zerotier/pkg/zerotier" -) - -func Help() { - fmt.Printf(`ZeroTier Network Hypervisor Service Version %d.%d.%d -(c)2013-2021 ZeroTier, Inc. -Licensed under the ZeroTier BSL (see LICENSE.txt) - -Usage: zerotier [-global options] [command args] - -Global Options: - - -j Output raw JSON where applicable - -p Use alternate base path - -t Load secret auth token from a file - -T Set secret auth token on command line - -Common Operations: - - help Show this help - version Print version - -· status Show node status and configuration - -· set [option] [value] List all settings (with no args) -· port Primary P2P port -· secondaryport Secondary P2P port (0 to disable) -· blacklist cidr Toggle physical path blacklisting -· blacklist if Toggle interface prefix blacklisting -· portmap Toggle use of uPnP or NAT-PMP - -· peer [address] [command] [option] Peer management commands -· list List peers -· listroots List root peers -· show Show peer details -· try [...] Try peer at explicit endpoint - -· network list List VL2 networks -· network [command] [option] -· show Show network details (default) -· set [option] [value] Get or set network options -· manageips Is IP management allowed? -· manageroutes Is route management allowed? -· managedns Allow network to push DNS config -· globalips Allow assignment of global IPs? -· globalroutes Can global IP space routes be set? -· defaultroute Can default route be overridden? - -· join [-options] Join a virtual network - -a Token to submit to controller - -c Controller identity or fingerprint -· leave Leave a virtual network - -Advanced Operations: - - service Start this node (runs until stopped) - now [duration] Print current time [-]#[ms|s|m|h] - - controller [option] -· list List networks on controller -· new Create a new network -· set [setting] [value] Show or modify network settings -· members List members of a network -· member [setting] [value] Show or modify member level settings -· auth
Authorize a peer -· deauth
Deauthorize a peer - - identity [args] - new [c25519 | p384] Create identity (default: c25519) - getpublic Extract only public part of identity - fingerprint Get an identity's fingerprint - validate Locally validate an identity - sign Sign a file with an identity's key - verify Verify a signature - - locator [args] - new [...] Create new signed locator - verify Verify locator signature - show Show contents of a locator - - cert [args] -· list List certificates at local node -· show Show certificate details - newsid Create a new subject unique ID - newcsr Create a subject CSR - sign Sign a CSR to create a certificate - verify Verify certificate (not chain) - dump Verify and print certificate -· import [trust,[trust]] Import certificate into this node - trust flag: rootca Certificate is a root CA - trust flag: ztrootset ZeroTier root node set -· restore Re-import default certificates -· export [path] Export a certificate from this node -· delete Delete certificate from this node - - · Command requires a running node and access to a local API token. - -An
may be specified as a 10-digit short ZeroTier address, a -fingerprint containing both an address and a SHA384 hash, or an identity. -The latter two options are equivalent in terms of specificity and may be -used if stronger security guarantees are desired than those provided by -the basic ZeroTier addressing system. Fields of type must be -full identities and may be specified either verbatim or as a path to a file. - -An is a place where a peer may be reached. Currently these are -just 'IP/port' format addresses but other types may be added in the future. - -The 'service' command starts a node. It will run until the node receives -an exit signal and is normally not used directly. -`,zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) -} diff --git a/attic/go/cmd/zerotier/cli/identity.go b/attic/go/cmd/zerotier/cli/identity.go deleted file mode 100644 index 8c64db798..000000000 --- a/attic/go/cmd/zerotier/cli/identity.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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/hex" - "fmt" - "io/ioutil" - "strings" - - "zerotier/pkg/zerotier" -) - -func Identity(args []string) int { - if len(args) > 0 { - switch args[0] { - - case "new": - idType := zerotier.IdentityTypeC25519 - if len(args) > 1 { - if len(args) > 2 { - Help() - return 1 - } - switch args[1] { - case "c25519", "C25519", "0": - idType = zerotier.IdentityTypeC25519 - case "p384", "P384", "1": - idType = zerotier.IdentityTypeP384 - default: - Help() - return 1 - } - } - - id, err := zerotier.NewIdentity(idType) - if err != nil { - pErr("internal error generating identity: %s", err.Error()) - return 1 - } - - fmt.Println(id.PrivateKeyString()) - return 0 - - case "getpublic": - if len(args) == 2 { - fmt.Println(cliGetIdentityOrFatal(args[1]).String()) - return 0 - } - pErr("no identity specified") - return 1 - - case "fingerprint": - if len(args) == 2 { - fmt.Println(cliGetIdentityOrFatal(args[1]).Fingerprint().String()) - return 0 - } - pErr("no identity specified") - return 1 - - case "validate": - if len(args) == 2 { - if cliGetIdentityOrFatal(args[1]).LocallyValidate() { - fmt.Println("VALID") - return 0 - } - fmt.Println("INVALID") - return 1 - } - - case "sign", "verify": - if len(args) > 2 { - id := cliGetIdentityOrFatal(args[1]) - msg, err := ioutil.ReadFile(args[2]) - if err != nil { - pErr("unable to read input file: %s", err.Error()) - return 1 - } - - if args[0] == "verify" { - if len(args) == 4 { - sig, err := hex.DecodeString(strings.TrimSpace(args[3])) - if err != nil { - fmt.Println("FAILED") - return 1 - } - if id.Verify(msg, sig) { - fmt.Println("OK") - return 0 - } - } - fmt.Println("FAILED") - return 1 - } else { - sig, err := id.Sign(msg) - if err != nil { - pErr("internal error signing message: %s", err.Error()) - return 1 - } - fmt.Println(hex.EncodeToString(sig)) - return 0 - } - } - - } - } - - Help() - return 1 -} diff --git a/attic/go/cmd/zerotier/cli/join.go b/attic/go/cmd/zerotier/cli/join.go deleted file mode 100644 index 549c76cf9..000000000 --- a/attic/go/cmd/zerotier/cli/join.go +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "flag" - "fmt" - "os" - "strconv" - "strings" - - "zerotier/pkg/zerotier" -) - -func Join(basePath string, authTokenGenerator func() string, args []string) int { - authToken := authTokenGenerator() - - joinOpts := flag.NewFlagSet("join", flag.ContinueOnError) - controllerAuthToken := joinOpts.String("a", "", "") - controllerFingerprint := joinOpts.String("c", "", "") - err := joinOpts.Parse(os.Args[1:]) - if err != nil { - Help() - return 1 - } - args = joinOpts.Args() - if len(args) < 1 { - Help() - return 1 - } - - if !isValidNetworkID(args[0]) { - pErr("invalid network ID: %s", args[0]) - return 1 - } - nwid, err := strconv.ParseUint(args[0], 16, 64) - if err != nil { - pErr("ERROR: invalid network ID: %s", args[0]) - return 1 - } - nwids := fmt.Sprintf("%.16x", nwid) - - _ = *controllerAuthToken // TODO: not implemented yet - - var fp *zerotier.Fingerprint - if len(*controllerFingerprint) > 0 { - if strings.ContainsRune(*controllerFingerprint, '-') { - fp, err = zerotier.NewFingerprintFromString(*controllerFingerprint) - if err != nil { - pErr("invalid network controller fingerprint: %s", *controllerFingerprint) - return 1 - } - } else { - id, err := zerotier.NewIdentityFromString(*controllerFingerprint) - if err != nil { - pErr("invalid network controller identity: %s", *controllerFingerprint) - return 1 - } - fp = id.Fingerprint() - } - } - - var network zerotier.APINetwork - network.ID = zerotier.NetworkID(nwid) - network.ControllerFingerprint = fp - - if apiPost(basePath, authToken, "/network/"+nwids, &network, nil) <= 0 { - fmt.Println("FAILED") - } else { - if fp == nil { - fmt.Printf("OK %s\n", nwids) - } else { - fmt.Printf("OK %s %s\n", nwids, fp.String()) - } - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/leave.go b/attic/go/cmd/zerotier/cli/leave.go deleted file mode 100644 index eb6857628..000000000 --- a/attic/go/cmd/zerotier/cli/leave.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "strconv" - "zerotier/pkg/zerotier" -) - -func Leave(basePath string, authTokenGenerator func() string, args []string) int { - authToken := authTokenGenerator() - - if len(args) != 1 { - Help() - return 1 - } - - if len(args[0]) != zerotier.NetworkIDStringLength { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - return 1 - } - nwid, err := strconv.ParseUint(args[0], 16, 64) - if err != nil { - fmt.Printf("ERROR: invalid network ID: %s\n", args[0]) - return 1 - } - nwids := fmt.Sprintf("%.16x", nwid) - - apiDelete(basePath, authToken, "/network/"+nwids, nil) - fmt.Printf("OK %s", nwids) - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/locator.go b/attic/go/cmd/zerotier/cli/locator.go deleted file mode 100644 index 3db73078b..000000000 --- a/attic/go/cmd/zerotier/cli/locator.go +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "zerotier/pkg/zerotier" -) - -func Locator(args []string) int { - if len(args) < 1 { - Help() - return 1 - } - - switch args[0] { - - case "new": - if len(args) < 3 { - Help() - return 1 - } - id := cliGetIdentityOrFatal(args[1]) - if !id.HasPrivate() { - pErr("identity must include secret key to sign locator") - return 1 - } - var eps []*zerotier.Endpoint - for i:=2;i 0 { - f := s[0] - for _, c := range isTrueStringPrefixChars { - if c == f { - return true - } - } - } - return false -} - -func jsonDump(obj interface{}) string { - j, _ := json.MarshalIndent(obj, "", "\t") - return string(j) -} - -// parseAddressFingerprintOrIdentity parses an argument as an address, fingerprint, or identity. -// If it's an address, only that return variable is filled out. Fingerprints fill out both address and -// fingerprint. Identity fills out all three. -func parseAddressFingerprintOrIdentity(s string) (a zerotier.Address, fp *zerotier.Fingerprint, id *zerotier.Identity) { - var err error - - s = strings.TrimSpace(s) - hasColon := strings.ContainsRune(s, ':') - hasDash := strings.ContainsRune(s, '-') - - if len(s) == zerotier.AddressStringLength && !hasColon && !hasDash { - a, err = zerotier.NewAddressFromString(s) - if err == nil { - return - } - } - - if hasDash { - fp, err = zerotier.NewFingerprintFromString(s) - if err == nil { - a = fp.Address - return - } - } - - if hasColon { - id, err = zerotier.NewIdentityFromString(s) - if err == nil { - a = id.Address() - fp = id.Fingerprint() - return - } - } - - a = zerotier.Address(0) - return -} - -func cliGetIdentityOrFatal(s string) *zerotier.Identity { - if strings.ContainsRune(s, ':') { - id, _ := zerotier.NewIdentityFromString(s) - if id != nil { - return id - } - } - idData, err := ioutil.ReadFile(s) - if err != nil { - pErr("identity '%s' cannot be parsed as file or literal: %s", s, err.Error()) - os.Exit(1) - } - id, err := zerotier.NewIdentityFromString(string(idData)) - if err != nil { - pErr("identity '%s' cannot be parsed as file or literal: %s", s, err.Error()) - os.Exit(1) - } - return id -} - -func cliGetLocatorOrFatal(s string) *zerotier.Locator { - if strings.ContainsRune(s, '@') { - loc, _ := zerotier.NewLocatorFromString(s) - if loc != nil { - return loc - } - } - locData, err := ioutil.ReadFile(s) - if err != nil { - pErr("locator '%s' cannot be parsed as file or literal: %s", s, err.Error()) - os.Exit(1) - } - loc, err := zerotier.NewLocatorFromString(string(locData)) - if err != nil { - pErr("locator '%s' cannot be parsed as file or literal: %s", s, err.Error()) - os.Exit(1) - } - return loc -} - -func networkStatusStr(status int) string { - switch status { - case zerotier.NetworkStatusNotFound: - return "not-found" - case zerotier.NetworkStatusAccessDenied: - return "access-denied" - case zerotier.NetworkStatusRequestingConfiguration: - return "updating" - case zerotier.NetworkStatusOK: - return "ok" - } - return "???" -} - -func readJSONFile(p string, obj interface{}) error { - b, err := ioutil.ReadFile(p) - if err != nil { - return err - } - return json.Unmarshal(b, obj) -} - -func isValidAddress(a string) bool { - if len(a) == zerotier.AddressStringLength { - for _, c := range a { - if !strings.ContainsRune("0123456789abcdefABCDEF", c) { - return false - } - } - return true - } - return false -} - -func isValidNetworkID(a string) bool { - if len(a) == zerotier.NetworkIDStringLength { - for _, c := range a { - if !strings.ContainsRune("0123456789abcdefABCDEF", c) { - return false - } - } - return true - } - return false -} - -/* -func prompt(str string, dfl string) string { - if len(dfl) > 0 { - fmt.Printf("%s [%s]: ", str, dfl) - text, _ := bufio.NewReader(os.Stdin).ReadString('\n') - text = strings.TrimSpace(text) - if len(text) == 0 { - text = dfl - } - return text - } - fmt.Print(str) - text, _ := bufio.NewReader(os.Stdin).ReadString('\n') - return strings.TrimSpace(text) -} - -func promptInt(str string, dfl int64) int64 { - s := prompt(str, "") - if len(s) > 0 { - i, err := strconv.ParseInt(s, 10, 64) - if err == nil { - return i - } - } - return dfl -} - -func promptFile(str string) []byte { - s := prompt(str, "") - if len(s) > 0 { - b, err := ioutil.ReadFile(s) - if err == nil { - return b - } - } - return nil -} -*/ diff --git a/attic/go/cmd/zerotier/cli/network.go b/attic/go/cmd/zerotier/cli/network.go deleted file mode 100644 index ea7ba7f08..000000000 --- a/attic/go/cmd/zerotier/cli/network.go +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "strconv" - "strings" - - "zerotier/pkg/zerotier" -) - -func listNetworks(basePath, authToken string, jsonOutput bool) int { - var networks []zerotier.APINetwork - apiGet(basePath, authToken, "/network", &networks) - - if jsonOutput { - fmt.Println(jsonDump(networks)) - } else { - fmt.Printf("%-16s %-24s %-17s %-8s \n", "", "", "", "") - for _, nw := range networks { - t := "PRIVATE" - if nw.Config.Type == zerotier.NetworkTypePublic { - t = "PUBLIC" - } - fmt.Printf("%.16x %-24s %-17s %-16s %-7s %-16s ", uint64(nw.ID), nw.Config.Name, nw.Config.MAC.String(), networkStatusStr(nw.Config.Status), t, nw.PortName) - for i, ip := range nw.Config.AssignedAddresses { - if i > 0 { - fmt.Print(",") - } - fmt.Print(ip.String()) - } - fmt.Print("\n") - } - } - - return 0 -} - -func showNetwork(nwids string, network *zerotier.APINetwork, jsonOutput bool) { - if jsonOutput { - fmt.Println(jsonDump(&network)) - } else { - fmt.Printf("%s\t%s\n", nwids, network.Config.Name) - fmt.Printf("\tstatus:\t%s\n", networkStatusStr(network.Config.Status)) - enabled := "no" - if network.PortEnabled { - enabled = "yes" - } - bridge := "no" - if network.Config.Bridge { - bridge = "yes" - } - broadcast := "off" - if network.Config.BroadcastEnabled { - broadcast = "on" - } - fmt.Printf("\tport:\t%s dev %s type %s mtu %d enabled %s bridge %s broadcast %s\n", network.Config.MAC.String(), network.PortName, network.PortType, network.Config.MTU, enabled, bridge, broadcast) - fmt.Printf("\tmanaged addresses:\t") - for i, a := range network.Config.AssignedAddresses { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(a.String()) - } - fmt.Printf("\n\tmanaged routes:\t") - for i, r := range network.Config.Routes { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(r.Target.String()) - if r.Via == nil { - fmt.Print("->LAN") - } else { - fmt.Printf("->%s", r.Via.String()) - } - } - managedIPs := "blocked" - if network.Settings.AllowManagedIPs { - managedIPs = "allowed" - } - managedIPsGlobal := "blocked" - if network.Settings.AllowGlobalIPs { - managedIPsGlobal = "allowed" - } - fmt.Printf("\n\tmanaged address local permissions:\t%s global %s\n", managedIPs, managedIPsGlobal) - managedRoutes := "blocked" - if network.Settings.AllowManagedRoutes { - managedRoutes = "allowed" - } - managedGlobalRoutes := "blocked" - if network.Settings.AllowGlobalRoutes { - managedGlobalRoutes = "allowed" - } - managedDefaultRoute := "blocked" - if network.Settings.AllowDefaultRouteOverride { - managedDefaultRoute = "allowed" - } - fmt.Printf("\tmanaged route local permissions:\t%s global %s default %s\n", managedRoutes, managedGlobalRoutes, managedDefaultRoute) - } -} - -func Network(basePath string, authTokenGenerator func() string, args []string, jsonOutput bool) int { - if len(args) < 1 { - Help() - return 1 - } - - authToken := authTokenGenerator() - - if len(args) == 1 && args[0] == "list" { - return listNetworks(basePath, authToken, jsonOutput) - } - - if len(args[0]) != zerotier.NetworkIDStringLength { - pErr("ERROR: invalid network ID: %s", args[0]) - return 1 - } - nwid, err := strconv.ParseUint(args[0], 16, 64) - if err != nil { - pErr("ERROR: invalid network ID: %s", args[0]) - return 1 - } - nwids := fmt.Sprintf("%.16x", nwid) - - var network zerotier.APINetwork - apiGet(basePath, authToken, "/network/"+nwids, &network) - - if len(args) == 1 { - showNetwork(nwids, &network, jsonOutput) - } else { - switch args[1] { - - case "show", "info": - showNetwork(nwids, &network, jsonOutput) - - case "set": - if len(args) > 3 { - Help() - return 1 - } else if len(args) > 2 { - fieldName := strings.ToLower(strings.TrimSpace(args[2])) - var field *bool - switch fieldName { - case "managedips": - field = &network.Settings.AllowManagedIPs - case "managedroutes": - field = &network.Settings.AllowGlobalRoutes - case "globalips": - field = &network.Settings.AllowGlobalIPs - case "globalroutes": - field = &network.Settings.AllowGlobalRoutes - case "defaultroute": - field = &network.Settings.AllowDefaultRouteOverride - default: - Help() - return 1 - } - - if len(args) == 3 { - *field = isTrue(args[2]) - } - - fmt.Printf("%s\t%t\n", fieldName, allowedBlocked(*field)) - } else { - fmt.Printf("manageips\t%s\n", allowedBlocked(network.Settings.AllowManagedIPs)) - fmt.Printf("manageroutes\t%s\n", allowedBlocked(network.Settings.AllowManagedRoutes)) - fmt.Printf("globalips\t%s\n", allowedBlocked(network.Settings.AllowGlobalIPs)) - fmt.Printf("globalroutes\t%s\n", allowedBlocked(network.Settings.AllowGlobalRoutes)) - fmt.Printf("defaultroute\t%s\n", allowedBlocked(network.Settings.AllowDefaultRouteOverride)) - } - - } - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/peer.go b/attic/go/cmd/zerotier/cli/peer.go deleted file mode 100644 index bac76517d..000000000 --- a/attic/go/cmd/zerotier/cli/peer.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "strings" - "zerotier/pkg/zerotier" -) - -func listPeers(basePath, authToken string, jsonOutput bool, rootsOnly bool) int { - var peers []zerotier.Peer - apiGet(basePath, authToken, "/peer", &peers) - - if rootsOnly { - roots := make([]zerotier.Peer, 0, len(peers)) - for i := range peers { - if peers[i].Root { - roots = append(roots, peers[i]) - } - } - peers = roots - } - - if jsonOutput { - fmt.Println(jsonDump(&peers)) - } else { - fmt.Printf("
\n") - for _, peer := range peers { - root := "" - if peer.Root { - root = " *" - } - - var paths strings.Builder - if len(peer.Paths) > 0 { - if paths.Len() > 0 { - paths.WriteRune(' ') - } - paths.WriteString(peer.Paths[0].Endpoint.String()) - } else { - paths.WriteString("(relayed)") - } - - fmt.Printf("%.10x %-7s %-6s %-9d %s\n", - uint64(peer.Address), - fmt.Sprintf("%d.%d.%d", peer.Version[0], peer.Version[1], peer.Version[2]), - root, - peer.Latency, - paths.String()) - } - } - - return 0 -} - -func Peer(basePath string, authTokenGenerator func() string, args []string, jsonOutput bool) int { - if len(args) < 1 { - Help() - return 1 - } - - authToken := authTokenGenerator() - - //var addr zerotier.Address - if isValidAddress(args[0]) { - //addr, _ = zerotier.NewAddressFromString(args[0]) - args = args[1:] - if len(args) < 1 { - Help() - return 1 - } - } - - switch args[0] { - - case "list": - return listPeers(basePath, authToken, jsonOutput, false) - case "listroots": - return listPeers(basePath, authToken, jsonOutput, true) - - case "show": - - case "try": - - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/service.go b/attic/go/cmd/zerotier/cli/service.go deleted file mode 100644 index b5a009ace..000000000 --- a/attic/go/cmd/zerotier/cli/service.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "io/ioutil" - "os" - "os/signal" - "path" - "strconv" - "syscall" - - "zerotier/pkg/zerotier" -) - -func Service(basePath string, args []string) int { - if len(args) > 0 { - Help() - return 1 - } - - pidPath := path.Join(basePath, "zerotier.pid") - _ = ioutil.WriteFile(pidPath, []byte(strconv.FormatInt(int64(os.Getpid()), 10)), 0644) - - node, err := zerotier.NewNode(basePath) - if err != nil { - fmt.Println("FATAL: error initializing node: " + err.Error()) - } else { - osSignalChannel := make(chan os.Signal, 2) - signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT) - <-osSignalChannel - node.Close() - } - - _ = os.Remove(pidPath) - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/set.go b/attic/go/cmd/zerotier/cli/set.go deleted file mode 100644 index 4b4fe1d24..000000000 --- a/attic/go/cmd/zerotier/cli/set.go +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 - -func Set(basePath string, authTokenGenerator func() string, args []string) int { - return 0 -} diff --git a/attic/go/cmd/zerotier/cli/status.go b/attic/go/cmd/zerotier/cli/status.go deleted file mode 100644 index 9101c5e0f..000000000 --- a/attic/go/cmd/zerotier/cli/status.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 ( - "fmt" - "zerotier/pkg/zerotier" -) - -func Status(basePath string, authTokenGenerator func() string, args []string, jsonOutput bool) int { - var status zerotier.APIStatus - apiGet(basePath, authTokenGenerator(), "/status", &status) - - if jsonOutput { - fmt.Println(jsonDump(&status)) - } else { - online := "ONLINE" - if !status.Online { - online = "OFFLINE" - } - fmt.Printf("%.10x: %s %s\n", uint64(status.Address), online, status.Version) - fmt.Printf("\tidentity:\t%s\n", status.Identity.String()) - if status.Config.Settings.SecondaryPort > 0 && status.Config.Settings.SecondaryPort < 65536 { - fmt.Printf("\tports:\t%d %d\n", status.Config.Settings.PrimaryPort, status.Config.Settings.SecondaryPort) - } else { - fmt.Printf("\tports:\t%d\n", status.Config.Settings.PrimaryPort) - } - fmt.Printf("\tport mapping (uPnP/NAT-PMP):\t%s\n", enabledDisabled(status.Config.Settings.PortMapping)) - fmt.Printf("\tblacklisted interface prefixes:\t") - for i, bl := range status.Config.Settings.InterfacePrefixBlacklist { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(bl) - } - fmt.Printf("\n\texplicit external addresses: ") - for i, ea := range status.Config.Settings.ExplicitAddresses { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(ea.String()) - } - fmt.Printf("\n\tsystem interface addresses: ") - for i, a := range status.InterfaceAddresses { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(a.String()) - } - fmt.Printf("\n\tmapped external addresses: ") - for i, a := range status.MappedExternalAddresses { - if i > 0 { - fmt.Print(" ") - } - fmt.Print(a.String()) - } - fmt.Printf("\n") - } - - return 0 -} diff --git a/attic/go/cmd/zerotier/zerotier.go b/attic/go/cmd/zerotier/zerotier.go deleted file mode 100644 index 4a81d295c..000000000 --- a/attic/go/cmd/zerotier/zerotier.go +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 main - -import ( - "flag" - "fmt" - "io/ioutil" - "os" - "path" - "runtime" - "runtime/debug" - "strings" - "time" - - "zerotier/cmd/zerotier/cli" - "zerotier/pkg/zerotier" -) - -// authToken returns a function that reads the authorization token if needed. -// If the authorization token can't be read, the function terminates the program with a fatal error. -func authToken(basePath, tflag, tTflag string) func () string { - savedAuthToken := new(string) - return func() string { - authToken := *savedAuthToken - if len(authToken) > 0 { - return authToken - } - - if len(tflag) > 0 { - at, err := ioutil.ReadFile(tflag) - if err != nil || len(at) == 0 { - fmt.Println("FATAL: unable to read local service API authorization token from " + tflag) - os.Exit(1) - return "" - } - authToken = string(at) - } else if len(tTflag) > 0 { - authToken = tTflag - } else { - var authTokenPaths []string - authTokenPaths = append(authTokenPaths, path.Join(basePath, "authtoken.secret")) - userHome, _ := os.UserHomeDir() - if len(userHome) > 0 { - if runtime.GOOS == "darwin" { - authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "authtoken.secret")) - authTokenPaths = append(authTokenPaths, path.Join(userHome, "Library", "Application Support", "ZeroTier", "One", "authtoken.secret")) - } - authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zerotierauth")) - authTokenPaths = append(authTokenPaths, path.Join(userHome, ".zeroTierOneAuthToken")) - } - - for _, p := range authTokenPaths { - tmp, _ := ioutil.ReadFile(p) - if len(tmp) > 0 { - authToken = string(tmp) - break - } - } - - if len(authToken) == 0 { - fmt.Println("FATAL: unable to read local service API authorization token from any of:") - for _, p := range authTokenPaths { - fmt.Println(" " + p) - } - os.Exit(1) - return "" - } - } - - authToken = strings.TrimSpace(authToken) - if len(authToken) == 0 { - fmt.Println("FATAL: unable to read API authorization token from command line or any filesystem location.") - os.Exit(1) - return "" - } - - *savedAuthToken = authToken - return authToken - } -} - -func main() { - // Reduce Go's thread and memory footprint. This would slow things down if the Go code - // were doing a lot, but it's not. It just manages the core and is not directly involved - // in pushing a lot of packets around. If that ever changes this should be adjusted. - runtime.GOMAXPROCS(1) - debug.SetGCPercent(10) - - globalOpts := flag.NewFlagSet("global", flag.ContinueOnError) - hflag := globalOpts.Bool("h", false, "") // support -h to be canonical with other Unix utilities - jflag := globalOpts.Bool("j", false, "") - pflag := globalOpts.String("p", "", "") - tflag := globalOpts.String("t", "", "") - tTflag := globalOpts.String("T", "", "") - err := globalOpts.Parse(os.Args[1:]) - if err != nil { - cli.Help() - os.Exit(1) - return - } - args := globalOpts.Args() - if len(args) < 1 || *hflag { - cli.Help() - os.Exit(0) - return - } - var cmdArgs []string - if len(args) > 1 { - cmdArgs = args[1:] - } - basePath := zerotier.PlatformDefaultHomePath - if len(*pflag) > 0 { - basePath = *pflag - } - - exitCode := 0 - switch args[0] { - default: - cli.Help() - exitCode = 1 - case "help": - cli.Help() - case "version": - fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) - case "now": - if len(args) > 2 { - cli.Help() - exitCode = 1 - } else if len(args) == 2 { - d, err := time.ParseDuration(args[1]) - if err == nil { - fmt.Printf("%d\n", zerotier.TimeMs() + d.Milliseconds()) - } else { - fmt.Printf("FATAL: invalid duration \"%s\": %s\n", args[1], err.Error()) - exitCode = 1 - } - } else { - fmt.Printf("%d\n", zerotier.TimeMs()) - } - - case "service": - exitCode = cli.Service(basePath, cmdArgs) - case "status", "info": - exitCode = cli.Status(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) - case "join": - exitCode = cli.Join(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) - case "leave": - exitCode = cli.Leave(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) - case "networks", "listnetworks": - exitCode = cli.Network(basePath, authToken(basePath, *tflag, *tTflag), []string{"list"}, *jflag) - case "network": - exitCode = cli.Network(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) - case "peers", "listpeers": - exitCode = cli.Peer(basePath, authToken(basePath, *tflag, *tTflag), []string{"list"}, *jflag) - case "peer": - exitCode = cli.Peer(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) - case "controller": - exitCode = cli.Controller(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) - case "set": - exitCode = cli.Set(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs) - case "identity": - exitCode = cli.Identity(cmdArgs) - case "locator": - exitCode = cli.Locator(cmdArgs) - case "certs", "listcerts", "lscerts": // same as "cert show" with no specific serial to show - exitCode = cli.Cert(basePath, authToken(basePath, *tflag, *tTflag), []string{"show"}, *jflag) - case "cert": - exitCode = cli.Cert(basePath, authToken(basePath, *tflag, *tTflag), cmdArgs, *jflag) - } - os.Exit(exitCode) -} diff --git a/attic/go/cmd/zt_service_tests/certificate.go b/attic/go/cmd/zt_service_tests/certificate.go deleted file mode 100644 index e60654e3f..000000000 --- a/attic/go/cmd/zt_service_tests/certificate.go +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 main - -import ( - "bytes" - "fmt" - "zerotier/pkg/zerotier" -) - -func TestCertificate() bool { - id, err := zerotier.NewIdentityFromString("8e4df28b72:0:ac3d46abe0c21f3cfe7a6c8d6a85cfcffcb82fbd55af6a4d6350657c68200843fa2e16f9418bbd9702cae365f2af5fb4c420908b803a681d4daef6114d78a2d7:bd8dd6e4ce7022d2f812797a80c6ee8ad180dc4ebf301dec8b06d1be08832bddd63a2f1cfa7b2c504474c75bdc8898ba476ef92e8e2d0509f8441985171ff16e") - if err != nil { - fmt.Printf("FATAL: error deserializing test identity: %s\n", err.Error()) - return false - } - - uniqueId, uniqueIdPrivate, err := zerotier.NewCertificateSubjectUniqueId(zerotier.CertificateUniqueIdTypeNistP384) - if err != nil { - fmt.Printf("FATAL: error generating unique ID: %s", err.Error()) - return false - } - - var c zerotier.Certificate - - c.SerialNo = make([]byte, 48) - for i := 0; i < 48; i++ { - c.SerialNo[i] = byte(i) - } - c.Flags = 1234 - c.Timestamp = 5678 - c.Validity[0] = 1010 - c.Validity[1] = 2020 - - c.Subject.Timestamp = 31337 - c.Subject.Identities = append(c.Subject.Identities, zerotier.CertificateIdentity{ - Identity: id, - Locator: nil, - }) - c.Subject.Networks = append(c.Subject.Networks, zerotier.CertificateNetwork{ - ID: 1111, - Controller: zerotier.Fingerprint{ - Address: zerotier.Address(2222), - Hash: c.SerialNo, - }, - }) - c.Subject.Certificates = append(c.Subject.Certificates, c.SerialNo) - c.Subject.UpdateURLs = append(c.Subject.UpdateURLs, "https://www.zerotier.com/asdfasdf") - c.Subject.Name.SerialNo = "a" - c.Subject.Name.CommonName = "b" - c.Subject.Name.StreetAddress = "c" - c.Subject.Name.Locality = "d" - c.Subject.Name.Province = "e" - c.Subject.Name.PostalCode = "f" - c.Subject.Name.Country = "g" - c.Subject.Name.Organization = "h" - c.Subject.Name.Unit = "i" - c.Subject.Name.Email = "j" - c.Subject.Name.URL = "k" - c.Subject.Name.Host = "l" - c.Subject.UniqueID = uniqueId - - c.Issuer = id - c.IssuerName.SerialNo = "m" - c.IssuerName.CommonName = "n" - c.IssuerName.StreetAddress = "o" - c.IssuerName.Locality = "p" - c.IssuerName.Province = "q" - c.IssuerName.PostalCode = "r" - c.IssuerName.Country = "s" - c.IssuerName.Organization = "t" - c.IssuerName.Unit = "u" - c.IssuerName.Email = "v" - c.IssuerName.URL = "w" - c.IssuerName.Host = "x" - - c.ExtendedAttributes = c.SerialNo - c.MaxPathLength = 9999 - c.Signature = []byte("qwerty") - - fmt.Printf("Checking certificate marshal/unmarshal (10000 tests)... ") - for k := 0; k < 10000; k++ { - cb, err := c.Marshal() - if err != nil { - fmt.Printf("marshal FAILED (%s)\n", err.Error()) - return false - } - c2, err := zerotier.NewCertificateFromBytes(cb, false) - if err != nil { - fmt.Printf("unmarshal FAILED (%s)\n", err.Error()) - return false - } - cb2, err := c2.Marshal() - if err != nil { - fmt.Printf("second marshal FAILED (%s)\n", err.Error()) - return false - } - if !bytes.Equal(cb, cb2) { - fmt.Printf("FAILED (results not equal)\n") - return false - } - } - fmt.Println("OK") - - fmt.Printf("Checking certificate CSR sign/verify (100 tests)... ") - for k := 0; k < 100; k++ { - csr, err := zerotier.NewCertificateCSR(&c.Subject, uniqueId, uniqueIdPrivate) - if err != nil { - fmt.Printf("CSR generate FAILED (%s)\n", err.Error()) - return false - } - //fmt.Printf("CSR size: %d ", len(csr)) - csr2, err := zerotier.NewCertificateFromBytes(csr, false) - if err != nil { - fmt.Printf("CSR decode FAILED (%s)\n", err.Error()) - return false - } - signedCert, err := csr2.Sign(id) - if err != nil { - fmt.Printf("CSR sign FAILED (%s)\n", err.Error()) - return false - } - if len(signedCert.Signature) == 0 { - fmt.Println("CSR sign FAILED (no signature found)", err.Error()) - return false - } - } - fmt.Println("OK") - - return true -} diff --git a/attic/go/cmd/zt_service_tests/locator.go b/attic/go/cmd/zt_service_tests/locator.go deleted file mode 100644 index 3f78b0712..000000000 --- a/attic/go/cmd/zt_service_tests/locator.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 main - -import ( - "bytes" - "fmt" - "zerotier/pkg/zerotier" -) - -func TestLocator() bool { - fmt.Printf("Creating Endpoint instances... ") - ep0, err := zerotier.NewEndpointFromString("1.1.1.1/1") - if err != nil { - fmt.Printf("IPv4 FAILED (%s)\n",err.Error()) - return false - } - ep1, err := zerotier.NewEndpointFromString("2600:1901:0:4006::1234/2") - if err != nil { - fmt.Printf("IPv6 FAILED (%s)\n",err.Error()) - return false - } - eps := []*zerotier.Endpoint{ep0, ep1} - fmt.Printf("OK\n") - - fmt.Printf("Creating signing Identity... ") - signer, err := zerotier.NewIdentity(zerotier.IdentityTypeP384) - if err != nil { - fmt.Printf("FAILED (%s)\n", err.Error()) - return false - } - fmt.Printf("OK %s\n",signer.String()) - - fmt.Printf("Creating Locator instance... ") - loc, err := zerotier.NewLocator(zerotier.TimeMs(), eps, signer) - if err != nil { - fmt.Printf("FAILED (%s)\n",err.Error()) - return false - } - locStr := loc.String() - locBytes := loc.Bytes() - fmt.Printf("OK (%d bytes)\n",len(locBytes)) - - fmt.Printf("Testing Locator Validate()... ") - if !loc.Validate(signer) { - fmt.Printf("FAILED (should have validated)\n") - return false - } - fmt.Printf("OK\n") - - fmt.Printf("Testing Locator marshal/unmarshal... ") - loc2, err := zerotier.NewLocatorFromString(locStr) - if err != nil { - fmt.Printf("FAILED (%s)\n",err.Error()) - return false - } - if !bytes.Equal(locBytes, loc2.Bytes()) { - fmt.Printf("FAILED (not equal)\n") - return false - } - loc2, err = zerotier.NewLocatorFromBytes(locBytes) - if err != nil { - fmt.Printf("FAILED (%s)\n",err.Error()) - return false - } - if !bytes.Equal(locBytes, loc2.Bytes()) { - fmt.Printf("FAILED (not equal)\n") - return false - } - fmt.Printf("OK\n") - - return true -} diff --git a/attic/go/cmd/zt_service_tests/zt_service_tests.go b/attic/go/cmd/zt_service_tests/zt_service_tests.go deleted file mode 100644 index dfe63c8a7..000000000 --- a/attic/go/cmd/zt_service_tests/zt_service_tests.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( - "os" - "runtime" - "runtime/debug" -) - -const numToRun = 10000 - -func main() { - runtime.GOMAXPROCS(1) - debug.SetGCPercent(10) - - for k:=0;k> 32) - b[1] = byte(a >> 24) - b[2] = byte(a >> 16) - b[3] = byte(a >> 8) - b[4] = byte(a) -} - -// IsReserved returns true if this address is reserved and therefore is not valid for a real node. -func (a Address) IsReserved() bool { return a == 0 || (a>>32) == 0xff } - -// String returns this address's 10-digit hex identifier. -func (a Address) String() string { - return fmt.Sprintf("%.10x", uint64(a)) -} - -// MarshalJSON marshals this Address as a string -func (a Address) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("\"%.10x\"", uint64(a))), nil -} - -// UnmarshalJSON unmarshals this Address from a string -func (a *Address) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - tmp, err := NewAddressFromString(s) - *a = tmp - return err -} diff --git a/attic/go/pkg/zerotier/api.go b/attic/go/pkg/zerotier/api.go deleted file mode 100644 index b5d4a74dd..000000000 --- a/attic/go/pkg/zerotier/api.go +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "bytes" - secrand "crypto/rand" - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "net" - "net/http" - "path" - "runtime" - "strconv" - "strings" - "time" - - "github.com/hectane/go-acl" -) - -// APISocketName is the default socket name for accessing the API -const APISocketName = "apisocket" - -// APIGet makes a query to the API via a Unix domain or windows pipe socket -func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, int64, error) { - client, err := createNamedSocketHTTPClient(basePath, socketName) - if err != nil { - return http.StatusTeapot, 0, err - } - req, err := http.NewRequest("GET", "http://socket"+queryPath, nil) - if err != nil { - return http.StatusTeapot, 0, err - } - req.Header.Add("Authorization", "bearer "+authToken) - resp, err := client.Do(req) - if err != nil { - return http.StatusTeapot, 0, err - } - err = json.NewDecoder(resp.Body).Decode(obj) - ts := resp.Header.Get("X-ZT-Clock") - t := int64(0) - if len(ts) > 0 { - t, _ = strconv.ParseInt(ts, 10, 64) - } - return resp.StatusCode, t, err -} - -// APIPost posts a JSON object to the API via a Unix domain or windows pipe socket and reads a response -func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, int64, error) { - client, err := createNamedSocketHTTPClient(basePath, socketName) - if err != nil { - return http.StatusTeapot, 0, err - } - var data []byte - if post != nil { - data, err = json.Marshal(post) - if err != nil { - return http.StatusTeapot, 0, err - } - } else { - data = []byte("null") - } - req, err := http.NewRequest("POST", "http://socket"+queryPath, bytes.NewReader(data)) - if err != nil { - return http.StatusTeapot, 0, err - } - req.Header.Add("Content-Type", "application/json") - req.Header.Add("Authorization", "bearer "+authToken) - resp, err := client.Do(req) - if err != nil { - return http.StatusTeapot, 0, err - } - ts := resp.Header.Get("X-ZT-Clock") - t := int64(0) - if len(ts) > 0 { - t, _ = strconv.ParseInt(ts, 10, 64) - } - if result != nil { - err = json.NewDecoder(resp.Body).Decode(result) - return resp.StatusCode, t, err - } - return resp.StatusCode, t, nil -} - -// APIDelete posts DELETE to a path and fills result with the outcome (if any) if result is non-nil -func APIDelete(basePath, socketName, authToken, queryPath string, result interface{}) (int, int64, error) { - client, err := createNamedSocketHTTPClient(basePath, socketName) - if err != nil { - return http.StatusTeapot, 0, err - } - req, err := http.NewRequest("DELETE", "http://socket"+queryPath, nil) - if err != nil { - return http.StatusTeapot, 0, err - } - req.Header.Add("Authorization", "bearer "+authToken) - resp, err := client.Do(req) - if err != nil { - return http.StatusTeapot, 0, err - } - ts := resp.Header.Get("X-ZT-Clock") - t := int64(0) - if len(ts) > 0 { - t, _ = strconv.ParseInt(ts, 10, 64) - } - if result != nil { - err = json.NewDecoder(resp.Body).Decode(result) - return resp.StatusCode, t, err - } - return resp.StatusCode, t, nil -} - -// APIStatus is the object returned by API status inquiries -type APIStatus struct { - Address Address `json:"address"` - Clock int64 `json:"clock"` - StartupTime int64 `json:"startupTime"` - Config *LocalConfig `json:"config"` - Online bool `json:"online"` - PeerCount int `json:"peerCount"` - PathCount int `json:"pathCount"` - Identity *Identity `json:"identity"` - InterfaceAddresses []net.IP `json:"localInterfaceAddresses,omitempty"` - MappedExternalAddresses []*InetAddress `json:"mappedExternalAddresses,omitempty"` - Version string `json:"version"` - VersionMajor int `json:"versionMajor"` - VersionMinor int `json:"versionMinor"` - VersionRevision int `json:"versionRevision"` - VersionBuild int `json:"versionBuild"` - OS string `json:"os"` - Architecture string `json:"architecture"` - Concurrency int `json:"concurrency"` - Runtime string `json:"runtimeVersion"` -} - -// APINetwork is the object returned by API network inquiries -type APINetwork struct { - ID NetworkID `json:"id"` - Config NetworkConfig `json:"config"` - Settings *NetworkLocalSettings `json:"settings,omitempty"` - ControllerFingerprint *Fingerprint `json:"controllerFingerprint,omitempty"` - MulticastSubscriptions []*MulticastGroup `json:"multicastSubscriptions,omitempty"` - PortType string `json:"portType"` - PortName string `json:"portName"` - PortEnabled bool `json:"portEnabled"` - PortErrorCode int `json:"portErrorCode"` - PortError string `json:"portError"` -} - -func apiNetworkFromNetwork(n *Network) *APINetwork { - var nn APINetwork - nn.ID = n.ID() - nn.Config = n.Config() - ls := n.LocalSettings() - nn.Settings = &ls - nn.MulticastSubscriptions = n.MulticastSubscriptions() - nn.PortType = n.Tap().Type() - nn.PortName = n.Tap().DeviceName() - nn.PortEnabled = n.Tap().Enabled() - ec, errStr := n.Tap().Error() - nn.PortErrorCode = ec - nn.PortError = errStr - return &nn -} - -func apiSetStandardHeaders(out http.ResponseWriter) { - h := out.Header() - h.Set("Cache-Control", "no-cache, no-store, must-revalidate") - h.Set("Expires", "0") - h.Set("Pragma", "no-cache") - t := time.Now().UTC() - h.Set("Date", t.Format(time.RFC1123)) - h.Set("X-ZT-Clock", strconv.FormatInt(t.UnixNano()/int64(1000000), 10)) -} - -func apiSendObj(out http.ResponseWriter, req *http.Request, httpStatusCode int, obj interface{}) error { - h := out.Header() - h.Set("Content-Type", "application/json") - if req.Method == http.MethodHead { - out.WriteHeader(httpStatusCode) - return nil - } - var j []byte - var err error - if obj != nil { - j, err = json.Marshal(obj) - if err != nil { - return err - } - } - out.WriteHeader(httpStatusCode) - _, err = out.Write(j) - return err -} - -func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (err error) { - err = json.NewDecoder(req.Body).Decode(&dest) - if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid JSON: " + err.Error()}) - } - return -} - -func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool { - ah := req.Header.Get("Authorization") - if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) { - return true - } - - ah = req.Header.Get("X-ZT1-Auth") - if len(ah) > 0 && strings.TrimSpace(ah) == token { - return true - } - - _ = apiSendObj(out, req, http.StatusUnauthorized, &APIErr{"authorization token not found or incorrect (checked X-ZT1-Auth and Authorization headers)"}) - return false -} - -// createAPIServer creates and starts an HTTP server for a given node -func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, error) { - // Read authorization token, automatically generating one if it's missing - var authToken string - authTokenFile := path.Join(basePath, "authtoken.secret") - authTokenB, err := ioutil.ReadFile(authTokenFile) - if err != nil { - var atb [20]byte - _, err = secrand.Read(atb[:]) - if err != nil { - return nil, nil, err - } - for i := 0; i < 20; i++ { - atb[i] = "abcdefghijklmnopqrstuvwxyz0123456789"[atb[i]%36] - } - err = ioutil.WriteFile(authTokenFile, atb[:], 0600) - if err != nil { - return nil, nil, err - } - _ = acl.Chmod(authTokenFile, 0600) - authToken = string(atb[:]) - } else { - authToken = strings.TrimSpace(string(authTokenB)) - } - - smux := http.NewServeMux() - - // ----------------------------------------------------------------------------------------------------------------- - - smux.HandleFunc("/status", func(out http.ResponseWriter, req *http.Request) { - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - if req.Method == http.MethodGet || req.Method == http.MethodHead { - pathCount := 0 - peers := node.Peers() - for _, p := range peers { - pathCount += len(p.Paths) - } - _ = apiSendObj(out, req, http.StatusOK, &APIStatus{ - Address: node.Address(), - Clock: TimeMs(), - StartupTime: node.startupTime, - Config: node.LocalConfig(), - Online: node.Online(), - PeerCount: len(peers), - PathCount: pathCount, - Identity: node.Identity(), - InterfaceAddresses: node.LocalInterfaceAddresses(), - MappedExternalAddresses: nil, - Version: fmt.Sprintf("%d.%d.%d", CoreVersionMajor, CoreVersionMinor, CoreVersionRevision), - VersionMajor: CoreVersionMajor, - VersionMinor: CoreVersionMinor, - VersionRevision: CoreVersionRevision, - VersionBuild: CoreVersionBuild, - OS: runtime.GOOS, - Architecture: runtime.GOARCH, - Concurrency: runtime.NumCPU(), - Runtime: runtime.Version(), - }) - } else { - out.Header().Set("Allow", "GET, HEAD") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"/status is read-only"}) - } - }) - - // ----------------------------------------------------------------------------------------------------------------- - - smux.HandleFunc("/config", func(out http.ResponseWriter, req *http.Request) { - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - if req.Method == http.MethodPost || req.Method == http.MethodPut { - var c LocalConfig - if apiReadObj(out, req, &c) == nil { - _, err := node.SetLocalConfig(&c) - if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"error applying local config: " + err.Error()}) - } else { - lc := node.LocalConfig() - _ = apiSendObj(out, req, http.StatusOK, &lc) - } - } - } else if req.Method == http.MethodGet || req.Method == http.MethodHead { - _ = apiSendObj(out, req, http.StatusOK, node.LocalConfig()) - } else { - out.Header().Set("Allow", "GET, HEAD, PUT, POST") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method: " + req.Method}) - } - }) - - // ----------------------------------------------------------------------------------------------------------------- - - smux.HandleFunc("/peer/", func(out http.ResponseWriter, req *http.Request) { - var err error - - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - var queriedStr string - var queriedID Address - var queriedFP *Fingerprint - if len(req.URL.Path) > 6 { - queriedStr = req.URL.Path[6:] - if len(queriedStr) == AddressStringLength { - queriedID, err = NewAddressFromString(queriedStr) - if err != nil { - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) - return - } - } else { - queriedFP, err = NewFingerprintFromString(queriedStr) - if err != nil { - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) - return - } - } - } - - var peer *Peer - peers := node.Peers() - if queriedFP != nil { - for _, p := range peers { - if p.Fingerprint.Equals(queriedFP) { - peer = p - break - } - } - } else if queriedID != 0 { - for _, p := range peers { - if p.Address == queriedID { - peer = p - break - } - } - } - - if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodPost || req.Method == http.MethodPut { - if peer != nil { - _ = apiSendObj(out, req, http.StatusOK, peer) - } else if len(queriedStr) > 0 { - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) - } else { - _ = apiSendObj(out, req, http.StatusOK, peers) - } - } else { - out.Header().Set("Allow", "GET, HEAD") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method"}) - } - }) - - // ----------------------------------------------------------------------------------------------------------------- - - smux.HandleFunc("/network/", func(out http.ResponseWriter, req *http.Request) { - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"caught unexpected error in request handler"}) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - var queriedID NetworkID - if len(req.URL.Path) > 9 { - var err error - queriedID, err = NewNetworkIDFromString(req.URL.Path[9:]) - if err != nil { - _ = apiSendObj(out, req, http.StatusNotFound, nil) - return - } - } - - if req.Method == http.MethodDelete { - - if queriedID == 0 { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only specific networks can be deleted"}) - return - } - networks := node.Networks() - for _, nw := range networks { - if nw.id == queriedID { - _ = node.Leave(queriedID) - _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(nw)) - return - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"}) - - } else if req.Method == http.MethodPost || req.Method == http.MethodPut { - - if queriedID == 0 { - _ = apiSendObj(out, req, http.StatusBadRequest, nil) - return - } - var nw APINetwork - if apiReadObj(out, req, &nw) == nil { - n := node.Network(nw.ID) - if n == nil { - if nw.ControllerFingerprint != nil && nw.ControllerFingerprint.Address != nw.ID.Controller() { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"fingerprint's address does not match what should be the controller's address"}) - } else { - n, err := node.Join(nw.ID, nw.ControllerFingerprint, nw.Settings, nil) - if err != nil { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"only individual networks can be added or modified with POST/PUT"}) - } else { - _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n)) - } - } - } else { - if nw.Settings != nil { - n.SetLocalSettings(nw.Settings) - } - _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(n)) - } - } - - } else if req.Method == http.MethodGet || req.Method == http.MethodHead { - - networks := node.Networks() - if queriedID == 0 { // no queried ID lists all networks - nws := make([]*APINetwork, 0, len(networks)) - for _, nw := range networks { - nws = append(nws, apiNetworkFromNetwork(nw)) - } - _ = apiSendObj(out, req, http.StatusOK, nws) - return - } - for _, nw := range networks { - if nw.ID() == queriedID { - _ = apiSendObj(out, req, http.StatusOK, apiNetworkFromNetwork(nw)) - return - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"network not found"}) - - } else { - out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method}) - } - }) - - // ----------------------------------------------------------------------------------------------------------------- - - smux.HandleFunc("/cert/", func(out http.ResponseWriter, req *http.Request) { - defer func() { - e := recover() - if e != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, nil) - } - }() - - if !apiCheckAuth(out, req, authToken) { - return - } - apiSetStandardHeaders(out) - - var queriedSerialNo []byte - if len(req.URL.Path) > 6 { - b, err := base64.URLEncoding.DecodeString(req.URL.Path[6:]) - if err != nil || len(b) != CertificateSerialNoSize { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"invalid base64 serial number in certificate path"}) - return - } - queriedSerialNo = b - } - - if req.Method == http.MethodGet || req.Method == http.MethodHead { - - certs, err := node.ListCertificates() - if err != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"unexpected error listing certificates"}) - return - } - - if len(queriedSerialNo) == CertificateSerialNoSize { - for _, c := range certs { - if bytes.Equal(c.Certificate.SerialNo, queriedSerialNo) { - _ = apiSendObj(out, req, http.StatusOK, c) - break - } - } - } else { - _ = apiSendObj(out, req, http.StatusOK, certs) - } - - } else if req.Method == http.MethodPost || req.Method == http.MethodPut { - - var lc LocalCertificate - if apiReadObj(out, req, &lc) == nil { - if lc.Certificate == nil { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"missing certificate"}) - return - } - } - - if len(queriedSerialNo) == CertificateSerialNoSize && !bytes.Equal(queriedSerialNo, lc.Certificate.SerialNo) { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"certificate serial does not match serial in path"}) - return - } - - err := node.AddCertificate(lc.Certificate, lc.LocalTrust) - if err == nil { - _ = apiSendObj(out, req, http.StatusOK, lc) - } else { - _ = apiSendObj(out, req, http.StatusBadRequest, &APIErr{"certificate rejected: " + err.Error()}) - } - - } else if req.Method == http.MethodDelete { - - if len(queriedSerialNo) == CertificateSerialNoSize { - certs, err := node.ListCertificates() - if err != nil { - _ = apiSendObj(out, req, http.StatusInternalServerError, &APIErr{"unexpected error"}) - return - } - for _, c := range certs { - if bytes.Equal(c.Certificate.SerialNo, queriedSerialNo) { - _ = node.DeleteCertificate(queriedSerialNo) - _ = apiSendObj(out, req, http.StatusOK, c) - return - } - } - } - _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"certificate not found"}) - - } else { - out.Header().Set("Allow", "GET, HEAD, PUT, POST, DELETE") - _ = apiSendObj(out, req, http.StatusMethodNotAllowed, &APIErr{"unsupported method " + req.Method}) - } - }) - - // ----------------------------------------------------------------------------------------------------------------- - - listener, err := createNamedSocketListener(basePath, APISocketName) - if err != nil { - return nil, nil, err - } - httpServer := &http.Server{ - MaxHeaderBytes: 4096, - Handler: smux, - IdleTimeout: 10 * time.Second, - ReadTimeout: 10 * time.Second, - WriteTimeout: 600 * time.Second, - } - httpServer.SetKeepAlivesEnabled(true) - go func() { - _ = httpServer.Serve(listener) - _ = listener.Close() - }() - - var tcpHttpServer *http.Server - tcpBindAddr := node.LocalConfig().Settings.APITCPBindAddress - if tcpBindAddr != nil { - tcpListener, err := net.ListenTCP("tcp", &net.TCPAddr{ - IP: tcpBindAddr.IP, - Port: tcpBindAddr.Port, - }) - if err != nil { - node.infoLog.Printf("ERROR: unable to start API HTTP server at TCP bind address %s: %s (named socket listener startd, continuing anyway)", tcpBindAddr.String(), err.Error()) - } else { - tcpHttpServer = &http.Server{ - MaxHeaderBytes: 4096, - Handler: smux, - IdleTimeout: 10 * time.Second, - ReadTimeout: 10 * time.Second, - WriteTimeout: 600 * time.Second, - } - tcpHttpServer.SetKeepAlivesEnabled(true) - go func() { - _ = tcpHttpServer.Serve(tcpListener) - _ = tcpListener.Close() - }() - } - } - - return httpServer, tcpHttpServer, nil -} diff --git a/attic/go/pkg/zerotier/base32blob.go b/attic/go/pkg/zerotier/base32blob.go deleted file mode 100644 index b70f6da85..000000000 --- a/attic/go/pkg/zerotier/base32blob.go +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "encoding/json" - "strings" -) - -// Base32Blob is a byte array that JSON serializes to a Base32 string. -type Base32Blob []byte - -// MarshalJSON returns this blob marshaled as a byte array or a string. -func (b *Base32Blob) MarshalJSON() ([]byte, error) { - return []byte("\""+Base32.EncodeToString(*b)+"\""), nil -} - -// UnmarshalJSON unmarshals this blob from a JSON array or string. -func (b *Base32Blob) UnmarshalJSON(j []byte) error { - var b32 string - err := json.Unmarshal(j, &b32) - if err != nil { - return err - } - *b, err = Base32.DecodeString(strings.TrimSpace(b32)) - return err -} diff --git a/attic/go/pkg/zerotier/certificate.go b/attic/go/pkg/zerotier/certificate.go deleted file mode 100644 index a6466ed1e..000000000 --- a/attic/go/pkg/zerotier/certificate.go +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -// static inline void *_ZT_Certificate_clone2(uintptr_t p) { return (void *)ZT_Certificate_clone((const ZT_Certificate *)p); } -import "C" - -import ( - "encoding/json" - "fmt" - "unsafe" -) - -const ( - CertificateSerialNoSize = 48 - CertificateMaxStringLength = int(C.ZT_CERTIFICATE_MAX_STRING_LENGTH) - - CertificateLocalTrustFlagRootCA = int(C.ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) - CertificateLocalTrustFlagZeroTierRootSet = int(C.ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) - - CertificateUniqueIdTypeNistP384 = int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384) - CertificateUniqueIdTypeNistP384Size = int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE) - CertificateUniqueIdTypeNistP384PrivateSize = int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE) -) - -// CertificateName identifies a real-world entity that owns a subject or has signed a certificate. -type CertificateName struct { - SerialNo string `json:"serialNo,omitempty"` - CommonName string `json:"commonName,omitempty"` - StreetAddress string `json:"streetAddress,omitempty"` - Locality string `json:"locality,omitempty"` - Province string `json:"province,omitempty"` - PostalCode string `json:"postalCode,omitempty"` - Country string `json:"country,omitempty"` - Organization string `json:"organization,omitempty"` - Unit string `json:"unit,omitempty"` - Email string `json:"email,omitempty"` - URL string `json:"url,omitempty"` - Host string `json:"host,omitempty"` -} - -// CertificateIdentity bundles an identity with an optional locator. -type CertificateIdentity struct { - Identity *Identity `json:"identity,omitempty"` - Locator *Locator `json:"locator,omitempty"` -} - -// CertificateNetwork bundles a network ID with the fingerprint of its primary controller. -type CertificateNetwork struct { - ID uint64 `json:"id"` - Controller Fingerprint `json:"controller"` -} - -// CertificateSubject contains information about the subject of a certificate. -type CertificateSubject struct { - Timestamp int64 `json:"timestamp"` - Identities []CertificateIdentity `json:"identities,omitempty"` - Networks []CertificateNetwork `json:"networks,omitempty"` - Certificates []Base32Blob `json:"certificates,omitempty"` - UpdateURLs []string `json:"updateURLs,omitempty"` - Name CertificateName `json:"name"` - UniqueID Base32Blob `json:"uniqueId,omitempty"` - UniqueIDProofSignature Base32Blob `json:"uniqueIdProofSignature,omitempty"` -} - -// Certificate is a Go reflection of the C ZT_Certificate struct. -type Certificate struct { - SerialNo Base32Blob `json:"serialNo,omitempty"` - Flags uint64 `json:"flags"` - Timestamp int64 `json:"timestamp"` - Validity [2]int64 `json:"validity"` - Subject CertificateSubject `json:"subject"` - Issuer *Identity `json:"issuer,omitempty"` - IssuerName CertificateName `json:"issuerName"` - ExtendedAttributes Base32Blob `json:"extendedAttributes,omitempty"` - MaxPathLength uint `json:"maxPathLength,omitempty"` - CRL []Base32Blob `json:"crl,omitempty"` - Signature Base32Blob `json:"signature,omitempty"` -} - -// CertificateSubjectUniqueIDSecret bundles a certificate subject unique ID and its secret key. -type CertificateSubjectUniqueIDSecret struct { - UniqueID Base32Blob `json:"uniqueId,omitempty"` - UniqueIDSecret Base32Blob `json:"uniqueIdSecret,omitempty"` -} - -// LocalCertificate combines a certificate with its local trust flags. -type LocalCertificate struct { - Certificate *Certificate `json:"certificate,omitempty"` - LocalTrust uint `json:"localTrust"` -} - -func certificateErrorToError(cerr int) error { - switch cerr { - case C.ZT_CERTIFICATE_ERROR_NONE: - return nil - case C.ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT: - return ErrCertificateHaveNewerCert - case C.ZT_CERTIFICATE_ERROR_INVALID_FORMAT: - return ErrCertificateInvalidFormat - case C.ZT_CERTIFICATE_ERROR_INVALID_IDENTITY: - return ErrCertificateInvalidIdentity - case C.ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE: - return ErrCertificateInvalidPrimarySignature - case C.ZT_CERTIFICATE_ERROR_INVALID_CHAIN: - return ErrCertificateInvalidChain - case C.ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE: - return ErrCertificateInvalidComponentSignature - case C.ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF: - return ErrCertificateInvalidUniqueIDProof - case C.ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS: - return ErrCertificateMissingRequiredFields - case C.ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW: - return ErrCertificateOutOfValidTimeWindow - } - return ErrInternal -} - -// NewCertificateFromBytes decodes a certificate from an encoded byte string. -// Note that this is also used to decode a CSR. When used for a CSR only the -// Subject part of the certificate will contain anything and the rest will be -// blank. If 'verify' is true the certificate will also be verified. If using -// to decode a CSR this should be false as a CSR will not contain a full set -// of fields or a certificate signature. -func NewCertificateFromBytes(cert []byte, verify bool) (*Certificate, error) { - if len(cert) == 0 { - return nil, ErrInvalidParameter - } - var dec unsafe.Pointer - ver := C.int(0) - if verify { - ver = 1 - } - cerr := C.ZT_Certificate_decode((**C.ZT_Certificate)(unsafe.Pointer(&dec)), unsafe.Pointer(&cert[0]), C.int(len(cert)), ver) - if cerr != 0 { - return nil, certificateErrorToError(int(cerr)) - } - if dec == nil { - return nil, ErrInternal - } - defer C.ZT_Certificate_delete((*C.ZT_Certificate)(dec)) - - goCert := newCertificateFromCCertificate(dec) - if goCert == nil { - return nil, ErrInternal - } - return goCert, nil -} - -// newCertificateFromCCertificate translates a C ZT_Certificate into a Go Certificate. -func newCertificateFromCCertificate(ccptr unsafe.Pointer) *Certificate { - cc := (*C.ZT_Certificate)(ccptr) - c := new(Certificate) - - if cc == nil { - return c - } - - sn := (*[48]byte)(unsafe.Pointer(&cc.serialNo[0]))[:] - if !allZero(sn) { - var tmp [48]byte - copy(tmp[:], sn) - c.SerialNo = tmp[:] - } - c.Flags = uint64(cc.flags) - c.Timestamp = int64(cc.timestamp) - c.Validity[0] = int64(cc.validity[0]) - c.Validity[1] = int64(cc.validity[1]) - - c.Subject.Timestamp = int64(cc.subject.timestamp) - - for i := 0; i < int(cc.subject.identityCount); i++ { - cid := (*C.ZT_Certificate_Identity)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.identities)) + (uintptr(C.sizeof_ZT_Certificate_Identity) * uintptr(i)))) - if cid.identity == nil { - return nil - } - id, err := newIdentityFromCIdentity(cid.identity) - if err != nil { - return nil - } - var loc *Locator - if cid.locator != nil { - loc, err = newLocatorFromCLocator(cid.locator, false) - if err != nil { - return nil - } - } - c.Subject.Identities = append(c.Subject.Identities, CertificateIdentity{ - Identity: id, - Locator: loc, - }) - } - - for i := 0; i < int(cc.subject.networkCount); i++ { - cn := (*C.ZT_Certificate_Network)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.networks)) + (uintptr(C.sizeof_ZT_Certificate_Network) * uintptr(i)))) - fp := newFingerprintFromCFingerprint(&cn.controller) - if fp == nil { - return nil - } - c.Subject.Networks = append(c.Subject.Networks, CertificateNetwork{ - ID: uint64(cn.id), - Controller: *fp, - }) - } - - for i := 0; i < int(cc.subject.certificateCount); i++ { - csn := *((**[CertificateSerialNoSize]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.certificates)) + (uintptr(i) * pointerSize)))) - var tmp [CertificateSerialNoSize]byte - copy(tmp[:], csn[:]) - c.Subject.Certificates = append(c.Subject.Certificates, tmp[:]) - } - - for i := 0; i < int(cc.subject.updateURLCount); i++ { - curl := *((**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.subject.updateURLs)) + (uintptr(i) * pointerSize)))) - c.Subject.UpdateURLs = append(c.Subject.UpdateURLs, C.GoString(curl)) - } - - c.Subject.Name.SerialNo = C.GoString(&cc.subject.name.serialNo[0]) - c.Subject.Name.CommonName = C.GoString(&cc.subject.name.commonName[0]) - c.Subject.Name.Country = C.GoString(&cc.subject.name.country[0]) - c.Subject.Name.Organization = C.GoString(&cc.subject.name.organization[0]) - c.Subject.Name.Unit = C.GoString(&cc.subject.name.unit[0]) - c.Subject.Name.Locality = C.GoString(&cc.subject.name.locality[0]) - c.Subject.Name.Province = C.GoString(&cc.subject.name.province[0]) - c.Subject.Name.StreetAddress = C.GoString(&cc.subject.name.streetAddress[0]) - c.Subject.Name.PostalCode = C.GoString(&cc.subject.name.postalCode[0]) - c.Subject.Name.Email = C.GoString(&cc.subject.name.email[0]) - c.Subject.Name.URL = C.GoString(&cc.subject.name.url[0]) - c.Subject.Name.Host = C.GoString(&cc.subject.name.host[0]) - - if cc.subject.uniqueIdSize > 0 { - c.Subject.UniqueID = C.GoBytes(unsafe.Pointer(cc.subject.uniqueId), C.int(cc.subject.uniqueIdSize)) - if cc.subject.uniqueIdProofSignatureSize > 0 { - c.Subject.UniqueIDProofSignature = C.GoBytes(unsafe.Pointer(cc.subject.uniqueIdProofSignature), C.int(cc.subject.uniqueIdProofSignatureSize)) - } - } - - if cc.issuer != nil { - id, err := newIdentityFromCIdentity(cc.issuer) - if err != nil { - return nil - } - c.Issuer = id - } - - c.IssuerName.SerialNo = C.GoString(&cc.issuerName.serialNo[0]) - c.IssuerName.CommonName = C.GoString(&cc.issuerName.commonName[0]) - c.IssuerName.Country = C.GoString(&cc.issuerName.country[0]) - c.IssuerName.Organization = C.GoString(&cc.issuerName.organization[0]) - c.IssuerName.Unit = C.GoString(&cc.issuerName.unit[0]) - c.IssuerName.Locality = C.GoString(&cc.issuerName.locality[0]) - c.IssuerName.Province = C.GoString(&cc.issuerName.province[0]) - c.IssuerName.StreetAddress = C.GoString(&cc.issuerName.streetAddress[0]) - c.IssuerName.PostalCode = C.GoString(&cc.issuerName.postalCode[0]) - c.IssuerName.Email = C.GoString(&cc.issuerName.email[0]) - c.IssuerName.URL = C.GoString(&cc.issuerName.url[0]) - c.IssuerName.Host = C.GoString(&cc.issuerName.host[0]) - - if cc.extendedAttributesSize > 0 { - c.ExtendedAttributes = C.GoBytes(unsafe.Pointer(cc.extendedAttributes), C.int(cc.extendedAttributesSize)) - } - - c.MaxPathLength = uint(cc.maxPathLength) - - for i := 0; i < int(cc.crlCount); i++ { - csn := *((**[CertificateSerialNoSize]byte)(unsafe.Pointer(uintptr(unsafe.Pointer(cc.crl)) + (uintptr(i) * pointerSize)))) - var tmp [CertificateSerialNoSize]byte - copy(tmp[:], csn[:]) - c.CRL = append(c.CRL, tmp[:]) - } - - if cc.signatureSize > 0 { - c.Signature = C.GoBytes(unsafe.Pointer(cc.signature), C.int(cc.signatureSize)) - } - - return c -} - -// deleteCCertificate deletes a ZT_Certificate object returned by Certificate.CCertificate() -func deleteCCertificate(cc unsafe.Pointer) { - C.ZT_Certificate_delete((*C.ZT_Certificate)(cc)) -} - -// cCertificate creates a C ZT_Certificate structure from the content of a Certificate. -// It must be deleted with deleteCCertificate. -func (c *Certificate) cCertificate() unsafe.Pointer { - var cc C.ZT_Certificate - var subjectIdentities []C.ZT_Certificate_Identity - var subjectNetworks []C.ZT_Certificate_Network - var subjectCertificates []uintptr - var subjectUpdateURLs []uintptr - var subjectUpdateURLsData [][]byte - var crl []uintptr - - if len(c.SerialNo) == 48 { - copy((*[CertificateSerialNoSize]byte)(unsafe.Pointer(&cc.serialNo[0]))[:], c.SerialNo) - } - cc.flags = C.uint64_t(c.Flags) - cc.timestamp = C.int64_t(c.Timestamp) - cc.validity[0] = C.int64_t(c.Validity[0]) - cc.validity[1] = C.int64_t(c.Validity[1]) - - cc.subject.timestamp = C.int64_t(c.Subject.Timestamp) - - if len(c.Subject.Identities) > 0 { - subjectIdentities = make([]C.ZT_Certificate_Identity, len(c.Subject.Identities)) - for i, id := range c.Subject.Identities { - if id.Identity == nil { - return nil - } - subjectIdentities[i].identity = id.Identity.cIdentity() - if id.Locator != nil { - subjectIdentities[i].locator = id.Locator.cl - } - } - cc.subject.identities = &subjectIdentities[0] - cc.subject.identityCount = C.uint(len(subjectIdentities)) - } - - if len(c.Subject.Networks) > 0 { - subjectNetworks = make([]C.ZT_Certificate_Network, len(c.Subject.Networks)) - for i, n := range c.Subject.Networks { - subjectNetworks[i].id = C.uint64_t(n.ID) - subjectNetworks[i].controller.address = C.uint64_t(n.Controller.Address) - if len(n.Controller.Hash) == 48 { - copy((*[48]byte)(unsafe.Pointer(&subjectNetworks[i].controller.hash[0]))[:], n.Controller.Hash) - } - } - cc.subject.networks = &subjectNetworks[0] - cc.subject.networkCount = C.uint(len(subjectNetworks)) - } - - if len(c.Subject.Certificates) > 0 { - subjectCertificates = make([]uintptr, len(c.Subject.Certificates)) - for i, cert := range c.Subject.Certificates { - if len(cert) != CertificateSerialNoSize { - return nil - } - subjectCertificates[i] = uintptr(unsafe.Pointer(&cert[0])) - } - cc.subject.certificates = (**C.uint8_t)(unsafe.Pointer(&subjectCertificates[0])) - cc.subject.certificateCount = C.uint(len(subjectCertificates)) - } - - if len(c.Subject.UpdateURLs) > 0 { - subjectUpdateURLs = make([]uintptr, len(c.Subject.UpdateURLs)) - subjectUpdateURLsData = make([][]byte, len(c.Subject.UpdateURLs)) - for i, u := range c.Subject.UpdateURLs { - subjectUpdateURLsData[i] = stringAsZeroTerminatedBytes(u) - subjectUpdateURLs[i] = uintptr(unsafe.Pointer(&subjectUpdateURLsData[0][0])) - } - cc.subject.updateURLs = (**C.char)(unsafe.Pointer(&subjectUpdateURLs[0])) - cc.subject.updateURLCount = C.uint(len(subjectUpdateURLs)) - } - - cStrCopy(unsafe.Pointer(&cc.subject.name.serialNo[0]), CertificateMaxStringLength+1, c.Subject.Name.SerialNo) - cStrCopy(unsafe.Pointer(&cc.subject.name.commonName[0]), CertificateMaxStringLength+1, c.Subject.Name.CommonName) - cStrCopy(unsafe.Pointer(&cc.subject.name.country[0]), CertificateMaxStringLength+1, c.Subject.Name.Country) - cStrCopy(unsafe.Pointer(&cc.subject.name.organization[0]), CertificateMaxStringLength+1, c.Subject.Name.Organization) - cStrCopy(unsafe.Pointer(&cc.subject.name.unit[0]), CertificateMaxStringLength+1, c.Subject.Name.Unit) - cStrCopy(unsafe.Pointer(&cc.subject.name.locality[0]), CertificateMaxStringLength+1, c.Subject.Name.Locality) - cStrCopy(unsafe.Pointer(&cc.subject.name.province[0]), CertificateMaxStringLength+1, c.Subject.Name.Province) - cStrCopy(unsafe.Pointer(&cc.subject.name.streetAddress[0]), CertificateMaxStringLength+1, c.Subject.Name.StreetAddress) - cStrCopy(unsafe.Pointer(&cc.subject.name.postalCode[0]), CertificateMaxStringLength+1, c.Subject.Name.PostalCode) - cStrCopy(unsafe.Pointer(&cc.subject.name.email[0]), CertificateMaxStringLength+1, c.Subject.Name.Email) - cStrCopy(unsafe.Pointer(&cc.subject.name.url[0]), CertificateMaxStringLength+1, c.Subject.Name.URL) - cStrCopy(unsafe.Pointer(&cc.subject.name.host[0]), CertificateMaxStringLength+1, c.Subject.Name.Host) - - if len(c.Subject.UniqueID) > 0 { - cc.subject.uniqueId = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueID[0])) - cc.subject.uniqueIdSize = C.uint(len(c.Subject.UniqueID)) - if len(c.Subject.UniqueIDProofSignature) > 0 { - cc.subject.uniqueIdProofSignature = (*C.uint8_t)(unsafe.Pointer(&c.Subject.UniqueIDProofSignature[0])) - cc.subject.uniqueIdProofSignatureSize = C.uint(len(c.Subject.UniqueIDProofSignature)) - } - } - - if c.Issuer != nil { - cc.issuer = c.Issuer.cIdentity() - } - - cStrCopy(unsafe.Pointer(&cc.issuerName.serialNo[0]), CertificateMaxStringLength+1, c.IssuerName.SerialNo) - cStrCopy(unsafe.Pointer(&cc.issuerName.commonName[0]), CertificateMaxStringLength+1, c.IssuerName.CommonName) - cStrCopy(unsafe.Pointer(&cc.issuerName.country[0]), CertificateMaxStringLength+1, c.IssuerName.Country) - cStrCopy(unsafe.Pointer(&cc.issuerName.organization[0]), CertificateMaxStringLength+1, c.IssuerName.Organization) - cStrCopy(unsafe.Pointer(&cc.issuerName.unit[0]), CertificateMaxStringLength+1, c.IssuerName.Unit) - cStrCopy(unsafe.Pointer(&cc.issuerName.locality[0]), CertificateMaxStringLength+1, c.IssuerName.Locality) - cStrCopy(unsafe.Pointer(&cc.issuerName.province[0]), CertificateMaxStringLength+1, c.IssuerName.Province) - cStrCopy(unsafe.Pointer(&cc.issuerName.streetAddress[0]), CertificateMaxStringLength+1, c.IssuerName.StreetAddress) - cStrCopy(unsafe.Pointer(&cc.issuerName.postalCode[0]), CertificateMaxStringLength+1, c.IssuerName.PostalCode) - cStrCopy(unsafe.Pointer(&cc.issuerName.email[0]), CertificateMaxStringLength+1, c.IssuerName.Email) - cStrCopy(unsafe.Pointer(&cc.issuerName.url[0]), CertificateMaxStringLength+1, c.IssuerName.URL) - cStrCopy(unsafe.Pointer(&cc.issuerName.host[0]), CertificateMaxStringLength+1, c.IssuerName.Host) - - if len(c.ExtendedAttributes) > 0 { - cc.extendedAttributes = (*C.uint8_t)(unsafe.Pointer(&c.ExtendedAttributes[0])) - cc.extendedAttributesSize = C.uint(len(c.ExtendedAttributes)) - } - - cc.maxPathLength = C.uint(c.MaxPathLength) - - if len(c.CRL) > 0 { - crl = make([]uintptr, len(c.CRL)) - for i, cert := range c.CRL { - if len(cert) != CertificateSerialNoSize { - return nil - } - crl[i] = uintptr(unsafe.Pointer(&cert[0])) - } - cc.crl = (**C.uint8_t)(unsafe.Pointer(&crl[0])) - cc.crlCount = C.uint(len(crl)) - } - - if len(c.Signature) > 0 { - cc.signature = (*C.uint8_t)(unsafe.Pointer(&c.Signature[0])) - cc.signatureSize = C.uint(len(c.Signature)) - } - - // HACK: pass pointer to cc as uintptr to disable Go's protection against "Go pointers to - // Go pointers," as the C function called here will make a deep clone and then we are going - // to throw away 'cc' and its components. - return unsafe.Pointer(C._ZT_Certificate_clone2(C.uintptr_t(uintptr(unsafe.Pointer(&cc))))) -} - -// Marshal encodes this certificate as a byte array (binary format). -func (c *Certificate) Marshal() ([]byte, error) { - cc := c.cCertificate() - if cc == nil { - return nil, ErrInternal - } - defer deleteCCertificate(cc) - var encoded [16384]byte - encodedSize := C.int(16384) - rv := int(C.ZT_Certificate_encode((*C.ZT_Certificate)(cc), unsafe.Pointer(&encoded[0]), &encodedSize)) - if rv != 0 { - return nil, fmt.Errorf("Certificate encode error %d", rv) - } - return append(make([]byte, 0, int(encodedSize)), encoded[0:int(encodedSize)]...), nil -} - -// Sign signs this certificate and returns a new one with signature and issuer filled out. -// This should only be used after decoding a CSR with NewCertificateFromBytes. The non-subject -// parts of this Certificate, if any, are ignored. A new Certificate is returned with a completed -// signature. -func (c *Certificate) Sign(id *Identity) (*Certificate, error) { - if id == nil || !id.HasPrivate() { - return nil, ErrInvalidParameter - } - ctmp := c.cCertificate() - if ctmp == nil { - return nil, ErrInternal - } - defer deleteCCertificate(ctmp) - var signedCert [16384]byte - signedCertSize := C.int(16384) - rv := int(C.ZT_Certificate_sign((*C.ZT_Certificate)(ctmp), id.cIdentity(), unsafe.Pointer(&signedCert[0]), &signedCertSize)) - if rv != 0 { - return nil, fmt.Errorf("signing failed: error %d", rv) - } - return NewCertificateFromBytes(signedCert[0:int(signedCertSize)], true) -} - -// Verify returns nil on success or a certificate error if there is a problem with this certificate. -func (c *Certificate) Verify() error { - cc := c.cCertificate() - if cc == nil { - return ErrInternal - } - defer deleteCCertificate(cc) - return certificateErrorToError(int(C.ZT_Certificate_verify((*C.ZT_Certificate)(cc)))) -} - -// String returns a compact JSON representation of this certificate. -func (c *Certificate) String() string { - j, _ := json.Marshal(c) - return string(j) -} - -// JSON returns this certificate as a human-readable indented JSON string. -func (c *Certificate) JSON() string { - j, _ := json.MarshalIndent(c, "", " ") - return string(j) -} - -// NewCertificateSubjectUniqueId creates a new certificate subject unique ID and corresponding private key. -// Right now only one type is supported: CertificateUniqueIdTypeNistP384 -func NewCertificateSubjectUniqueId(uniqueIdType int) (id []byte, priv []byte, err error) { - if uniqueIdType != CertificateUniqueIdTypeNistP384 { - err = ErrInvalidParameter - return - } - id = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE)) - priv = make([]byte, int(C.ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_PRIVATE_SIZE)) - idSize := C.int(len(id)) - idPrivateSize := C.int(len(priv)) - rv := int(C.ZT_Certificate_newSubjectUniqueId((C.enum_ZT_CertificateUniqueIdType)(uniqueIdType), unsafe.Pointer(&id[0]), &idSize, unsafe.Pointer(&priv[0]), &idPrivateSize)) - if rv != 0 { - id = nil - priv = nil - err = fmt.Errorf("error %d", rv) - return - } - if int(idSize) != len(id) || int(idPrivateSize) != len(priv) { - id = nil - priv = nil - err = ErrInvalidParameter - return - } - return -} - -// NewCertificateCSR creates a new certificate signing request (CSR) from a certificate subject and optional unique ID. -func NewCertificateCSR(subject *CertificateSubject, uniqueId []byte, uniqueIdPrivate []byte) ([]byte, error) { - var uid unsafe.Pointer - var uidp unsafe.Pointer - if len(uniqueId) > 0 && len(uniqueIdPrivate) > 0 { - uid = unsafe.Pointer(&uniqueId[0]) - uidp = unsafe.Pointer(&uniqueIdPrivate[0]) - } - - var tmp Certificate - tmp.Subject = *subject - ctmp := tmp.cCertificate() - if ctmp == nil { - return nil, ErrInternal - } - defer deleteCCertificate(ctmp) - - var csr [16384]byte - csrSize := C.int(16384) - cc := (*C.ZT_Certificate)(ctmp) - rv := int(C.ZT_Certificate_newCSR(&(cc.subject), uid, C.int(len(uniqueId)), uidp, C.int(len(uniqueIdPrivate)), unsafe.Pointer(&csr[0]), &csrSize)) - if rv != 0 { - return nil, fmt.Errorf("ZT_Certificate_newCSR() failed: %d", rv) - } - - return append(make([]byte, 0, int(csrSize)), csr[0:int(csrSize)]...), nil -} diff --git a/attic/go/pkg/zerotier/endpoint.go b/attic/go/pkg/zerotier/endpoint.go deleted file mode 100644 index 0bb887ec1..000000000 --- a/attic/go/pkg/zerotier/endpoint.go +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -// const ZT_Fingerprint *_getFP(const ZT_Endpoint *ep) { return &(ep->value.fp); } -// uint64_t _getAddress(const ZT_Endpoint *ep) { return ep->value.fp.address; } -// uint64_t _getMAC(const ZT_Endpoint *ep) { return ep->value.mac; } -// const struct sockaddr_storage *_getSS(const ZT_Endpoint *ep) { return &(ep->value.ss); } -// void _setSS(ZT_Endpoint *ep,const void *ss) { memcpy(&(ep->value.ss),ss,sizeof(struct sockaddr_storage)); } -import "C" - -import ( - "encoding/json" - "unsafe" -) - -const ( - EndpointTypeNil = C.ZT_ENDPOINT_TYPE_NIL - EndpointTypeZeroTier = C.ZT_ENDPOINT_TYPE_ZEROTIER - EndpointTypeEthernet = C.ZT_ENDPOINT_TYPE_ETHERNET - EndpointTypeWifiDirect = C.ZT_ENDPOINT_TYPE_WIFI_DIRECT - EndpointTypeBluetooth = C.ZT_ENDPOINT_TYPE_BLUETOOTH - EndpointTypeIp = C.ZT_ENDPOINT_TYPE_IP - EndpointTypeIpUdp = C.ZT_ENDPOINT_TYPE_IP_UDP - EndpointTypeIpTcp = C.ZT_ENDPOINT_TYPE_IP_TCP - EndpointTypeIpHttp = C.ZT_ENDPOINT_TYPE_IP_HTTP -) - -type Endpoint struct { - cep C.ZT_Endpoint -} - -func EndpointTypeToString(t int) string { - switch t { - case EndpointTypeZeroTier: - return "zerotier" - case EndpointTypeEthernet: - return "ethernet" - case EndpointTypeWifiDirect: - return "wifi-direct" - case EndpointTypeBluetooth: - return "bluetooth" - case EndpointTypeIp: - return "ip/raw" - case EndpointTypeIpUdp: - return "ip/udp" - case EndpointTypeIpTcp: - return "ip/tcp" - case EndpointTypeIpHttp: - return "ip/http" - } - return "unsupported" -} - -// NewEndpointFromString constructs a new endpoint from an InetAddress or Endpoint string. -// This will auto detect whether this is a plain InetAddress or an Endpoint in string -// format. If the former it's created as a ZT_ENDPOINT_TYPE_IP_UDP endpoint. -func NewEndpointFromString(s string) (*Endpoint, error) { - if len(s) == 0 { - var ep Endpoint - ep.cep._type = C.ZT_ENDPOINT_TYPE_NIL - return &ep, nil - } - var ep Endpoint - cs := C.CString(s) - defer C.free(unsafe.Pointer(cs)) - if C.ZT_Endpoint_fromString(&ep.cep, cs) != 0 { - return nil, ErrInvalidParameter - } - return &ep, nil -} - -func NewEndpointFromInetAddress(addr *InetAddress) (*Endpoint, error) { - var ep Endpoint - var ss C.struct_sockaddr_storage - if !makeSockaddrStorage(addr.IP, addr.Port, &ss) { - return nil, ErrInvalidParameter - } - ep.cep._type = C.ZT_ENDPOINT_TYPE_IP_UDP - C._setSS(&ep.cep, unsafe.Pointer(&ss)) - return &ep, nil -} - -// Type returns this endpoint's type. -func (ep *Endpoint) Type() int { - return int(ep.cep._type) -} - -// TypeString returns a human-readable endpoint type. -func (ep *Endpoint) TypeString() string { - return EndpointTypeToString(int(ep.cep._type)) -} - -// InetAddress gets this Endpoint as an InetAddress or nil if its type is not addressed by one. -func (ep *Endpoint) InetAddress() *InetAddress { - switch ep.cep._type { - case EndpointTypeIp, EndpointTypeIpUdp, EndpointTypeIpTcp, EndpointTypeIpHttp: - ua := sockaddrStorageToUDPAddr(C._getSS(&ep.cep)) - return &InetAddress{IP: ua.IP, Port: ua.Port} - } - return nil -} - -// Address returns a ZeroTier address if this is a ZeroTier endpoint or a zero address otherwise. -func (ep *Endpoint) Address() Address { - switch ep.cep._type { - case EndpointTypeZeroTier: - return Address(C._getAddress(&ep.cep)) - } - return Address(0) -} - -// Fingerprint returns a fingerprint if this is a ZeroTier endpoint or nil otherwise. -func (ep *Endpoint) Fingerprint() *Fingerprint { - switch ep.cep._type { - case EndpointTypeZeroTier: - cfp := C._getFP(&ep.cep) - fp := Fingerprint{Address: Address(cfp.address), Hash: C.GoBytes(unsafe.Pointer(&cfp.hash[0]), 48)} - if allZero(fp.Hash) { - fp.Hash = nil - } - return &fp - } - return nil -} - -// MAC returns a MAC address if this is an Ethernet type endpoint or a zero address otherwise. -func (ep *Endpoint) MAC() MAC { - switch ep.cep._type { - case EndpointTypeEthernet, EndpointTypeWifiDirect, EndpointTypeBluetooth: - return MAC(C._getMAC(&ep.cep)) - } - return MAC(0) -} - -func (ep *Endpoint) String() string { - var buf [4096]byte - cs := C.ZT_Endpoint_toString(&ep.cep, (*C.char)(unsafe.Pointer(&buf[0])), 4096) - if cs == nil { - return "0" - } - return C.GoString(cs) -} - -func (ep *Endpoint) MarshalJSON() ([]byte, error) { - s := ep.String() - return json.Marshal(&s) -} - -func (ep *Endpoint) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - ep2, err := NewEndpointFromString(s) - if err != nil { - return err - } - *ep = *ep2 - return nil -} - -func (ep *Endpoint) setFromCEndpoint(cp *C.ZT_Endpoint) { - ep.cep = *cp -} diff --git a/attic/go/pkg/zerotier/errors.go b/attic/go/pkg/zerotier/errors.go deleted file mode 100644 index 0aebf1f40..000000000 --- a/attic/go/pkg/zerotier/errors.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// Err is a basic string error type for ZeroTier -type Err string - -func (e Err) Error() string { return (string)(e) } - -// Simple ZeroTier Errors -const ( - ErrInternal Err = "internal error" - ErrNodeInitFailed Err = "unable to initialize core Node instance" - ErrInvalidMACAddress Err = "invalid MAC address" - ErrInvalidZeroTierAddress Err = "invalid ZeroTier address" - ErrInvalidNetworkID Err = "invalid network ID" - ErrInvalidParameter Err = "invalid parameter" - ErrTapInitFailed Err = "unable to create native Tap instance" - ErrUnrecognizedIdentityType Err = "unrecognized identity type" - ErrInvalidKey Err = "invalid key data" - - ErrCertificateHaveNewerCert Err = "a newer certificate for this subject unique ID is already loaded" - ErrCertificateInvalidFormat Err = "invalid certificate format" - ErrCertificateInvalidIdentity Err = "invalid identity in certificate" - ErrCertificateInvalidPrimarySignature Err = "invalid primary signature" - ErrCertificateInvalidChain Err = "certificate chain verification failed" - ErrCertificateInvalidComponentSignature Err = "an internal component of this certificate has an invalid signature" - ErrCertificateInvalidUniqueIDProof Err = "certificate subject unique ID proof signature verification failed" - ErrCertificateMissingRequiredFields Err = "certificate is missing one or more required fields" - ErrCertificateOutOfValidTimeWindow Err = "certificate is out of its valid time window" -) - -// APIErr is returned by the JSON API when a call fails -type APIErr struct { - Reason string -} - -func (e *APIErr) Error() string { return e.Reason } diff --git a/attic/go/pkg/zerotier/fingerprint.go b/attic/go/pkg/zerotier/fingerprint.go deleted file mode 100644 index d263cf188..000000000 --- a/attic/go/pkg/zerotier/fingerprint.go +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "bytes" - "encoding/json" - "fmt" - "strings" - "unsafe" -) - -// FingerprintHashSize is the length of a fingerprint hash in bytes. -const FingerprintHashSize = 48 - -// Fingerprint bundles an address with an optional SHA384 full hash of the identity's key(s). -type Fingerprint struct { - Address Address `json:"address"` - Hash []byte `json:"hash"` -} - -// NewFingerprintFromString decodes a string-format fingerprint. -// A fingerprint has the format address-hash, where address is a 10-digit -// ZeroTier address and a hash is a base32-encoded SHA384 hash. Fingerprints -// can be missing the hash in which case they are represented the same as -// an Address and the hash field will be nil. -func NewFingerprintFromString(fps string) (*Fingerprint, error) { - if len(fps) < AddressStringLength { - return nil, ErrInvalidZeroTierAddress - } - ss := strings.Split(fps, "-") - if len(ss) < 1 || len(ss) > 2 { - return nil, ErrInvalidParameter - } - a, err := NewAddressFromString(ss[0]) - if err != nil { - return nil, err - } - if len(ss) == 2 { - h, err := Base32.DecodeString(ss[1]) - if err != nil { - return nil, err - } - if len(h) != 48 { - return nil, ErrInvalidParameter - } - return &Fingerprint{Address: a, Hash: h}, nil - } - return &Fingerprint{Address: a, Hash: nil}, nil -} - -func newFingerprintFromCFingerprint(cfp *C.ZT_Fingerprint) *Fingerprint { - var fp Fingerprint - if uintptr(unsafe.Pointer(cfp)) != 0 { - fp.Address = Address(cfp.address) - fp.Hash = C.GoBytes(unsafe.Pointer(&cfp.hash[0]), 48) - if allZero(fp.Hash) { - fp.Hash = nil - } - } - return &fp -} - -// String returns an address or a full address-hash depenting on whether a hash is present. -func (fp *Fingerprint) String() string { - if len(fp.Hash) == FingerprintHashSize { - return fmt.Sprintf("%.10x-%s", uint64(fp.Address), Base32.EncodeToString(fp.Hash)) - } - return fp.Address.String() -} - -// Equals test for full equality with another fingerprint (including hash). -func (fp *Fingerprint) Equals(fp2 *Fingerprint) bool { - return fp.Address == fp2.Address && bytes.Equal(fp.Hash[:], fp2.Hash[:]) -} - -// BestSpecificityEquals compares either just the addresses or also the hashes if both are present. -func (fp *Fingerprint) BestSpecificityEquals(fp2 *Fingerprint) bool { - if fp2 == nil || fp.Address != fp2.Address { - return false - } - if len(fp.Hash) == FingerprintHashSize && len(fp2.Hash) == FingerprintHashSize { - return bytes.Equal(fp.Hash, fp2.Hash) - } - return true -} - -func (fp *Fingerprint) MarshalJSON() ([]byte, error) { - return []byte("\"" + fp.String() + "\""), nil -} - -func (fp *Fingerprint) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - fp2, err := NewFingerprintFromString(s) - fp.Address = fp2.Address - fp.Hash = fp2.Hash - return err -} - -func (fp *Fingerprint) cFingerprint() *C.ZT_Fingerprint { - var apifp C.ZT_Fingerprint - apifp.address = C.uint64_t(fp.Address) - copy((*[48]byte)(unsafe.Pointer(&apifp.hash[0]))[:], fp.Hash[:]) - return &apifp -} diff --git a/attic/go/pkg/zerotier/identity.go b/attic/go/pkg/zerotier/identity.go deleted file mode 100644 index cc5d2b202..000000000 --- a/attic/go/pkg/zerotier/identity.go +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "bytes" - "encoding/hex" - "encoding/json" - "fmt" - "runtime" - "strings" - "unsafe" -) - -// TODO: export keys in ssh format? - -const ( - IdentityTypeC25519 = 0 - IdentityTypeP384 = 1 - - IdentityTypeC25519PublicKeySize = 64 - IdentityTypeC25519PrivateKeySize = 64 - IdentityTypeP384PublicKeySize = 114 - IdentityTypeP384PrivateKeySize = 112 -) - -// Identity is precisely what it sounds like: the address and associated keys for a ZeroTier node -type Identity struct { - address Address - idtype int - publicKey []byte - privateKey []byte - cid unsafe.Pointer -} - -func identityFinalizer(obj interface{}) { - cid := obj.(*Identity).cid - if cid != nil { - C.ZT_Identity_delete(cid) - } -} - -// NewIdentity generates a new identity of the selected type. -func NewIdentity(identityType int) (*Identity, error) { - var cid unsafe.Pointer - switch identityType { - case C.ZT_IDENTITY_TYPE_C25519: - cid = C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_C25519) - case C.ZT_IDENTITY_TYPE_P384: - cid = C.ZT_Identity_new(C.ZT_IDENTITY_TYPE_P384) - default: - return nil, ErrInvalidParameter - } - id, err := newIdentityFromCIdentity(cid) - if err != nil { - return nil, err - } - id.cid = cid - return id, nil -} - -// NewIdentityFromString generates a new identity from its string representation. -// The private key is imported as well if it is present. -func NewIdentityFromString(s string) (*Identity, error) { - ss := strings.Split(strings.TrimSpace(s), ":") - if len(ss) < 3 { - return nil, ErrInvalidParameter - } - - var err error - id := new(Identity) - id.address, err = NewAddressFromString(ss[0]) - if err != nil { - return nil, err - } - - if ss[1] == "0" { - id.idtype = 0 - } else if ss[1] == "1" { - id.idtype = 1 - } else { - return nil, ErrUnrecognizedIdentityType - } - - switch id.idtype { - - case 0: - id.publicKey, err = hex.DecodeString(ss[2]) - if err != nil { - return nil, err - } - if len(ss) >= 4 { - id.privateKey, err = hex.DecodeString(ss[3]) - if err != nil { - return nil, err - } - } - - case 1: - id.publicKey, err = Base32.DecodeString(ss[2]) - if err != nil { - return nil, err - } - if len(id.publicKey) != IdentityTypeP384PublicKeySize { - return nil, ErrInvalidKey - } - if len(ss) >= 4 { - id.privateKey, err = Base32.DecodeString(ss[3]) - if err != nil { - return nil, err - } - if len(id.privateKey) != IdentityTypeP384PrivateKeySize { - return nil, ErrInvalidKey - } - } - - } - - return id, nil -} - -func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { - if cid == nil { - 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(idStr)) - if err != nil { - return nil, err - } - - runtime.SetFinalizer(id, identityFinalizer) - - return id, nil -} - -func (id *Identity) cIdentity() unsafe.Pointer { - if id.cid == nil { - str := []byte(id.PrivateKeyString()) - if len(str) == 0 { - str = []byte(id.String()) - } - if len(str) == 0 { - return nil - } - str = append(str, byte(0)) - id.cid = C.ZT_Identity_fromString((*C.char)(unsafe.Pointer(&str[0]))) - } - return id.cid -} - -// Address returns this identity's address. -func (id *Identity) Address() Address { return id.address } - -// HasPrivate returns true if this identity has its own private portion. -func (id *Identity) HasPrivate() bool { return len(id.privateKey) > 0 } - -// Fingerprint gets this identity's address plus hash of public key(s). -func (id *Identity) Fingerprint() *Fingerprint { - return newFingerprintFromCFingerprint(C.ZT_Identity_fingerprint(id.cIdentity())) -} - -// PrivateKeyString returns the full identity.secret if the private key is set, -// or an empty string if no private key is set. -func (id *Identity) PrivateKeyString() string { - switch id.idtype { - case IdentityTypeC25519: - if len(id.publicKey) == IdentityTypeC25519PublicKeySize && len(id.privateKey) == IdentityTypeC25519PrivateKeySize { - return fmt.Sprintf("%.10x:0:%x:%x", uint64(id.address), id.publicKey, id.privateKey) - } - case IdentityTypeP384: - if len(id.publicKey) == IdentityTypeP384PublicKeySize && len(id.privateKey) == IdentityTypeP384PrivateKeySize { - return fmt.Sprintf("%.10x:1:%s:%s", uint64(id.address), Base32.EncodeToString(id.publicKey), Base32.EncodeToString(id.privateKey)) - } - } - return "" -} - -// PublicKeyString returns the address and public key (identity.public contents). -// An empty string is returned if this identity is invalid or not initialized. -func (id *Identity) String() string { - switch id.idtype { - case IdentityTypeC25519: - if len(id.publicKey) == IdentityTypeC25519PublicKeySize { - return fmt.Sprintf("%.10x:0:%x", uint64(id.address), id.publicKey) - } - case IdentityTypeP384: - if len(id.publicKey) == IdentityTypeP384PublicKeySize { - return fmt.Sprintf("%.10x:1:%s", uint64(id.address), Base32.EncodeToString(id.publicKey)) - } - } - return "" -} - -// LocallyValidate performs local self-validation of this identity -func (id *Identity) LocallyValidate() bool { - return C.ZT_Identity_validate(id.cIdentity()) != 0 -} - -// Sign signs a message with this identity -func (id *Identity) Sign(msg []byte) ([]byte, error) { - var dataP unsafe.Pointer - if len(msg) > 0 { - dataP = unsafe.Pointer(&msg[0]) - } - var sig [96]byte - sigLen := C.ZT_Identity_sign(id.cIdentity(), dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), 96) - if sigLen <= 0 { - return nil, ErrInvalidKey - } - - return sig[0:uint(sigLen)], nil -} - -// Verify verifies a signature -func (id *Identity) Verify(msg, sig []byte) bool { - if len(sig) == 0 { - return false - } - var dataP unsafe.Pointer - if len(msg) > 0 { - dataP = unsafe.Pointer(&msg[0]) - } - return C.ZT_Identity_verify(id.cIdentity(), dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 -} - -// Equals performs a deep equality test between this and another identity -func (id *Identity) Equals(id2 *Identity) bool { - if id2 == nil { - return id == nil - } - if id == nil { - return false - } - return id.address == id2.address && id.idtype == id2.idtype && bytes.Equal(id.publicKey, id2.publicKey) && bytes.Equal(id.privateKey, id2.privateKey) -} - -func (id *Identity) MarshalJSON() ([]byte, error) { - return []byte("\"" + id.String() + "\""), nil -} - -func (id *Identity) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - nid, err := NewIdentityFromString(s) - if err != nil { - return err - } - *id = *nid - return nil -} diff --git a/attic/go/pkg/zerotier/inetaddress.go b/attic/go/pkg/zerotier/inetaddress.go deleted file mode 100644 index 65e35761e..000000000 --- a/attic/go/pkg/zerotier/inetaddress.go +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "net" - "strconv" - "strings" - "syscall" - "unsafe" -) - -func sockaddrStorageToIPNet(ss *C.struct_sockaddr_storage) *net.IPNet { - var a net.IPNet - switch ss.ss_family { - case syscall.AF_INET: - sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss)) - var ip4 [4]byte - copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:]) - a.IP = ip4[:] - a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])), 32) - return &a - case syscall.AF_INET6: - sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss)) - var ip6 [16]byte - copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:]) - a.IP = ip6[:] - a.Mask = net.CIDRMask(int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])), 128) - return &a - } - return nil -} - -func sockaddrStorageToUDPAddr(ss *C.struct_sockaddr_storage) *net.UDPAddr { - var a net.UDPAddr - switch ss.ss_family { - case syscall.AF_INET: - sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss)) - var ip4 [4]byte - copy(ip4[:], (*[4]byte)(unsafe.Pointer(&sa4.sin_addr))[:]) - a.IP = ip4[:] - a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:])) - return &a - case syscall.AF_INET6: - sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss)) - var ip6 [16]byte - copy(ip6[:], (*[16]byte)(unsafe.Pointer(&sa6.sin6_addr))[:]) - a.IP = ip6[:] - a.Port = int(binary.BigEndian.Uint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:])) - return &a - } - return nil -} - -func sockaddrStorageToUDPAddr2(ss unsafe.Pointer) *net.UDPAddr { - return sockaddrStorageToUDPAddr((*C.struct_sockaddr_storage)(ss)) -} - -func zeroSockaddrStorage(ss *C.struct_sockaddr_storage) { - C.memset(unsafe.Pointer(ss), 0, C.sizeof_struct_sockaddr_storage) -} - -func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) bool { - zeroSockaddrStorage(ss) - if len(ip) == 4 { - sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss)) - sa4.sin_family = syscall.AF_INET - copy(((*[4]byte)(unsafe.Pointer(&sa4.sin_addr)))[:], ip) - binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa4.sin_port)))[:], uint16(port)) - return true - } - if len(ip) == 16 { - sa6 := (*C.struct_sockaddr_in6)(unsafe.Pointer(ss)) - sa6.sin6_family = syscall.AF_INET6 - copy(((*[16]byte)(unsafe.Pointer(&sa6.sin6_addr)))[:], ip) - binary.BigEndian.PutUint16(((*[2]byte)(unsafe.Pointer(&sa6.sin6_port)))[:], uint16(port)) - return true - } - return false -} - -// InetAddress implements net.Addr but has a ZeroTier-like string representation -type InetAddress struct { - IP net.IP - Port int -} - -// Nil returns true if this InetAddress is empty. -func (ina *InetAddress) Nil() bool { - return len(ina.IP) == 0 -} - -// Less returns true if this IP/port is lexicographically less than another -func (ina *InetAddress) Less(i2 *InetAddress) bool { - c := bytes.Compare(ina.IP, i2.IP) - if c < 0 { - return true - } - if c == 0 { - return ina.Port < i2.Port - } - return false -} - -// NewInetAddressFromString parses an IP[/port] format address -func NewInetAddressFromString(s string) *InetAddress { - i := new(InetAddress) - ss := strings.Split(strings.TrimSpace(s), "/") - if len(ss) > 0 { - i.IP = net.ParseIP(ss[0]) - i4 := i.IP.To4() - if len(i4) == 4 { // down-convert IPv4-in-6 IPs to native IPv4 as this is what all our code expects - i.IP = i4 - } - if len(ss) > 1 { - p64, _ := strconv.ParseUint(ss[1], 10, 64) - i.Port = int(p64 & 0xffff) - } - } - return i -} - -// NewInetAddressFromSockaddr parses a sockaddr_in or sockaddr_in6 C structure (may crash if given something other than these!) -// This is a convenience wrapper around the CGO functions in node.go. -func NewInetAddressFromSockaddr(sa unsafe.Pointer) *InetAddress { - i := new(InetAddress) - if uintptr(sa) != 0 { - ua := sockaddrStorageToUDPAddr2(sa) - if ua != nil { - i.IP = ua.IP - i.Port = ua.Port - } - } - return i -} - -// Network returns "udp" to implement net.Addr -func (ina *InetAddress) Network() string { - return "udp" -} - -// String returns this address in ZeroTier-canonical IP/port format -func (ina *InetAddress) String() string { - return ina.IP.String() + "/" + strconv.FormatInt(int64(ina.Port), 10) -} - -// Family returns the address family (AFInet etc.) or 0 if none -func (ina *InetAddress) Family() int { - switch len(ina.IP) { - case 4: - return syscall.AF_INET - case 16: - return syscall.AF_INET6 - } - return 0 -} - -// Valid returns true if both the IP and port have valid values -func (ina *InetAddress) Valid() bool { - return (len(ina.IP) == 4 || len(ina.IP) == 16) && (ina.Port > 0 && ina.Port < 65536) -} - -// MarshalJSON marshals this MAC as a string -func (ina *InetAddress) MarshalJSON() ([]byte, error) { - s := ina.String() - return json.Marshal(&s) -} - -// UnmarshalJSON unmarshals this MAC from a string -func (ina *InetAddress) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - *ina = *NewInetAddressFromString(s) - return nil -} - -// key returns a short array suitable for use as a map[] key for this IP -func (ina *InetAddress) key() (k [3]uint64) { - copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ina.IP) - k[2] = uint64(ina.Port) - return -} diff --git a/attic/go/pkg/zerotier/localconfig.go b/attic/go/pkg/zerotier/localconfig.go deleted file mode 100644 index 8a956db6c..000000000 --- a/attic/go/pkg/zerotier/localconfig.go +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "encoding/json" - "io/ioutil" - "os" - "runtime" -) - -// LocalConfigPhysicalPathConfiguration contains settings for physical paths -type LocalConfigPhysicalPathConfiguration struct { - // Blacklist flags this path as unusable for ZeroTier traffic - Blacklist bool -} - -// 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:"try,omitempty"` -} - -// LocalConfigSettings contains node settings -type LocalConfigSettings struct { - // PrimaryPort is the main UDP port and must be set. - PrimaryPort int `json:"primaryPort"` - - // SecondaryPort is the secondary UDP port, set to 0 to disable (picked at random by default) - SecondaryPort int `json:"secondaryPort"` - - // PortMapping enables uPnP and NAT-PMP support - PortMapping bool `json:"portMapping"` - - // LogSizeMax is the maximum size of the infoLog in kilobytes or 0 for no limit and -1 to disable logging - LogSizeMax int `json:"logSizeMax"` - - // IP/port to bind for TCP access to control API (TCP API port disabled if null) - APITCPBindAddress *InetAddress `json:"apiTCPBindAddress,omitempty"` - - // InterfacePrefixBlacklist are prefixes of physical network interface names that won't be used by ZeroTier (e.g. "lo" or "utun") - InterfacePrefixBlacklist []string `json:"interfacePrefixBlacklist,omitempty"` - - // ExplicitAddresses are explicit IP/port addresses to advertise to other nodes, such as externally mapped ports on a router - ExplicitAddresses []InetAddress `json:"explicitAddresses,omitempty"` -} - -// LocalConfig is the local.conf file and stores local settings for the node. -type LocalConfig struct { - // Physical path configurations by CIDR IP/bits - Physical map[string]LocalConfigPhysicalPathConfiguration `json:"physical,omitempty"` - - // Virtual node specific configurations by 10-digit hex ZeroTier address - Virtual map[Address]LocalConfigVirtualAddressConfiguration `json:"virtual,omitempty"` - - // Network local configurations by 16-digit hex ZeroTier network ID - Network map[NetworkID]NetworkLocalSettings `json:"network,omitempty"` - - // LocalConfigSettings contains other local settings for this node - Settings LocalConfigSettings `json:"settings"` - - initialized bool -} - -// Read this local config from a file, initializing to defaults if the file does not exist. -func (lc *LocalConfig) Read(p string, saveDefaultsIfNotExist, isTotallyNewNode bool) error { - // Initialize defaults, which may be replaced if we read a file from disk. - if !lc.initialized { - lc.initialized = true - - lc.Physical = make(map[string]LocalConfigPhysicalPathConfiguration) - lc.Virtual = make(map[Address]LocalConfigVirtualAddressConfiguration) - lc.Network = make(map[NetworkID]NetworkLocalSettings) - - lc.Settings.PrimaryPort = 9993 - lc.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))] - lc.Settings.PortMapping = true - lc.Settings.LogSizeMax = 128 - - if !isTotallyNewNode && runtime.GOOS != "darwin" && runtime.GOOS != "windows" { - // If this doesn't look like a new node and it's not a desktop OS, go ahead - // and bind the local TCP API port so as not to break scripts. - lc.Settings.APITCPBindAddress = NewInetAddressFromString("127.0.0.1/9993") - } - - switch runtime.GOOS { - case "windows": - lc.Settings.InterfacePrefixBlacklist = []string{"loopback"} - case "darwin": - lc.Settings.InterfacePrefixBlacklist = []string{"lo", "utun", "feth"} - default: - lc.Settings.InterfacePrefixBlacklist = []string{"lo"} - } - } - - data, err := ioutil.ReadFile(p) - if err != nil { - if !os.IsNotExist(err) { - return err - } - if saveDefaultsIfNotExist { - err = lc.Write(p) - if err != nil { - return err - } - } - return nil - } - return json.Unmarshal(data, lc) -} - -// Write this local config to a file -func (lc *LocalConfig) Write(p string) error { - data, err := json.MarshalIndent(lc, "", "\t") - if err != nil { - return err - } - return ioutil.WriteFile(p, data, 0644) -} diff --git a/attic/go/pkg/zerotier/locator.go b/attic/go/pkg/zerotier/locator.go deleted file mode 100644 index 7517ea603..000000000 --- a/attic/go/pkg/zerotier/locator.go +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "encoding/json" - "runtime" - "unsafe" -) - -type Locator struct { - Timestamp int64 `json:"timestamp"` - Fingerprint *Fingerprint `json:"fingerprint"` - Endpoints []Endpoint `json:"endpoints"` - cl unsafe.Pointer -} - -func newLocatorFromCLocator(cl unsafe.Pointer, needFinalizer bool) (*Locator, error) { - loc := new(Locator) - loc.cl = cl - err := loc.init(needFinalizer) - if err != nil { - return nil, err - } - return loc, nil -} - -func NewLocator(ts int64, endpoints []*Endpoint, signer *Identity) (*Locator, error) { - if ts <= 0 || len(endpoints) == 0 || signer == nil { - return nil, ErrInvalidParameter - } - eps := make([]C.ZT_Endpoint, 0, len(endpoints)) - for _, e := range endpoints { - eps = append(eps, e.cep) - } - loc := C.ZT_Locator_create(C.int64_t(ts), &eps[0], nil, C.uint(len(eps)), signer.cIdentity()) - if uintptr(loc) == 0 { - return nil, ErrInvalidParameter - } - - goloc := new(Locator) - goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init(true) -} - -func NewLocatorFromBytes(lb []byte) (*Locator, error) { - if len(lb) == 0 { - return nil, ErrInvalidParameter - } - loc := C.ZT_Locator_unmarshal(unsafe.Pointer(&lb[0]), C.uint(len(lb))) - if uintptr(loc) == 0 { - return nil, ErrInvalidParameter - } - - goloc := new(Locator) - goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init(true) -} - -func NewLocatorFromString(s string) (*Locator, error) { - if len(s) == 0 { - return nil, ErrInvalidParameter - } - sb := []byte(s) - sb = append(sb, 0) - loc := C.ZT_Locator_fromString((*C.char)(unsafe.Pointer(&sb[0]))) - if loc == nil { - return nil, ErrInvalidParameter - } - - goloc := new(Locator) - goloc.cl = unsafe.Pointer(loc) - return goloc, goloc.init(true) -} - -func (loc *Locator) Validate(id *Identity) bool { - if id == nil { - return false - } - return C.ZT_Locator_verify(loc.cl, id.cIdentity()) != 0 -} - -func (loc *Locator) Bytes() []byte { - if loc.cl == nil { - return nil - } - var buf [16384]byte // larger than ZT_LOCATOR_MARSHAL_SIZE_MAX - bl := C.ZT_Locator_marshal(loc.cl, unsafe.Pointer(&buf[0]), 16384) - if bl <= 0 { - return nil - } - return buf[0:int(bl)] -} - -func (loc *Locator) String() string { - if loc.cl == nil { - return "" - } - var buf [16384]C.char // 16384 == ZT_LOCATOR_STRING_SIZE_MAX - return C.GoString(C.ZT_Locator_toString(loc.cl, &buf[0], 16384)) -} - -func (loc *Locator) MarshalJSON() ([]byte, error) { - return json.Marshal(loc) -} - -func (loc *Locator) UnmarshalJSON(j []byte) error { - if loc.cl != nil { - C.ZT_Locator_delete(loc.cl) - loc.cl = unsafe.Pointer(nil) - } - - err := json.Unmarshal(j, loc) - if err != nil { - return err - } - return loc.init(true) -} - -func locatorFinalizer(obj interface{}) { - if obj != nil { - cl := obj.(*Locator).cl - if cl != nil { - C.ZT_Locator_delete(cl) - } - } -} - -func (loc *Locator) init(needFinalizer bool) error { - loc.Timestamp = int64(C.ZT_Locator_timestamp(loc.cl)) - cfp := C.ZT_Locator_fingerprint(loc.cl) - if uintptr(unsafe.Pointer(cfp)) == 0 { - return ErrInternal - } - loc.Fingerprint = newFingerprintFromCFingerprint(cfp) - epc := int(C.ZT_Locator_endpointCount(loc.cl)) - loc.Endpoints = make([]Endpoint, epc) - for i := 0; i < epc; i++ { - loc.Endpoints[i].cep = *C.ZT_Locator_endpoint(loc.cl, C.uint(i)) - } - if needFinalizer { - runtime.SetFinalizer(loc, locatorFinalizer) - } - return nil -} diff --git a/attic/go/pkg/zerotier/mac.go b/attic/go/pkg/zerotier/mac.go deleted file mode 100644 index a6c5a295a..000000000 --- a/attic/go/pkg/zerotier/mac.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" -) - -// MAC represents an Ethernet hardware address -type MAC uint64 - -// NewMACFromString decodes a MAC address in canonical colon-separated hex format -func NewMACFromString(s string) (MAC, error) { - ss := strings.Split(s, ":") - if len(ss) != 6 { - return MAC(0), ErrInvalidMACAddress - } - var m uint64 - for i := 0; i < 6; i++ { - m <<= 8 - c, _ := strconv.ParseUint(ss[i], 16, 64) - if c > 0xff { - return MAC(0), ErrInvalidMACAddress - } - m |= c & 0xff - } - return MAC(m), nil -} - -// NewMACFromBytes decodes a MAC from a 6-byte array -func NewMACFromBytes(b []byte) (MAC, error) { - if len(b) < 6 { - return MAC(0), ErrInvalidMACAddress - } - var m uint64 - for i := 0; i < 6; i++ { - m <<= 8 - m |= uint64(b[i]) - } - return MAC(m), nil -} - -// NewMACForNetworkMember computes the static MAC for a given address and network ID -func NewMACForNetworkMember(addr Address, nwid NetworkID) MAC { - // This is the same algorithm as found in MAC::fromAddress() in MAC.hpp - firstOctetForNetwork := byte((byte(nwid) & 0xfe) | 0x02) - if firstOctetForNetwork == 0x52 { - firstOctetForNetwork = 0x32 - } - m := uint64(firstOctetForNetwork) << 40 - m |= uint64(addr) - m ^= ((uint64(nwid) >> 8) & 0xff) << 32 - m ^= ((uint64(nwid) >> 16) & 0xff) << 24 - m ^= ((uint64(nwid) >> 24) & 0xff) << 16 - m ^= ((uint64(nwid) >> 32) & 0xff) << 8 - m ^= (uint64(nwid) >> 40) & 0xff - return MAC(m) -} - -// String returns this MAC address in canonical human-readable form -func (m MAC) String() string { - return fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", (uint64(m)>>40)&0xff, (uint64(m)>>32)&0xff, (uint64(m)>>24)&0xff, (uint64(m)>>16)&0xff, (uint64(m)>>8)&0xff, uint64(m)&0xff) -} - -// MarshalJSON marshals this MAC as a string -func (m MAC) MarshalJSON() ([]byte, error) { - return []byte("\"" + m.String() + "\""), nil -} - -// UnmarshalJSON unmarshals this MAC from a string -func (m *MAC) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - *m, err = NewMACFromString(s) - return err -} diff --git a/attic/go/pkg/zerotier/misc.go b/attic/go/pkg/zerotier/misc.go deleted file mode 100644 index f2f560a29..000000000 --- a/attic/go/pkg/zerotier/misc.go +++ /dev/null @@ -1,304 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "encoding/base32" - "encoding/binary" - "math/rand" - "net" - "sync" - "time" - "unsafe" -) - -// LogoChar is the unicode character that is ZeroTier's logo -const LogoChar = "⏁" - -// pointerSize is the size of a pointer on this system -const pointerSize = unsafe.Sizeof(uintptr(0)) - -// Base32Alphabet is the Base32 alphabet used in ZeroTier. -const Base32Alphabet = "abcdefghijklmnopqrstuvwxyz234567" - -// Base32 is an encoder using the ZeroTier base32 encoding and no padding (same as core). -var Base32 = base32.NewEncoding(Base32Alphabet).WithPadding(base32.NoPadding) - -// unassignedPrivilegedPorts are ports below 1024 that do not appear to be assigned by IANA. -// The new 2.0+ ZeroTier default is 793, which we will eventually seek to have assigned. These -// are searched as backups if this port is already in use on a system. -var unassignedPrivilegedPorts = []int{ - 4, - 6, - 8, - 10, - 12, - 14, - 15, - 16, - 26, - 28, - 30, - 32, - 34, - 36, - 40, - 60, - 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, - 285, - 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, - 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, - 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, - 703, - 708, - 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, - 732, 733, 734, 735, 736, 737, 738, 739, 740, - 743, - 745, 746, - 755, 756, - 766, - 768, - 778, 779, - 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, - 802, 803, 804, 805, 806, 807, 808, 809, - 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, - 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, - 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, - 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, - 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, - 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, - 904, 905, 906, 907, 908, 909, 910, 911, - 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, - 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, - 1023, -} - -var prng = rand.NewSource(time.Now().UnixNano()) -var prngLock sync.Mutex - -func randomUInt() uint { - prngLock.Lock() - i := prng.Int63() - prngLock.Unlock() - return uint(i) -} - -// TimeMs returns the time in milliseconds since epoch. -func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) } - -// ipNetToKey creates a key that can be used in a map[] from a net.IPNet -func ipNetToKey(ipn *InetAddress) (k [3]uint64) { - copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], ipn.IP) - k[2] = uint64(ipn.Port) - return -} - -func allZero(b []byte) bool { - for _, bb := range b { - if bb != 0 { - return false - } - } - return true -} - -// checkPort does trial binding to a port using both UDP and TCP and returns false if any bindings fail. -func checkPort(port int) bool { - var ua net.UDPAddr - ua.IP = net.IPv6zero - ua.Port = port - uc, err := net.ListenUDP("udp6", &ua) - if uc != nil { - _ = uc.Close() - } - if err != nil { - return false - } - ua.IP = net.IPv4zero - uc, err = net.ListenUDP("udp4", &ua) - if uc != nil { - _ = uc.Close() - } - if err != nil { - return false - } - - var ta net.TCPAddr - ta.IP = net.IPv6zero - ta.Port = port - tc, err := net.ListenTCP("tcp6", &ta) - if tc != nil { - _ = tc.Close() - } - if err != nil { - return false - } - ta.IP = net.IPv4zero - tc, err = net.ListenTCP("tcp4", &ta) - if tc != nil { - _ = tc.Close() - } - if err != nil { - return false - } - - return true -} - -// The ipClassify code below is based on and should produce identical results to -// InetAddress::ipScope() in the C++ code. - -const ( - ipClassificationNone = -1 - ipClassificationLoopback = 0 - ipClassificationPseudoprivate = 1 - ipClassificationPrivate = 2 - ipClassificationLinkLocal = 3 - ipClassificationMulticast = 4 - ipClassificationGlobal = 5 -) - -var ipv4PseudoprivatePrefixes = []byte{ - 0x06, // 6.0.0.0/8 (US Army) - 0x0b, // 11.0.0.0/8 (US DoD) - 0x15, // 21.0.0.0/8 (US DDN-RVN) - 0x16, // 22.0.0.0/8 (US DISA) - 0x19, // 25.0.0.0/8 (UK Ministry of Defense) - 0x1a, // 26.0.0.0/8 (US DISA) - 0x1c, // 28.0.0.0/8 (US DSI-North) - 0x1d, // 29.0.0.0/8 (US DISA) - 0x1e, // 30.0.0.0/8 (US DISA) - 0x33, // 51.0.0.0/8 (UK Department of Social Security) - 0x37, // 55.0.0.0/8 (US DoD) - 0x38, // 56.0.0.0/8 (US Postal Service) -} - -// ipClassify determines the official or in a few cases unofficial role of an IP address -func ipClassify(ip net.IP) int { - if len(ip) == 16 { - ip4 := ip.To4() - if len(ip4) == 4 { - ip = ip4 - } - } - if len(ip) == 4 { - ip4FirstByte := ip[0] - for _, b := range ipv4PseudoprivatePrefixes { - if ip4FirstByte == b { - return ipClassificationPseudoprivate - } - } - ip4 := binary.BigEndian.Uint32(ip) - switch ip4FirstByte { - case 0x0a: // 10.0.0.0/8 - return ipClassificationPrivate - case 0x64: // 100.64.0.0/10 - if (ip4 & 0xffc00000) == 0x64400000 { - return ipClassificationPrivate - } - case 0x7f: // 127.0.0.1/8 - return ipClassificationLoopback - case 0xa9: // 169.254.0.0/16 - if (ip4 & 0xffff0000) == 0xa9fe0000 { - return ipClassificationLinkLocal - } - case 0xac: // 172.16.0.0/12 - if (ip4 & 0xfff00000) == 0xac100000 { - return ipClassificationPrivate - } - case 0xc0: // 192.168.0.0/16 - if (ip4 & 0xffff0000) == 0xc0a80000 { - return ipClassificationPrivate - } - } - switch ip4 >> 28 { - case 0xe: // 224.0.0.0/4 - return ipClassificationMulticast - case 0xf: // 240.0.0.0/4 ("reserved," usually unusable) - return ipClassificationNone - } - return ipClassificationGlobal - } - - if len(ip) == 16 { - if (ip[0] & 0xf0) == 0xf0 { - if ip[0] == 0xff { // ff00::/8 - return ipClassificationMulticast - } - if ip[0] == 0xfe && (ip[1]&0xc0) == 0x80 { - if allZero(ip[2:15]) { - if ip[15] == 0x01 { // fe80::1/128 - return ipClassificationLoopback - } - return ipClassificationLinkLocal - } - } - if (ip[0] & 0xfe) == 0xfc { // fc00::/7 - return ipClassificationPrivate - } - } - if allZero(ip[0:15]) { - if ip[15] == 0x01 { // ::1/128 - return ipClassificationLoopback - } - if ip[15] == 0x00 { // ::/128 - return ipClassificationNone - } - } - return ipClassificationGlobal - } - - return ipClassificationNone -} - -// stringAsZeroTerminatedBytes creates a C string but as a Go []byte -func stringAsZeroTerminatedBytes(s string) (b []byte) { - if len(s) == 0 { - b = []byte{0} // single zero - return - } - sb := []byte(s) - b = make([]byte, len(sb) + 1) - copy(b, sb) - // make() will zero memory, so b[len(sb)+1] will be 0 - return -} - -// cStrCopy copies src into dest as a zero-terminated C string -func cStrCopy(dest unsafe.Pointer, destSize int, src string) { - sb := []byte(src) - if len(sb) > (destSize - 1) { - sb = sb[0:destSize - 1] - } - dp := dest - for _, c := range sb { - *((*byte)(dp)) = c - dp = unsafe.Pointer(uintptr(dp) + 1) - } - *((*byte)(dp)) = 0 -} - -// cStr returns an always zero-terminated byte array. -// It's like C.CString but doesn't do a malloc or need a free. -func cStr(s string) []byte { - sb := []byte(s) - if len(sb) > 0 { - return append(append(make([]byte, 0, len(sb)+1), sb...), byte(0)) - } else { - return []byte{0} - } -} diff --git a/attic/go/pkg/zerotier/multicastgroup.go b/attic/go/pkg/zerotier/multicastgroup.go deleted file mode 100644 index 2a61d9209..000000000 --- a/attic/go/pkg/zerotier/multicastgroup.go +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import "fmt" - -// MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits -type MulticastGroup struct { - MAC MAC `json:"mac"` - ADI uint32 `json:"adi"` -} - -// String returns MAC#ADI -func (mg *MulticastGroup) String() string { - if mg.ADI != 0 { - return fmt.Sprintf("%s#%.8x", mg.MAC.String(), mg.ADI) - } - return mg.MAC.String() -} - -// Less returns true if this MulticastGroup is less than another. -func (mg *MulticastGroup) Less(mg2 *MulticastGroup) bool { - return mg.MAC < mg2.MAC || (mg.MAC == mg2.MAC && mg.ADI < mg2.ADI) -} - -// key returns an array usable as a key for a map[] -func (mg *MulticastGroup) key() (k [2]uint64) { - k[0] = uint64(mg.MAC) - k[1] = uint64(mg.ADI) - return -} diff --git a/attic/go/pkg/zerotier/nativetap.go b/attic/go/pkg/zerotier/nativetap.go deleted file mode 100644 index 92fb7d6da..000000000 --- a/attic/go/pkg/zerotier/nativetap.go +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 wraps the C++ EthernetTap and its implementations. - -package zerotier - -//#include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "fmt" - "net" - "sync" - "sync/atomic" - "syscall" - "unsafe" -) - -// nativeTap is a Tap implementation that wraps a native C++ interface to a system tun/tap device -type nativeTap struct { - tap unsafe.Pointer - networkStatus uint32 - enabled uint32 - multicastGroupHandlers []func(bool, *MulticastGroup) - multicastGroupHandlersLock sync.Mutex -} - -// Close is a no-op for the native tap because GoGlue does this when networks are left -func (t *nativeTap) Close() {} - -// Type returns a human-readable description of this tap implementation -func (t *nativeTap) Type() string { - return "native" -} - -// Error gets this tap device's error status -func (t *nativeTap) Error() (int, string) { - return 0, "" -} - -// SetEnabled sets this tap's enabled state -func (t *nativeTap) SetEnabled(enabled bool) { - if enabled && atomic.SwapUint32(&t.enabled, 1) == 0 { - C.ZT_GoTap_setEnabled(t.tap, 1) - } else if !enabled && atomic.SwapUint32(&t.enabled, 0) == 1 { - C.ZT_GoTap_setEnabled(t.tap, 0) - } -} - -// Enabled returns true if this tap is currently processing packets -func (t *nativeTap) Enabled() bool { - return atomic.LoadUint32(&t.enabled) != 0 -} - -// AddIP adds an IP address (with netmask) to this tap -func (t *nativeTap) AddIP(ip *InetAddress) error { - if len(ip.IP) == 16 { - if ip.Port > 128 || ip.Port < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_addIp(t.tap, C.int(syscall.AF_INET6), unsafe.Pointer(&ip.IP[0]), C.int(ip.Port)) - } else if len(ip.IP) == 4 { - if ip.Port > 32 || ip.Port < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_addIp(t.tap, C.int(syscall.AF_INET), unsafe.Pointer(&ip.IP[0]), C.int(ip.Port)) - } - return ErrInvalidParameter -} - -// RemoveIP removes this IP address (with netmask) from this tap -func (t *nativeTap) RemoveIP(ip *InetAddress) error { - if len(ip.IP) == 16 { - if ip.Port > 128 || ip.Port < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_removeIp(t.tap, C.int(syscall.AF_INET6), unsafe.Pointer(&ip.IP[0]), C.int(ip.Port)) - return nil - } - if len(ip.IP) == 4 { - if ip.Port > 32 || ip.Port < 0 { - return ErrInvalidParameter - } - C.ZT_GoTap_removeIp(t.tap, C.int(syscall.AF_INET), unsafe.Pointer(&ip.IP[0]), C.int(ip.Port)) - return nil - } - return ErrInvalidParameter -} - -// IPs returns IPs currently assigned to this tap (including externally or system-assigned IPs) -func (t *nativeTap) IPs() (ips []net.IPNet, err error) { - defer func() { - e := recover() - if e != nil { - err = fmt.Errorf("%v", e) - } - }() - var ipbuf [16384]byte - count := int(C.ZT_GoTap_ips(t.tap, unsafe.Pointer(&ipbuf[0]), 16384)) - ipptr := 0 - for i := 0; i < count; i++ { - af := int(ipbuf[ipptr]) - ipptr++ - switch af { - case syscall.AF_INET: - var ip [4]byte - for j := 0; j < 4; j++ { - ip[j] = ipbuf[ipptr] - ipptr++ - } - bits := ipbuf[ipptr] - ipptr++ - ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 32)}) - case syscall.AF_INET6: - var ip [16]byte - for j := 0; j < 16; j++ { - ip[j] = ipbuf[ipptr] - ipptr++ - } - bits := ipbuf[ipptr] - ipptr++ - ips = append(ips, net.IPNet{IP: net.IP(ip[:]), Mask: net.CIDRMask(int(bits), 128)}) - } - } - return -} - -// DeviceName gets this tap's OS-specific device name -func (t *nativeTap) DeviceName() string { - var dn [256]byte - C.ZT_GoTap_deviceName(t.tap, (*C.char)(unsafe.Pointer(&dn[0]))) - for i, b := range dn { - if b == 0 { - return string(dn[0:i]) - } - } - return "" -} - -// AddMulticastGroupChangeHandler adds a function to be called when the tap subscribes or unsubscribes to a multicast group. -func (t *nativeTap) AddMulticastGroupChangeHandler(handler func(bool, *MulticastGroup)) { - t.multicastGroupHandlersLock.Lock() - t.multicastGroupHandlers = append(t.multicastGroupHandlers, handler) - t.multicastGroupHandlersLock.Unlock() -} - -func handleTapMulticastGroupChange(gn unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t, added bool) { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return - } - node.networksLock.RLock() - network := node.networks[NetworkID(nwid)] - node.networksLock.RUnlock() - if network == nil { - return - } - - node.runWaitGroup.Add(1) - go func() { - defer node.runWaitGroup.Done() - tap, _ := network.tap.(*nativeTap) - if tap != nil { - mg := &MulticastGroup{MAC: MAC(mac), ADI: uint32(adi)} - tap.multicastGroupHandlersLock.Lock() - defer tap.multicastGroupHandlersLock.Unlock() - for _, h := range tap.multicastGroupHandlers { - h(added, mg) - } - } - }() -} - -//export goHandleTapAddedMulticastGroup -func goHandleTapAddedMulticastGroup(gn, _ unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { - handleTapMulticastGroupChange(gn, nwid, mac, adi, true) -} - -//export goHandleTapRemovedMulticastGroup -func goHandleTapRemovedMulticastGroup(gn, _ unsafe.Pointer, nwid, mac C.uint64_t, adi C.uint32_t) { - handleTapMulticastGroupChange(gn, nwid, mac, adi, false) -} diff --git a/attic/go/pkg/zerotier/network.go b/attic/go/pkg/zerotier/network.go deleted file mode 100644 index e7bb43e46..000000000 --- a/attic/go/pkg/zerotier/network.go +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "net" - "sort" - "strconv" - "sync" -) - -// NetworkID is a network's 64-bit unique ID -type NetworkID uint64 - -// NewNetworkIDFromString parses a network ID in string form -func NewNetworkIDFromString(s string) (NetworkID, error) { - if len(s) != 16 { - return NetworkID(0), ErrInvalidNetworkID - } - n, err := strconv.ParseUint(s, 16, 64) - return NetworkID(n), err -} - -// NewNetworkIDFromBytes reads an 8-byte / 64-bit network ID. -func NewNetworkIDFromBytes(b []byte) (NetworkID, error) { - if len(b) < 8 { - return NetworkID(0), ErrInvalidNetworkID - } - return NetworkID(binary.BigEndian.Uint64(b)), nil -} - -// Controller gets the Address of this network's controller. -func (n NetworkID) Controller() Address { - return Address(uint64(n) >> 24) -} - -// String returns this network ID's 16-digit hex identifier -func (n NetworkID) String() string { - return fmt.Sprintf("%.16x", uint64(n)) -} - -// Bytes returns this network ID as an 8-byte / 64-bit big-endian value. -func (n NetworkID) Bytes() []byte { - var b [8]byte - binary.BigEndian.PutUint64(b[:], uint64(n)) - return b[:] -} - -// MarshalJSON marshals this NetworkID as a string -func (n NetworkID) MarshalJSON() ([]byte, error) { - return []byte("\"" + n.String() + "\""), nil -} - -// UnmarshalJSON unmarshals this NetworkID from a string -func (n *NetworkID) UnmarshalJSON(j []byte) error { - var s string - err := json.Unmarshal(j, &s) - if err != nil { - return err - } - *n, err = NewNetworkIDFromString(s) - return err -} - -// NetworkConfig represents the network's current configuration as distributed by its network controller. -type NetworkConfig struct { - // ID is this network's 64-bit globally unique identifier - ID NetworkID `json:"id"` - - // MAC is the Ethernet MAC address of this device on this network - MAC MAC `json:"mac"` - - // Name is a short human-readable name set by the controller - Name string `json:"name"` - - // Status is a status code indicating this network's authorization status - Status int `json:"status"` - - // Type is this network's type - Type int `json:"type"` - - // MTU is the Ethernet MTU for this network - MTU int `json:"mtu"` - - // Bridge is true if this network is allowed to bridge in other devices with different Ethernet addresses - Bridge bool `json:"bridge"` - - // BroadcastEnabled is true if the broadcast (ff:ff:ff:ff:ff:ff) address works (excluding IPv4 ARP which is handled via a special path) - BroadcastEnabled bool `json:"broadcastEnabled"` - - // NetconfRevision is the revision number reported by the controller - NetconfRevision uint64 `json:"netconfRevision"` - - // AssignedAddresses are static IPs assigned by the network controller to this device - AssignedAddresses []InetAddress `json:"assignedAddresses,omitempty"` - - // Routes are static routes assigned by the network controller to this device - Routes []Route `json:"routes,omitempty"` -} - -// NetworkLocalSettings is settings for this network that can be changed locally -type NetworkLocalSettings struct { - // AllowManagedIPs determines whether managed IP assignment is allowed - AllowManagedIPs bool `json:"allowManagedIPs"` - - // AllowGlobalIPs determines if managed IPs that overlap with public Internet addresses are allowed - AllowGlobalIPs bool `json:"allowGlobalIPs"` - - // AllowManagedRoutes determines whether managed routes can be set - AllowManagedRoutes bool `json:"allowManagedRoutes"` - - // AllowGlobalRoutes determines if managed routes can overlap with public Internet addresses - AllowGlobalRoutes bool `json:"allowGlobalRoutes"` - - // AllowDefaultRouteOverride determines if the default (0.0.0.0 or ::0) route on the system can be overridden ("full tunnel" mode) - AllowDefaultRouteOverride bool `json:"allowDefaultRouteOverride"` -} - -// Network is a currently joined network -type Network struct { - node *Node - id NetworkID - mac MAC - tap Tap - config NetworkConfig - settings NetworkLocalSettings // locked by configLock - multicastSubscriptions map[[2]uint64]*MulticastGroup - configLock sync.RWMutex - multicastSubscriptionsLock sync.RWMutex -} - -// newNetwork creates a new network with default settings -func newNetwork(node *Node, id NetworkID, t Tap) (*Network, error) { - m := NewMACForNetworkMember(node.Identity().address, id) - n := &Network{ - node: node, - id: id, - mac: m, - tap: t, - config: NetworkConfig{ - ID: id, - MAC: m, - Status: NetworkStatusRequestingConfiguration, - Type: NetworkTypePrivate, - MTU: int(defaultVirtualNetworkMTU), - }, - settings: NetworkLocalSettings{ - AllowManagedIPs: true, - AllowGlobalIPs: false, - AllowManagedRoutes: true, - AllowGlobalRoutes: false, - AllowDefaultRouteOverride: false, - }, - multicastSubscriptions: make(map[[2]uint64]*MulticastGroup), - } - - t.AddMulticastGroupChangeHandler(func(added bool, mg *MulticastGroup) { - if added { - n.MulticastSubscribe(mg) - } else { - n.MulticastUnsubscribe(mg) - } - }) - - return n, nil -} - -// ID gets this network's unique ID -func (n *Network) ID() NetworkID { return n.id } - -// MAC returns the assigned MAC address of this network -func (n *Network) MAC() MAC { return n.mac } - -// Tap gets this network's tap device -func (n *Network) Tap() Tap { return n.tap } - -// Config returns a copy of this network's current configuration -func (n *Network) Config() NetworkConfig { - n.configLock.RLock() - defer n.configLock.RUnlock() - return n.config -} - -// SetLocalSettings modifies this network's local settings -func (n *Network) SetLocalSettings(ls *NetworkLocalSettings) { n.updateConfig(nil, ls) } - -// LocalSettings gets this network's current local settings -func (n *Network) LocalSettings() NetworkLocalSettings { - n.configLock.RLock() - defer n.configLock.RUnlock() - return n.settings -} - -// MulticastSubscribe subscribes to a multicast group -func (n *Network) MulticastSubscribe(mg *MulticastGroup) { - n.node.infoLog.Printf("%.16x joined multicast group %s", uint64(n.id), mg.String()) - k := mg.key() - n.multicastSubscriptionsLock.Lock() - if _, have := n.multicastSubscriptions[k]; have { - n.multicastSubscriptionsLock.Unlock() - return - } - n.multicastSubscriptions[k] = mg - n.multicastSubscriptionsLock.Unlock() - n.node.multicastSubscribe(uint64(n.id), mg) -} - -// MulticastUnsubscribe removes a subscription to a multicast group -func (n *Network) MulticastUnsubscribe(mg *MulticastGroup) { - n.node.infoLog.Printf("%.16x left multicast group %s", uint64(n.id), mg.String()) - n.multicastSubscriptionsLock.Lock() - delete(n.multicastSubscriptions, mg.key()) - n.multicastSubscriptionsLock.Unlock() - n.node.multicastUnsubscribe(uint64(n.id), mg) -} - -// MulticastSubscriptions returns an array of all multicast subscriptions for this network -func (n *Network) MulticastSubscriptions() []*MulticastGroup { - n.multicastSubscriptionsLock.RLock() - mgs := make([]*MulticastGroup, 0, len(n.multicastSubscriptions)) - for _, mg := range n.multicastSubscriptions { - mgs = append(mgs, mg) - } - n.multicastSubscriptionsLock.RUnlock() - sort.Slice(mgs, func(a, b int) bool { return mgs[a].Less(mgs[b]) }) - return mgs -} - -// leaving is called by Node when the network is being left -func (n *Network) leaving() { - n.tap.Close() -} - -func (n *Network) networkConfigRevision() uint64 { - n.configLock.RLock() - defer n.configLock.RUnlock() - return n.config.NetconfRevision -} - -func networkManagedIPAllowed(ip net.IP, ls *NetworkLocalSettings) bool { - if !ls.AllowManagedIPs { - return false - } - switch ipClassify(ip) { - case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast: - return false - case ipClassificationGlobal: - return ls.AllowGlobalIPs - } - return true -} - -func networkManagedRouteAllowed(r *Route, ls *NetworkLocalSettings) bool { - if !ls.AllowManagedRoutes { - return false - } - bits, _ := r.Target.Mask.Size() - if len(r.Target.IP) > 0 && allZero(r.Target.IP) && bits == 0 { - return ls.AllowDefaultRouteOverride - } - switch ipClassify(r.Target.IP) { - case ipClassificationNone, ipClassificationLoopback, ipClassificationLinkLocal, ipClassificationMulticast: - return false - case ipClassificationGlobal: - return ls.AllowGlobalRoutes - } - return true -} - -func (n *Network) updateConfig(nc *NetworkConfig, ls *NetworkLocalSettings) { - n.configLock.Lock() - defer n.configLock.Unlock() - - if n.tap == nil { // sanity check, should never happen - return - } - - if nc == nil { - nc = &n.config - } - if ls == nil { - ls = &n.settings - } - - // Add IPs to tap that are newly assigned in this config update, - // and remove any IPs from the tap that were assigned that are no - // longer wanted. IPs assigned to the tap externally (e.g. by an - // "ifconfig" command) are left alone. - haveAssignedIPs := make(map[[3]uint64]*InetAddress) - wantAssignedIPs := make(map[[3]uint64]bool) - if n.settings.AllowManagedIPs { - for _, ip := range n.config.AssignedAddresses { - if networkManagedIPAllowed(ip.IP, &n.settings) { // was it allowed? - haveAssignedIPs[ipNetToKey(&ip)] = &ip - } - } - } - if ls.AllowManagedIPs { - for _, ip := range nc.AssignedAddresses { - if networkManagedIPAllowed(ip.IP, ls) { // should it be allowed now? - k := ipNetToKey(&ip) - wantAssignedIPs[k] = true - if _, have := haveAssignedIPs[k]; !have { - n.node.infoLog.Printf("%.16x adding managed IP %s", uint64(n.id), ip.String()) - _ = n.tap.AddIP(&ip) - } - } - } - } - for k, ip := range haveAssignedIPs { - if _, want := wantAssignedIPs[k]; !want { - n.node.infoLog.Printf("%.16x removing managed IP %s", uint64(n.id), ip.String()) - _ = n.tap.RemoveIP(ip) - } - } - - // Do the same for managed routes - haveManagedRoutes := make(map[[6]uint64]*Route) - wantManagedRoutes := make(map[[6]uint64]bool) - if n.settings.AllowManagedRoutes { - for _, r := range n.config.Routes { - if networkManagedRouteAllowed(&r, &n.settings) { // was it allowed? - haveManagedRoutes[r.key()] = &r - } - } - } - if ls.AllowManagedRoutes { - for _, r := range nc.Routes { - if networkManagedRouteAllowed(&r, ls) { // should it be allowed now? - k := r.key() - wantManagedRoutes[k] = true - if _, have := haveManagedRoutes[k]; !have { - n.node.infoLog.Printf("%.16x adding managed route %s", uint64(n.id), r.String()) - //TODO _ = n.tap.AddRoute(&r) - } - } - } - } - for k, r := range haveManagedRoutes { - if _, want := wantManagedRoutes[k]; !want { - n.node.infoLog.Printf("%.16x removing managed route %s", uint64(n.id), r.String()) - //TODO _ = n.tap.RemoveRoute(r) - } - } - - if nc != &n.config { - n.config = *nc - } - if ls != &n.settings { - n.settings = *ls - } -} diff --git a/attic/go/pkg/zerotier/node.go b/attic/go/pkg/zerotier/node.go deleted file mode 100644 index fa8e4ca57..000000000 --- a/attic/go/pkg/zerotier/node.go +++ /dev/null @@ -1,955 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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: -I${SRCDIR}/../../build/core -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "log" - "math/rand" - "net" - "net/http" - "os" - "path" - "reflect" - "sort" - "strings" - "sync" - "sync/atomic" - "syscall" - "time" - "unsafe" - - "github.com/hectane/go-acl" -) - -var nullLogger = log.New(ioutil.Discard, "", 0) - -const ( - NetworkIDStringLength = 16 - NetworkIDLength = 8 - AddressStringLength = 10 - AddressLength = 5 - DefaultPort = int(C.ZT_DEFAULT_PORT) - DefaultRawIPProto = int(C.ZT_DEFAULT_RAW_IP_PROTOCOL) - DefaultEthernetProto = int(C.ZT_DEFAULT_ETHERNET_PROTOCOL) - NetworkMaxShortNameLength = int(C.ZT_MAX_NETWORK_SHORT_NAME_LENGTH) - - NetworkStatusRequestingConfiguration = int(C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION) - NetworkStatusOK = int(C.ZT_NETWORK_STATUS_OK) - NetworkStatusAccessDenied = int(C.ZT_NETWORK_STATUS_ACCESS_DENIED) - NetworkStatusNotFound = int(C.ZT_NETWORK_STATUS_NOT_FOUND) - - NetworkTypePrivate = int(C.ZT_NETWORK_TYPE_PRIVATE) - NetworkTypePublic = int(C.ZT_NETWORK_TYPE_PUBLIC) - - networkConfigOpUp = int(C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP) - networkConfigOpUpdate = int(C.ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE) - defaultVirtualNetworkMTU = int(C.ZT_DEFAULT_MTU) - maxCNodeRefs = 8 // perfectly fine to increase this -) - -var ( - PlatformDefaultHomePath string - CoreVersionMajor int - CoreVersionMinor int - CoreVersionRevision int - CoreVersionBuild int - - // cNodeRefs maps an index to a *Node - cNodeRefs [maxCNodeRefs]*Node - - // cNodeRefUsed maps an index to whether or not the corresponding cNodeRefs[] entry is used. - // This is accessed atomically to provide a really fast way to gate cNodeRefs. - cNodeRefUsed [maxCNodeRefs]uintptr -) - -func init() { - PlatformDefaultHomePath = C.GoString(C.ZT_PLATFORM_DEFAULT_HOMEPATH) - var vMaj, vMin, vRev, vBuild C.int - C.ZT_version(&vMaj, &vMin, &vRev, &vBuild) - CoreVersionMajor = int(vMaj) - CoreVersionMinor = int(vMin) - CoreVersionRevision = int(vRev) - CoreVersionBuild = int(vBuild) -} - -// Node is an instance of a virtual port on the global switch. -type Node struct { - // Time this node was created - startupTime int64 - - // cPtr is an arbitrary pseudo-pointer given to the core to map back to our Go object. - // This is an index into the cNodeRefs array. - cPtr uintptr - - networks map[NetworkID]*Network - networksByMAC map[MAC]*Network // locked by networksLock - networksLock sync.RWMutex - localInterfaceAddresses map[string]net.IP - localInterfaceAddressesLock sync.Mutex - running uintptr // atomic flag - online uintptr // atomic flag - basePath string - peersPath string - certsPath string - networksPath string - localConfigPath string - infoLogPath string - errorLogPath string - localConfig *LocalConfig - previousLocalConfig *LocalConfig - localConfigLock sync.RWMutex - infoLogW *sizeLimitWriter - errLogW *sizeLimitWriter - traceLogW io.Writer - infoLog *log.Logger - errLog *log.Logger - traceLog *log.Logger - namedSocketAPIServer *http.Server - tcpAPIServer *http.Server - - // gn is the GoNode instance, see serviceiocore/GoNode.hpp - gn *C.ZT_GoNode - - // zn is the underlying ZT_Node (ZeroTier::Node) instance - zn unsafe.Pointer - - // id is the identity of this node (includes private key) - id *Identity - - // runWaitGroup is used to wait for all node goroutines on shutdown. - // Any new goroutine is tracked via this wait group so node shutdown can - // itself wait until all goroutines have exited. - runWaitGroup sync.WaitGroup -} - -// NewNode creates and initializes a new instance of the ZeroTier node service -func NewNode(basePath string) (n *Node, err error) { - n = new(Node) - n.startupTime = TimeMs() - - // Register this with the cNodeRefs lookup array and set up a deferred function - // to unregister this if we exit before the end of the constructor such as by - // returning an error. - cPtr := -1 - for i := 0; i < maxCNodeRefs; i++ { - if atomic.CompareAndSwapUintptr(&cNodeRefUsed[i], 0, 1) { - cNodeRefs[i] = n - cPtr = i - n.cPtr = uintptr(i) - break - } - } - if cPtr < 0 { - return nil, ErrInternal - } - defer func() { - if cPtr >= 0 { - atomic.StoreUintptr(&cNodeRefUsed[cPtr], 0) - cNodeRefs[cPtr] = nil - } - }() - - n.networks = make(map[NetworkID]*Network) - n.networksByMAC = make(map[MAC]*Network) - n.localInterfaceAddresses = make(map[string]net.IP) - n.running = 1 - - _ = os.MkdirAll(basePath, 0755) - if _, err = os.Stat(basePath); err != nil { - return - } - n.basePath = basePath - n.peersPath = path.Join(basePath, "peers.d") - _ = os.MkdirAll(n.peersPath, 0700) - _ = acl.Chmod(n.peersPath, 0700) - if _, err = os.Stat(n.peersPath); err != nil { - return - } - n.certsPath = path.Join(basePath, "certs.d") - _ = os.MkdirAll(n.certsPath, 0755) - n.networksPath = path.Join(basePath, "networks.d") - _ = os.MkdirAll(n.networksPath, 0755) - n.localConfigPath = path.Join(basePath, "local.conf") - - _, isTotallyNewNode := os.Stat(path.Join(basePath, "identity.secret")) - n.localConfig = new(LocalConfig) - err = n.localConfig.Read(n.localConfigPath, true, isTotallyNewNode != nil) - if err != nil { - return - } - - n.infoLogPath = path.Join(basePath, "info.log") - n.errorLogPath = path.Join(basePath, "error.log") - if n.localConfig.Settings.LogSizeMax >= 0 { - n.infoLogW, err = sizeLimitWriterOpen(n.infoLogPath) - if err != nil { - return - } - n.errLogW, err = sizeLimitWriterOpen(n.errorLogPath) - if err != nil { - return - } - n.infoLog = log.New(n.infoLogW, "", log.LstdFlags) - n.errLog = log.New(n.errLogW, "", log.LstdFlags) - } else { - n.infoLog = nullLogger - n.errLog = nullLogger - } - - portsChanged := false - portCheckCount := 0 - origPort := n.localConfig.Settings.PrimaryPort - for portCheckCount < 256 { - portCheckCount++ - if checkPort(n.localConfig.Settings.PrimaryPort) { - if n.localConfig.Settings.PrimaryPort != origPort { - n.infoLog.Printf("primary port %d unavailable, found port %d and saved in local.conf", origPort, n.localConfig.Settings.PrimaryPort) - } - break - } - n.localConfig.Settings.PrimaryPort = int(4096 + (randomUInt() % 16384)) - portsChanged = true - } - if portCheckCount == 256 { - return nil, errors.New("unable to bind to primary port: tried configured port and 256 other random ports") - } - if n.localConfig.Settings.SecondaryPort > 0 { - portCheckCount = 0 - origPort = n.localConfig.Settings.SecondaryPort - for portCheckCount < 256 { - portCheckCount++ - if checkPort(n.localConfig.Settings.SecondaryPort) { - if n.localConfig.Settings.SecondaryPort != origPort { - n.infoLog.Printf("secondary port %d unavailable, found port %d (port search enabled)", origPort, n.localConfig.Settings.SecondaryPort) - } - break - } - n.infoLog.Printf("secondary port %d unavailable, trying a random port (port search enabled)", n.localConfig.Settings.SecondaryPort) - if portCheckCount <= 64 { - n.localConfig.Settings.SecondaryPort = unassignedPrivilegedPorts[randomUInt()%uint(len(unassignedPrivilegedPorts))] - } else { - n.localConfig.Settings.SecondaryPort = int(16384 + (randomUInt() % 16384)) - } - portsChanged = true - } - } - if portsChanged { - _ = n.localConfig.Write(n.localConfigPath) - } - - n.namedSocketAPIServer, n.tcpAPIServer, err = createAPIServer(basePath, n) - if err != nil { - n.infoLog.Printf("FATAL: unable to start API server: %s", err.Error()) - return nil, err - } - - cPath := cStr(basePath) - n.gn = C.ZT_GoNode_new((*C.char)(unsafe.Pointer(&cPath[0])), C.uintptr_t(n.cPtr)) - if n.gn == nil { - n.infoLog.Println("FATAL: node initialization failed") - return nil, ErrNodeInitFailed - } - n.zn = unsafe.Pointer(C.ZT_GoNode_getNode(n.gn)) - n.id, err = newIdentityFromCIdentity(C.ZT_Node_identity(n.zn)) - if err != nil { - n.infoLog.Printf("FATAL: error obtaining node's identity") - C.ZT_GoNode_delete(n.gn) - return nil, err - } - - n.runWaitGroup.Add(1) - go func() { - defer n.runWaitGroup.Done() - lastMaintenanceRun := int64(0) - for atomic.LoadUintptr(&n.running) != 0 { - time.Sleep(250 * time.Millisecond) - nowS := time.Now().Unix() - if (nowS - lastMaintenanceRun) >= 30 { - lastMaintenanceRun = nowS - n.runMaintenance() - } - time.Sleep(250 * time.Millisecond) - } - }() - - // Stop deferred cPtr table cleanup function from de-registering this instance - cPtr = -1 - - return n, nil -} - -// Close closes this Node and frees its underlying C++ Node structures -func (n *Node) Close() { - if atomic.SwapUintptr(&n.running, 0) != 0 { - if n.namedSocketAPIServer != nil { - _ = n.namedSocketAPIServer.Close() - } - if n.tcpAPIServer != nil { - _ = n.tcpAPIServer.Close() - } - - C.ZT_GoNode_delete(n.gn) - - n.runWaitGroup.Wait() - - cNodeRefs[n.cPtr] = nil - atomic.StoreUintptr(&cNodeRefUsed[n.cPtr], 0) - } -} - -// Address returns this node's address -func (n *Node) Address() Address { return n.id.address } - -// Identity returns this node's identity (including secret portion) -func (n *Node) Identity() *Identity { return n.id } - -// Online returns true if this node can reach something -func (n *Node) Online() bool { return atomic.LoadUintptr(&n.online) != 0 } - -// LocalInterfaceAddresses are external IPs belonging to physical interfaces on this machine -func (n *Node) LocalInterfaceAddresses() []net.IP { - n.localInterfaceAddressesLock.Lock() - defer n.localInterfaceAddressesLock.Unlock() - var ea []net.IP - for _, a := range n.localInterfaceAddresses { - ea = append(ea, a) - } - sort.Slice(ea, func(a, b int) bool { return bytes.Compare(ea[a], ea[b]) < 0 }) - return ea -} - -// LocalConfig gets this node's local configuration -func (n *Node) LocalConfig() *LocalConfig { - n.localConfigLock.RLock() - defer n.localConfigLock.RUnlock() - return n.localConfig -} - -// SetLocalConfig updates this node's local configuration -func (n *Node) SetLocalConfig(lc *LocalConfig) (restartRequired bool, err error) { - n.networksLock.RLock() - n.localConfigLock.Lock() - defer n.localConfigLock.Unlock() - defer n.networksLock.RUnlock() - - for nid, nc := range lc.Network { - nw := n.networks[nid] - if nw != nil { - nw.SetLocalSettings(&nc) - } - } - - if n.localConfig.Settings.PrimaryPort != lc.Settings.PrimaryPort || n.localConfig.Settings.SecondaryPort != lc.Settings.SecondaryPort || n.localConfig.Settings.LogSizeMax != lc.Settings.LogSizeMax { - restartRequired = true - } - - n.previousLocalConfig = n.localConfig - n.localConfig = lc - - return -} - -// Join a network. -// If tap is nil, the default system tap for this OS/platform is used (if available). -func (n *Node) Join(nwid NetworkID, controllerFingerprint *Fingerprint, settings *NetworkLocalSettings, tap Tap) (*Network, error) { - n.networksLock.RLock() - defer n.networksLock.RUnlock() - - if nw, have := n.networks[nwid]; have { - n.infoLog.Printf("join network %.16x ignored: already a member", nwid) - if settings != nil { - go nw.SetLocalSettings(settings) // "go" this to avoid possible deadlocks - } - return nw, nil - } - - if tap != nil { - panic("non-native taps not yet implemented") - } - var fp *C.ZT_Fingerprint - if controllerFingerprint != nil { - fp = controllerFingerprint.cFingerprint() - } - ntap := C.ZT_GoNode_join(n.gn, C.uint64_t(nwid), fp) - if ntap == nil { - n.infoLog.Printf("join network %.16x failed: tap device failed to initialize (check drivers / kernel modules)", uint64(nwid)) - return nil, ErrTapInitFailed - } - - nw, err := newNetwork(n, nwid, &nativeTap{tap: unsafe.Pointer(ntap), enabled: 1}) - if err != nil { - n.infoLog.Printf("join network %.16x failed: network failed to initialize: %s", nwid, err.Error()) - C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid)) - return nil, err - } - n.networks[nwid] = nw - if settings != nil { - go nw.SetLocalSettings(settings) - } - - return nw, nil -} - -// Leave a network. -func (n *Node) Leave(nwid NetworkID) error { - n.networksLock.Lock() - nw := n.networks[nwid] - delete(n.networks, nwid) - n.networksLock.Unlock() - - if nw != nil { - n.infoLog.Printf("leaving network %.16x", nwid) - nw.leaving() - C.ZT_GoNode_leave(n.gn, C.uint64_t(nwid)) - } - return nil -} - -// Network looks up a network by ID or returns nil if not joined -func (n *Node) Network(nwid NetworkID) *Network { - n.networksLock.RLock() - nw := n.networks[nwid] - n.networksLock.RUnlock() - return nw -} - -// Networks returns a list of networks that this node has joined -func (n *Node) Networks() []*Network { - n.networksLock.RLock() - defer n.networksLock.RUnlock() - var nws []*Network - for _, nw := range n.networks { - nws = append(nws, nw) - } - return nws -} - -// Peers retrieves a list of current peers -func (n *Node) Peers() []*Peer { - var peers []*Peer - pl := C.ZT_Node_peers(n.zn) - if pl != nil { - defer C.ZT_freeQueryResult(unsafe.Pointer(pl)) - for i := uintptr(0); i < uintptr(pl.peerCount); i++ { - p, _ := newPeerFromCPeer((*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_Peer)))) - if p != nil { - peers = append(peers, p) - } - } - } - sort.Slice(peers, func(a, b int) bool { - return peers[a].Address < peers[b].Address - }) - return peers -} - -// Peer looks up a single peer by address or full fingerprint. -// The fpOrAddress parameter may be either. If it is neither nil is returned. -// A nil pointer is returned if nothing is found. -func (n *Node) Peer(fpOrAddress interface{}) *Peer { - fp, _ := fpOrAddress.(*Fingerprint) - if fp == nil { - a, _ := fpOrAddress.(*Address) - if a == nil { - return nil - } - fp = &Fingerprint{Address: *a} - } - pl := C.ZT_Node_peers(n.zn) - if pl != nil { - defer C.ZT_freeQueryResult(unsafe.Pointer(pl)) - for i := uintptr(0); i < uintptr(pl.peerCount); i++ { - p, _ := newPeerFromCPeer((*C.ZT_Peer)(unsafe.Pointer(uintptr(unsafe.Pointer(pl.peers)) + (i * C.sizeof_ZT_Peer)))) - if p != nil && p.Identity.Fingerprint().BestSpecificityEquals(fp) { - return p - } - } - } - return nil -} - -// AddPeer adds a peer by explicit identity. -func (n *Node) AddPeer(id *Identity) error { - if id == nil { - return ErrInvalidParameter - } - rc := C.ZT_Node_addPeer(n.zn, nil, id.cIdentity()) - if rc != 0 { - return ErrInvalidParameter - } - return nil -} - -// TryPeer attempts to contact a peer at a given explicit endpoint. -// The peer may be identified by an Address or a full Fingerprint. Any other -// type for fpOrAddress will return false. -func (n *Node) TryPeer(fpOrAddress interface{}, ep *Endpoint, retries int) bool { - if ep == nil { - return false - } - fp, _ := fpOrAddress.(*Fingerprint) - if fp == nil { - a, _ := fpOrAddress.(*Address) - if a == nil { - return false - } - fp = &Fingerprint{Address: *a} - } - return C.ZT_Node_tryPeer(n.zn, nil, fp.cFingerprint(), &ep.cep, C.int(retries)) != 0 -} - -// ListCertificates lists certificates and their corresponding local trust flags. -func (n *Node) ListCertificates() (certs []LocalCertificate, err error) { - cl := C.ZT_Node_listCertificates(n.zn) - if cl != nil { - defer C.ZT_freeQueryResult(unsafe.Pointer(cl)) - for i := uintptr(0); i < uintptr(cl.certCount); i++ { - c := newCertificateFromCCertificate(unsafe.Pointer(uintptr(unsafe.Pointer(cl.certs)) + (i * pointerSize))) - if c != nil { - lt := *((*C.uint)(unsafe.Pointer(uintptr(unsafe.Pointer(cl.localTrust)) + (i * C.sizeof_int)))) - certs = append(certs, LocalCertificate{Certificate: c, LocalTrust: uint(lt)}) - } - } - } - return -} - -// AddCertificate adds a certificate to this node's local certificate store (after verification). -func (n *Node) AddCertificate(cert *Certificate, localTrust uint) error { - ccert := cert.cCertificate() - defer deleteCCertificate(ccert) - return certificateErrorToError(int(C.ZT_Node_addCertificate(n.zn, nil, C.int64_t(TimeMs()), C.uint(localTrust), (*C.ZT_Certificate)(ccert), nil, 0))) -} - -// DeleteCertificate deletes a certificate from this node's local certificate store. -func (n *Node) DeleteCertificate(serialNo []byte) error { - if len(serialNo) != CertificateSerialNoSize { - return ErrInvalidParameter - } - C.ZT_Node_deleteCertificate(n.zn, nil, unsafe.Pointer(&serialNo[0])) - return nil -} - -/********************************************************************************************************************/ - -func (n *Node) runMaintenance() { - n.localConfigLock.RLock() - defer n.localConfigLock.RUnlock() - - // Get local physical interface addresses, excluding blacklisted and - // ZeroTier-created interfaces. - interfaceAddresses := make(map[string]net.IP) - ifs, _ := net.Interfaces() - if len(ifs) > 0 { - n.networksLock.RLock() - scanInterfaces: - for _, i := range ifs { - for _, bl := range n.localConfig.Settings.InterfacePrefixBlacklist { - if strings.HasPrefix(strings.ToLower(i.Name), strings.ToLower(bl)) { - continue scanInterfaces - } - } - m, _ := NewMACFromBytes(i.HardwareAddr) - if _, isZeroTier := n.networksByMAC[m]; !isZeroTier { - addrs, _ := i.Addrs() - for _, a := range addrs { - ipn, _ := a.(*net.IPNet) - if ipn != nil && len(ipn.IP) > 0 && !ipn.IP.IsLoopback() && !ipn.IP.IsMulticast() && !ipn.IP.IsInterfaceLocalMulticast() && !ipn.IP.IsLinkLocalMulticast() && !ipn.IP.IsLinkLocalUnicast() { - isTemporary := false - if len(ipn.IP) == 16 { - var ss C.struct_sockaddr_storage - if makeSockaddrStorage(ipn.IP, 0, &ss) { - cIfName := C.CString(i.Name) - if C.ZT_isTemporaryV6Address(cIfName, &ss) != 0 { - isTemporary = true - } - C.free(unsafe.Pointer(cIfName)) - } - } - if !isTemporary { - interfaceAddresses[ipn.IP.String()] = ipn.IP - } - } - } - } - } - n.networksLock.RUnlock() - } - - // Open or close locally bound UDP ports for each local interface address. - // This opens ports if they are not already open and then closes ports if - // they are open but no longer seem to exist. - interfaceAddressesChanged := false - ports := make([]int, 0, 2) - if n.localConfig.Settings.PrimaryPort > 0 && n.localConfig.Settings.PrimaryPort < 65536 { - ports = append(ports, n.localConfig.Settings.PrimaryPort) - } - if n.localConfig.Settings.SecondaryPort > 0 && n.localConfig.Settings.SecondaryPort < 65536 { - ports = append(ports, n.localConfig.Settings.SecondaryPort) - } - n.localInterfaceAddressesLock.Lock() - for astr, ipn := range interfaceAddresses { - if _, alreadyKnown := n.localInterfaceAddresses[astr]; !alreadyKnown { - interfaceAddressesChanged = true - ipCstr := C.CString(ipn.String()) - for pn, p := range ports { - n.infoLog.Printf("UDP binding to port %d on interface %s", p, astr) - primary := C.int(0) - if pn == 0 { - primary = 1 - } - C.ZT_GoNode_phyStartListen(n.gn, nil, ipCstr, C.int(p), primary) - } - C.free(unsafe.Pointer(ipCstr)) - } - } - for astr, ipn := range n.localInterfaceAddresses { - if _, stillPresent := interfaceAddresses[astr]; !stillPresent { - interfaceAddressesChanged = true - ipCstr := C.CString(ipn.String()) - for _, p := range ports { - n.infoLog.Printf("UDP closing socket bound to port %d on interface %s", p, astr) - C.ZT_GoNode_phyStopListen(n.gn, nil, ipCstr, C.int(p)) - } - C.free(unsafe.Pointer(ipCstr)) - } - } - n.localInterfaceAddresses = interfaceAddresses - n.localInterfaceAddressesLock.Unlock() - - // Update node's interface address list if detected or configured addresses have changed. - if interfaceAddressesChanged || n.previousLocalConfig == nil || !reflect.DeepEqual(n.localConfig.Settings.ExplicitAddresses, n.previousLocalConfig.Settings.ExplicitAddresses) { - var cAddrs []C.ZT_InterfaceAddress - externalAddresses := make(map[[3]uint64]*InetAddress) - for _, a := range n.localConfig.Settings.ExplicitAddresses { - ak := a.key() - if _, have := externalAddresses[ak]; !have { - externalAddresses[ak] = &a - cAddrs = append(cAddrs, C.ZT_InterfaceAddress{}) - makeSockaddrStorage(a.IP, a.Port, &(cAddrs[len(cAddrs)-1].address)) - cAddrs[len(cAddrs)-1].permanent = 1 // explicit addresses are permanent, meaning they can be put in a locator - } - } - for _, ip := range interfaceAddresses { - for _, p := range ports { - a := InetAddress{IP: ip, Port: p} - ak := a.key() - if _, have := externalAddresses[ak]; !have { - externalAddresses[ak] = &a - cAddrs = append(cAddrs, C.ZT_InterfaceAddress{}) - makeSockaddrStorage(a.IP, a.Port, &(cAddrs[len(cAddrs)-1].address)) - cAddrs[len(cAddrs)-1].permanent = 0 - } - } - } - - if len(cAddrs) > 0 { - C.ZT_Node_setInterfaceAddresses(n.zn, &cAddrs[0], C.uint(len(cAddrs))) - } else { - C.ZT_Node_setInterfaceAddresses(n.zn, nil, 0) - } - } - - // Trim infoLog if it's gone over its size limit - if n.localConfig.Settings.LogSizeMax > 0 && n.infoLogW != nil { - _ = n.infoLogW.trim(n.localConfig.Settings.LogSizeMax*1024, 0.5, true) - } -} - -func (n *Node) multicastSubscribe(nwid uint64, mg *MulticastGroup) { - C.ZT_Node_multicastSubscribe(n.zn, nil, C.uint64_t(nwid), C.uint64_t(mg.MAC), C.ulong(mg.ADI)) -} - -func (n *Node) multicastUnsubscribe(nwid uint64, mg *MulticastGroup) { - C.ZT_Node_multicastUnsubscribe(n.zn, C.uint64_t(nwid), C.uint64_t(mg.MAC), C.ulong(mg.ADI)) -} - -func (n *Node) pathCheck(ip net.IP) bool { - n.localConfigLock.RLock() - defer n.localConfigLock.RUnlock() - for cidr, phy := range n.localConfig.Physical { - if phy.Blacklist { - _, ipn, _ := net.ParseCIDR(cidr) - if ipn != nil && ipn.Contains(ip) { - return false - } - } - } - return true -} - -func (n *Node) pathLookup(id *Identity) (net.IP, int) { - n.localConfigLock.RLock() - defer n.localConfigLock.RUnlock() - 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 - } - return nil, 0 -} - -func (n *Node) makeStateObjectPath(objType int, id []uint64) (string, bool) { - var fp string - secret := false - switch objType { - case C.ZT_STATE_OBJECT_IDENTITY_PUBLIC: - fp = path.Join(n.basePath, "identity.public") - case C.ZT_STATE_OBJECT_IDENTITY_SECRET: - fp = path.Join(n.basePath, "identity.secret") - secret = true - case C.ZT_STATE_OBJECT_LOCATOR: - fp = path.Join(n.basePath, "locator") - case C.ZT_STATE_OBJECT_PEER: - _ = os.Mkdir(n.peersPath, 0700) - fp = path.Join(n.peersPath, fmt.Sprintf("%.10x.peer", id[0])) - secret = true - case C.ZT_STATE_OBJECT_NETWORK_CONFIG: - _ = os.Mkdir(n.networksPath, 0755) - fp = path.Join(n.networksPath, fmt.Sprintf("%.16x.conf", id[0])) - case C.ZT_STATE_OBJECT_TRUST_STORE: - fp = path.Join(n.basePath, "truststore") - case C.ZT_STATE_OBJECT_CERT: - _ = os.Mkdir(n.certsPath, 0755) - fp = path.Join(n.certsPath, Base32.EncodeToString((*[48]byte)(unsafe.Pointer(&id[0]))[:])) - } - return fp, secret -} - -func (n *Node) stateObjectPut(objType int, id []uint64, data []byte) { - fp, secret := n.makeStateObjectPath(objType, id) - if len(fp) > 0 { - fileMode := os.FileMode(0644) - if secret { - fileMode = os.FileMode(0600) - } - _ = ioutil.WriteFile(fp, data, fileMode) - if secret { - _ = acl.Chmod(fp, 0600) // this emulates Unix chmod on Windows and uses os.Chmod on Unix-type systems - } - } -} - -func (n *Node) stateObjectDelete(objType int, id []uint64) { - fp, _ := n.makeStateObjectPath(objType, id) - if len(fp) > 0 { - _ = os.Remove(fp) - } -} - -func (n *Node) stateObjectGet(objType int, id []uint64) ([]byte, bool) { - fp, _ := n.makeStateObjectPath(objType, id) - if len(fp) > 0 { - fd, err := ioutil.ReadFile(fp) - if err != nil { - return nil, false - } - return fd, true - } - return nil, false -} - -func (n *Node) handleTrace(traceMessage string) { - if len(traceMessage) > 0 { - n.infoLog.Print("TRACE: " + traceMessage) - } -} - -/********************************************************************************************************************/ - -// These are callbacks called by the core and GoGlue stuff to talk to the -// service. These launch goroutines to do their work where possible to -// avoid blocking anything in the core. - -//export goPathCheckFunc -func goPathCheckFunc(gn, uptr unsafe.Pointer, af C.int, ip unsafe.Pointer, port C.int) C.int { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return 0 - } - - var nip net.IP - if af == syscall.AF_INET { - nip = ((*[4]byte)(ip))[:] - } else if af == syscall.AF_INET6 { - nip = ((*[16]byte)(ip))[:] - } else { - return 0 - } - if len(nip) > 0 && node.pathCheck(nip) { - return 1 - } - - return 0 -} - -//export goPathLookupFunc -func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFamily int, identity, familyP, ipP, portP unsafe.Pointer) C.int { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return 0 - } - - 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 { - *((*C.int)(familyP)) = C.int(syscall.AF_INET) - copy((*[4]byte)(ipP)[:], ip4) - *((*C.int)(portP)) = C.int(port) - return 1 - } else if len(ip) == 16 { - *((*C.int)(familyP)) = C.int(syscall.AF_INET6) - copy((*[16]byte)(ipP)[:], ip) - *((*C.int)(portP)) = C.int(port) - return 1 - } - } - - return 0 -} - -//export goStateObjectPutFunc -func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return - } - - // NOTE: this is unsafe and depends on node.stateObjectDelete() and node.stateObjectPut() - // not accessing beyond the expected number of elements in the id. - id2 := (*[6]uint64)(id) - var data2 []byte - if len > 0 { - data2 = C.GoBytes(data, len) - } - - if len < 0 { - node.stateObjectDelete(int(objType), id2[:]) - } else { - node.stateObjectPut(int(objType), id2[:], data2) - } -} - -//export goStateObjectGetFunc -func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, dataP unsafe.Pointer) C.int { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return -1 - } - - *((*uintptr)(dataP)) = 0 - tmp, found := node.stateObjectGet(int(objType), ((*[6]uint64)(id))[:]) - if found && len(tmp) > 0 { - cData := C.ZT_malloc(C.ulong(len(tmp))) // GoGlue sends free() to the core as the free function - if uintptr(cData) == 0 { - return -1 - } - *((*uintptr)(dataP)) = uintptr(cData) - return C.int(len(tmp)) - } - - return -1 -} - -//export goVirtualNetworkConfigFunc -func goVirtualNetworkConfigFunc(gn, uptr unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return - } - - node.networksLock.RLock() - network := node.networks[NetworkID(nwid)] - node.networksLock.RUnlock() - - if network != nil { - switch int(op) { - case networkConfigOpUp, networkConfigOpUpdate: - ncc := (*C.ZT_VirtualNetworkConfig)(conf) - if network.networkConfigRevision() > uint64(ncc.netconfRevision) { - return - } - var nc NetworkConfig - nc.ID = NetworkID(ncc.nwid) - nc.MAC = MAC(ncc.mac) - nc.Name = C.GoString(&ncc.name[0]) - nc.Status = int(ncc.status) - nc.Type = int(ncc._type) - nc.MTU = int(ncc.mtu) - nc.Bridge = ncc.bridge != 0 - nc.BroadcastEnabled = ncc.broadcastEnabled != 0 - nc.NetconfRevision = uint64(ncc.netconfRevision) - for i := 0; i < int(ncc.assignedAddressCount); i++ { - a := sockaddrStorageToIPNet(&ncc.assignedAddresses[i]) - if a != nil { - _, bits := a.Mask.Size() - nc.AssignedAddresses = append(nc.AssignedAddresses, InetAddress{IP: a.IP, Port: bits}) - } - } - for i := 0; i < int(ncc.routeCount); i++ { - tgt := sockaddrStorageToIPNet(&ncc.routes[i].target) - viaN := sockaddrStorageToIPNet(&ncc.routes[i].via) - var via *net.IP - if viaN != nil && len(viaN.IP) > 0 { - via = &viaN.IP - } - if tgt != nil { - nc.Routes = append(nc.Routes, Route{ - Target: *tgt, - Via: via, - Flags: uint16(ncc.routes[i].flags), - Metric: uint16(ncc.routes[i].metric), - }) - } - } - - node.runWaitGroup.Add(1) - go func() { - network.updateConfig(&nc, nil) - node.runWaitGroup.Done() - }() - } - } -} - -//export goZtEvent -func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { - node := cNodeRefs[uintptr(gn)] - if node == nil { - return - } - - switch eventType { - case C.ZT_EVENT_OFFLINE: - atomic.StoreUintptr(&node.online, 0) - case C.ZT_EVENT_ONLINE: - atomic.StoreUintptr(&node.online, 1) - } -} diff --git a/attic/go/pkg/zerotier/osdep-posix.go b/attic/go/pkg/zerotier/osdep-posix.go deleted file mode 100644 index fc2fd34bd..000000000 --- a/attic/go/pkg/zerotier/osdep-posix.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build !windows - -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "context" - "net" - "net/http" - "os" - "path" - "time" -) - -func createNamedSocketListener(basePath, name string) (net.Listener, error) { - apiSockPath := path.Join(basePath, name) - _ = os.Remove(apiSockPath) - return net.Listen("unix", apiSockPath) -} - -func createNamedSocketHTTPClient(basePath, name string) (*http.Client, error) { - apiSockPath := path.Join(basePath, name) - return &http.Client{ - Timeout: 10 * time.Second, - Transport: &http.Transport{ - DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { - return net.Dial("unix", apiSockPath) - }, - DisableKeepAlives: true, - DisableCompression: true, - }, - }, nil -} diff --git a/attic/go/pkg/zerotier/osdep-windows.go b/attic/go/pkg/zerotier/osdep-windows.go deleted file mode 100644 index db8198e2d..000000000 --- a/attic/go/pkg/zerotier/osdep-windows.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build windows - -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "net" - "net/http" - - winio "github.com/Microsoft/go-winio" -) - -const windowsAPISocketPathPrefix = "\\\\.\\pipe\\zerotier_" - -func createNamedSocketListener(basePath, name string) (net.Listener, error) { - return winio.ListenPipe(windowsAPISocketPathPrefix+name, nil) -} - -func createNamedSocketHTTPClient(basePath, name string) (*http.Client, error) { - panic("needs implementation") - return nil, nil -} diff --git a/attic/go/pkg/zerotier/path.go b/attic/go/pkg/zerotier/path.go deleted file mode 100644 index 92e711eaa..000000000 --- a/attic/go/pkg/zerotier/path.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -// Path is a path to another peer on the network -type Path struct { - Endpoint Endpoint `json:"endpoint"` - LastSend int64 `json:"lastSend"` - LastReceive int64 `json:"lastReceive"` -} - -func (p *Path) setFromCPath(cp *C.ZT_Path) { - p.Endpoint.setFromCEndpoint(&(cp.endpoint)) - p.LastSend = int64(cp.lastSend) - p.LastReceive = int64(cp.lastReceive) -} diff --git a/attic/go/pkg/zerotier/peer.go b/attic/go/pkg/zerotier/peer.go deleted file mode 100644 index 20887a681..000000000 --- a/attic/go/pkg/zerotier/peer.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -// #include "../../serviceiocore/GoGlue.h" -import "C" - -import "unsafe" - -// Peer is another ZeroTier node -type Peer struct { - Address Address `json:"address"` - Identity *Identity `json:"identity"` - Fingerprint *Fingerprint `json:"fingerprint"` - Version [3]int `json:"version"` - Latency int `json:"latency"` - Root bool `json:"root"` - Paths []Path `json:"paths,omitempty"` - Locator *Locator `json:"locator,omitempty"` -} - -func newPeerFromCPeer(cp *C.ZT_Peer) (p *Peer, err error) { - p = new(Peer) - p.Address = Address(cp.address) - p.Identity, err = newIdentityFromCIdentity(cp.identity) - if err != nil { - return - } - p.Fingerprint = newFingerprintFromCFingerprint(cp.fingerprint) - p.Version[0] = int(cp.versionMajor) - p.Version[1] = int(cp.versionMinor) - p.Version[2] = int(cp.versionRev) - p.Latency = int(cp.latency) - p.Root = cp.root != 0 - p.Paths = make([]Path, int(cp.pathCount)) - for i := range p.Paths { - p.Paths[i].setFromCPath((*C.ZT_Path)(unsafe.Pointer(uintptr(unsafe.Pointer(cp.paths)) + (uintptr(C.sizeof_ZT_Path) * uintptr(i))))) - } - p.Locator, err = NewLocatorFromBytes(C.GoBytes(unsafe.Pointer(cp.locator), C.int(cp.locatorSize))) - return -} diff --git a/attic/go/pkg/zerotier/route.go b/attic/go/pkg/zerotier/route.go deleted file mode 100644 index c65b5fd68..000000000 --- a/attic/go/pkg/zerotier/route.go +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "net" - "unsafe" -) - -// Route represents a route in a host's routing table -type Route struct { - // Target for this route - Target net.IPNet `json:"target"` - - // Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN) - Via *net.IP `json:"via,omitempty"` - - // Route flags (currently unused, always 0) - Flags uint16 `json:"flags"` - - // Metric is an interface metric that can affect route priority (behavior can be OS-specific) - Metric uint16 `json:"metric"` -} - -// String returns a string representation of this route -func (r *Route) String() string { - if r.Via != nil { - return r.Target.String() + "->LAN" - } - return r.Target.String() + "->" + r.Via.String() -} - -// key generates a key suitable for a map[] from this route -func (r *Route) key() (k [6]uint64) { - copy(((*[16]byte)(unsafe.Pointer(&k[0])))[:], r.Target.IP) - ones, bits := r.Target.Mask.Size() - k[2] = (uint64(ones) << 32) | uint64(bits) - if r.Via != nil { - copy(((*[16]byte)(unsafe.Pointer(&k[3])))[:], *r.Via) - } - k[5] = (uint64(r.Flags) << 32) | uint64(r.Metric) - return -} diff --git a/attic/go/pkg/zerotier/sizelimitwriter.go b/attic/go/pkg/zerotier/sizelimitwriter.go deleted file mode 100644 index 30592673a..000000000 --- a/attic/go/pkg/zerotier/sizelimitwriter.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import ( - "io" - "os" - "sync" -) - -type sizeLimitWriter struct { - f *os.File - l sync.Mutex -} - -func sizeLimitWriterOpen(p string) (*sizeLimitWriter, error) { - f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, 0644) - if err != nil { - return nil, err - } - _, _ = f.Seek(0, io.SeekEnd) - return &sizeLimitWriter{f: f}, nil -} - -// Write implements io.Writer -func (w *sizeLimitWriter) Write(b []byte) (int, error) { - w.l.Lock() - defer w.l.Unlock() - return w.f.Write(b) -} - -// Close closes the underlying file -func (w *sizeLimitWriter) Close() error { - w.l.Lock() - defer w.l.Unlock() - return w.f.Close() -} - -func (w *sizeLimitWriter) trim(maxSize int, trimFactor float64, trimAtCR bool) error { - w.l.Lock() - defer w.l.Unlock() - - flen, err := w.f.Seek(0, io.SeekEnd) - if err != nil { - return err - } - - if flen > int64(maxSize) { - var buf [131072]byte - trimAt := int64(float64(maxSize) * trimFactor) - if trimAt >= flen { // sanity check - return nil - } - - if trimAtCR { - lookForCR: - for { - nr, err := w.f.ReadAt(buf[0:1024], trimAt) - if err != nil { - return err - } - for i := 0; i < nr; i++ { - trimAt++ - if buf[i] == byte('\n') { - break lookForCR - } - } - if trimAt >= flen { - return nil - } - } - } - - copyTo := int64(0) - for trimAt < flen { - nr, _ := w.f.ReadAt(buf[:], trimAt) - if nr > 0 { - wr, _ := w.f.WriteAt(buf[0:nr], copyTo) - if wr > 0 { - copyTo += int64(wr) - } else { - break - } - } else { - break - } - } - - err = w.f.Truncate(copyTo) - if err != nil { - return err - } - _, err = w.f.Seek(0, io.SeekEnd) - return err - } - - return nil -} diff --git a/attic/go/pkg/zerotier/tap.go b/attic/go/pkg/zerotier/tap.go deleted file mode 100644 index 73a1aaba7..000000000 --- a/attic/go/pkg/zerotier/tap.go +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2026-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 - -import "net" - -// Tap represents an Ethernet tap connecting a virtual network to a device or something else "real" -type Tap interface { - // Close is called when this tap is being shut down - Close() - - // Type is a string describing what kind of tap this is e.g. "native" for OS-native - Type() string - - // Error returns the most recent error experienced by this tap - Error() (int, string) - - // SetEnabled sets whether this tap will accept and process packets - SetEnabled(enabled bool) - - // Enabled returns the current enabled status - Enabled() bool - - // AddIP assigns an IP address to this tap device - AddIP(ip *InetAddress) error - - // RemoveIP removes an IP address from this tap - RemoveIP(ip *InetAddress) error - - // IPs returns an array of all IPs currently assigned to this tap including those not assigned by ZeroTier - IPs() ([]net.IPNet, error) - - // DeviceName gets the OS-specific device name for this tap or an empty string if none - DeviceName() string - - // AddMulticastGroupChangeHandler registers a function to be called on multicast group subscribe or unsubscribe (first argument) - AddMulticastGroupChangeHandler(func(bool, *MulticastGroup)) -} diff --git a/attic/go/serviceiocore/CMakeLists.txt b/attic/go/serviceiocore/CMakeLists.txt deleted file mode 100644 index c7db87c9e..000000000 --- a/attic/go/serviceiocore/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -project(zt_service_io_core) - -set(src - GoGlue.cpp -) - -set(headers - GoGlue.h -) - -add_library(${PROJECT_NAME} STATIC ${src} ${headers}) -target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11) -target_include_directories( - ${PROJECT_NAME} - PUBLIC - ${CMAKE_BINARY_DIR}/core -) \ No newline at end of file diff --git a/attic/go/serviceiocore/GoGlue.cpp b/attic/go/serviceiocore/GoGlue.cpp deleted file mode 100644 index 8fb3f1f06..000000000 --- a/attic/go/serviceiocore/GoGlue.cpp +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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 _WIN32_WINNT -#define _WIN32_WINNT 0x06010000 -#endif - -#include "GoGlue.h" - -#include "../core/Constants.hpp" -#include "../core/InetAddress.hpp" -#include "../core/Node.hpp" -#include "../core/Utils.hpp" -#include "../core/MAC.hpp" -#include "../core/Address.hpp" -#include "../core/Containers.hpp" -#include "../core/Certificate.hpp" -#include "../osdep/OSUtils.hpp" -#include "../osdep/EthernetTap.hpp" - -#ifndef __WINDOWS__ -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __BSD__ -#include -#endif -#include -#include -#ifdef __LINUX__ -#ifndef IPV6_DONTFRAG -#define IPV6_DONTFRAG 62 -#endif -#endif -#endif // !__WINDOWS__ - -#include -#include -#include -#include - -#ifdef __WINDOWS__ -#define SETSOCKOPT_FLAG_TYPE BOOL -#define SETSOCKOPT_FLAG_TRUE TRUE -#define SETSOCKOPT_FLAG_FALSE FALSE -#else -#define SETSOCKOPT_FLAG_TYPE int -#define SETSOCKOPT_FLAG_TRUE 1 -#define SETSOCKOPT_FLAG_FALSE 0 -#endif - -#ifndef MSG_DONTWAIT -#define MSG_DONTWAIT 0 -#endif - -using namespace ZeroTier; - -struct ZT_GoNodeThread -{ - String ip; - int port; - int af; - bool primary; - std::atomic< bool > run; - std::thread thr; -}; - -struct ZT_GoNode_Impl -{ - void *goUserPtr; - Node *node; - volatile int64_t nextBackgroundTaskDeadline; - - String path; - std::atomic< bool > run; - - Map< ZT_SOCKET, ZT_GoNodeThread > threads; - Map< uint64_t, std::shared_ptr< EthernetTap > > taps; - - std::mutex threads_l; - std::mutex taps_l; - - std::thread backgroundTaskThread; -}; - -static const String defaultHomePath(OSUtils::platformDefaultHomePath()); -const char *const ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str(); - -// These are implemented in Go code. -extern "C" int goPathCheckFunc(void *, const ZT_Identity *, int, const void *, 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 *, const void *, int); -extern "C" int goStateObjectGetFunc(void *, int, const uint64_t *, void **); -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); -extern "C" void goHandleTapRemovedMulticastGroup(void *, ZT_GoTap *, uint64_t, uint64_t, uint32_t); - -static void ZT_GoNode_VirtualNetworkConfigFunction( - ZT_Node *node, - void *uptr, - void *tptr, - uint64_t nwid, - void **nptr, - enum ZT_VirtualNetworkConfigOperation op, - const ZT_VirtualNetworkConfig *cfg) -{ - goVirtualNetworkConfigFunc(reinterpret_cast(uptr)->goUserPtr, reinterpret_cast(*nptr), nwid, op, cfg); -} - -static void ZT_GoNode_VirtualNetworkFrameFunction( - ZT_Node *node, - void *uptr, - void *tptr, - uint64_t nwid, - void **nptr, - uint64_t srcMac, - uint64_t destMac, - unsigned int etherType, - unsigned int vlanId, - const void *data, - unsigned int len) -{ - if (*nptr) - reinterpret_cast(*nptr)->put(MAC(srcMac), MAC(destMac), etherType, data, len); -} - -static void ZT_GoNode_EventCallback( - ZT_Node *node, - void *uptr, - void *tptr, - enum ZT_Event et, - const void *data) -{ - goZtEvent(reinterpret_cast(uptr)->goUserPtr, et, data); -} - -static void ZT_GoNode_StatePutFunction( - ZT_Node *node, - void *uptr, - void *tptr, - enum ZT_StateObjectType objType, - const uint64_t id[2], - const void *data, - int len) -{ - goStateObjectPutFunc( - reinterpret_cast(uptr)->goUserPtr, - objType, - id, - data, - len); -} - -static void _freeFunc(void *p) -{ if (p) free(p); } - -static int ZT_GoNode_StateGetFunction( - ZT_Node *node, - void *uptr, - void *tptr, - enum ZT_StateObjectType objType, - const uint64_t id[2], - void **data, - void (**freeFunc)(void *)) -{ - *freeFunc = &_freeFunc; - return goStateObjectGetFunc( - reinterpret_cast(uptr)->goUserPtr, - (int)objType, - id, - data); -} - -static ZT_INLINE void doUdpSend(ZT_SOCKET sock, const struct sockaddr_storage *addr, const void *data, const unsigned int len, const unsigned int ipTTL) -{ - switch (addr->ss_family) { - case AF_INET: - if (unlikely((ipTTL > 0) && (ipTTL < 255))) { -#ifdef __WINDOWS__ - DWORD tmp = (DWORD)ipTTL; -#else - int tmp = (int)ipTTL; -#endif - setsockopt(sock, IPPROTO_IP, IP_TTL, reinterpret_cast(&tmp), sizeof(tmp)); - sendto(sock, reinterpret_cast(data), len, MSG_DONTWAIT, (const sockaddr *)addr, sizeof(struct sockaddr_in)); - tmp = 255; - setsockopt(sock, IPPROTO_IP, IP_TTL, reinterpret_cast(&tmp), sizeof(tmp)); - } else { - sendto(sock, reinterpret_cast(data), len, MSG_DONTWAIT, (const sockaddr *)addr, sizeof(struct sockaddr_in)); - } - break; - case AF_INET6: - // The ipTTL option isn't currently used with IPv6. It's only used - // with IPv4 "firewall opener" / "NAT buster" preamble packets as part - // of IPv4 NAT traversal. - sendto(sock, reinterpret_cast(data), len, MSG_DONTWAIT, (const sockaddr *)addr, sizeof(struct sockaddr_in6)); - break; - } -} - -static int ZT_GoNode_WirePacketSendFunction( - ZT_Node *node, - void *uptr, - void *tptr, - int64_t localSocket, - const struct sockaddr_storage *addr, - const void *data, - unsigned int len, - unsigned int ipTTL) -{ - if (likely(localSocket > 0)) { - doUdpSend((ZT_SOCKET)localSocket, addr, data, len, ipTTL); - } else { - ZT_GoNode *const gn = reinterpret_cast(uptr); - std::lock_guard< std::mutex > l(gn->threads_l); - for (auto t = gn->threads.begin(); t != gn->threads.end(); ++t) { - if ((t->second.af == addr->ss_family) && (t->second.primary)) { - doUdpSend(t->first, addr, data, len, ipTTL); - break; - } - } - } - return 0; -} - -static int ZT_GoNode_PathCheckFunction( - ZT_Node *node, - void *uptr, - void *tptr, - uint64_t ztAddress, - const ZT_Identity *id, - int64_t localSocket, - const struct sockaddr_storage *sa) -{ - switch (sa->ss_family) { - case AF_INET: - return goPathCheckFunc( - reinterpret_cast(uptr)->goUserPtr, - id, - AF_INET, - &(reinterpret_cast(sa)->sin_addr.s_addr), - Utils::ntoh((uint16_t)reinterpret_cast(sa)->sin_port)); - case AF_INET6: - return goPathCheckFunc( - reinterpret_cast(uptr)->goUserPtr, - id, - AF_INET6, - reinterpret_cast(sa)->sin6_addr.s6_addr, - Utils::ntoh((uint16_t)reinterpret_cast(sa)->sin6_port)); - } - return 0; -} - -static int ZT_GoNode_PathLookupFunction( - ZT_Node *node, - void *uptr, - void *tptr, - uint64_t ztAddress, - const ZT_Identity *id, - int desiredAddressFamily, - struct sockaddr_storage *sa) -{ - int family = 0; - uint8_t ip[16]; - int port = 0; - const int result = goPathLookupFunc( - reinterpret_cast(uptr)->goUserPtr, - ztAddress, - desiredAddressFamily, - id, - &family, - ip, - &port - ); - if (result != 0) { - switch (family) { - case AF_INET: - reinterpret_cast(sa)->sin_family = AF_INET; - memcpy(&(reinterpret_cast(sa)->sin_addr.s_addr), ip, 4); - reinterpret_cast(sa)->sin_port = Utils::hton((uint16_t)port); - return 1; - case AF_INET6: - reinterpret_cast(sa)->sin6_family = AF_INET6; - memcpy(reinterpret_cast(sa)->sin6_addr.s6_addr, ip, 16); - reinterpret_cast(sa)->sin6_port = Utils::hton((uint16_t)port); - return 1; - } - } - return 0; -} - -extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath, uintptr_t userPtr) -{ - try { - struct ZT_Node_Callbacks cb; - - cb.statePutFunction = &ZT_GoNode_StatePutFunction; - cb.stateGetFunction = &ZT_GoNode_StateGetFunction; - cb.wirePacketSendFunction = &ZT_GoNode_WirePacketSendFunction; - cb.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction; - cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction; - cb.eventCallback = &ZT_GoNode_EventCallback; - cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction; - cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction; - - ZT_GoNode_Impl *gn = new ZT_GoNode_Impl; - const int64_t now = OSUtils::now(); - gn->goUserPtr = reinterpret_cast(userPtr); - gn->node = new Node(reinterpret_cast(gn), nullptr, &cb, now); - gn->nextBackgroundTaskDeadline = now; - gn->path = workingPath; - gn->run = true; - - gn->backgroundTaskThread = std::thread([gn] { - int64_t lastCheckedTaps = 0; - while (gn->run) { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - const int64_t now = OSUtils::now(); - - if (now >= gn->nextBackgroundTaskDeadline) - gn->node->processBackgroundTasks(nullptr, now, &(gn->nextBackgroundTaskDeadline)); - - if ((now - lastCheckedTaps) > 10000) { - lastCheckedTaps = now; - std::vector< MulticastGroup > added, removed; - std::lock_guard< std::mutex > tl(gn->taps_l); - for (auto t = gn->taps.begin(); t != gn->taps.end(); ++t) { - added.clear(); - removed.clear(); - t->second->scanMulticastGroups(added, removed); - for (auto g = added.begin(); g != added.end(); ++g) - goHandleTapAddedMulticastGroup(gn, (ZT_GoTap *)t->second.get(), t->first, g->mac().toInt(), g->adi()); - for (auto g = removed.begin(); g != removed.end(); ++g) - goHandleTapRemovedMulticastGroup(gn, (ZT_GoTap *)t->second.get(), t->first, g->mac().toInt(), g->adi()); - } - } - } - }); - - return gn; - } catch (...) { - fprintf(stderr, "FATAL: unable to create new instance of Node (out of memory?)" ZT_EOL_S); - exit(1); - } -} - -extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) -{ - gn->run = false; - - gn->threads_l.lock(); - for (auto t = gn->threads.begin(); t != gn->threads.end(); ++t) { - t->second.run = false; -#ifdef __WINDOWS__ - shutdown(t->first, SD_BOTH); - closesocket(t->first); -#else - shutdown(t->first, SHUT_RDWR); - close(t->first); -#endif - t->second.thr.join(); - } - gn->threads_l.unlock(); - - gn->taps_l.lock(); - for (auto t = gn->taps.begin(); t != gn->taps.end(); ++t) - gn->node->leave(t->first, nullptr, nullptr); - gn->taps.clear(); - gn->taps_l.unlock(); - - gn->backgroundTaskThread.join(); - - gn->node->shutdown(nullptr); - delete gn->node; - - delete gn; -} - -extern "C" ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn) -{ - return gn->node; -} - -static void setCommonUdpSocketSettings(ZT_SOCKET udpSock, const char *dev) -{ - int bufSize = 1048576; - while (bufSize > 131072) { - if (setsockopt(udpSock, SOL_SOCKET, SO_RCVBUF, (const char *)&bufSize, sizeof(bufSize)) == 0) - break; - bufSize -= 131072; - } - bufSize = 1048576; - while (bufSize > 131072) { - if (setsockopt(udpSock, SOL_SOCKET, SO_SNDBUF, (const char *)&bufSize, sizeof(bufSize)) == 0) - break; - bufSize -= 131072; - } - - SETSOCKOPT_FLAG_TYPE fl; - -#ifdef SO_REUSEPORT - fl = SETSOCKOPT_FLAG_TRUE; - setsockopt(udpSock, SOL_SOCKET, SO_REUSEPORT, &fl, sizeof(fl)); -#endif -#ifndef __LINUX__ // linux wants just SO_REUSEPORT - fl = SETSOCKOPT_FLAG_TRUE; - setsockopt(udpSock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&fl), sizeof(fl)); -#endif - - fl = SETSOCKOPT_FLAG_TRUE; - setsockopt(udpSock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast(&fl), sizeof(fl)); - -#ifdef IP_DONTFRAG - fl = SETSOCKOPT_FLAG_FALSE; - setsockopt(udpSock,IPPROTO_IP,IP_DONTFRAG,&fl,sizeof(fl)); -#endif -#ifdef IP_MTU_DISCOVER - fl = SETSOCKOPT_FLAG_FALSE; - setsockopt(udpSock,IPPROTO_IP,IP_MTU_DISCOVER,&fl,sizeof(fl)); -#endif - -#ifdef SO_BINDTODEVICE - if ((dev)&&(strlen(dev))) - setsockopt(udpSock,SOL_SOCKET,SO_BINDTODEVICE,dev,strlen(dev)); -#endif -#if defined(__BSD__) && defined(IP_BOUND_IF) - if ((dev) && (strlen(dev))) { - int idx = if_nametoindex(dev); - if (idx != 0) - setsockopt(udpSock, IPPROTO_IP, IP_BOUND_IF, (void *)&idx, sizeof(idx)); - } -#endif -} - -extern "C" int ZT_GoNode_phyStartListen(ZT_GoNode *gn, const char *dev, const char *ip, const int port, const int primary) -{ - if (strchr(ip, ':')) { - struct sockaddr_in6 in6; - memset(&in6, 0, sizeof(in6)); - in6.sin6_family = AF_INET6; - if (inet_pton(AF_INET6, ip, &(in6.sin6_addr)) <= 0) - return errno; - in6.sin6_port = htons((uint16_t)port); - - ZT_SOCKET udpSock = socket(AF_INET6, SOCK_DGRAM, 0); - if (udpSock == ZT_INVALID_SOCKET) - return errno; - setCommonUdpSocketSettings(udpSock, dev); - SETSOCKOPT_FLAG_TYPE fl = SETSOCKOPT_FLAG_TRUE; - setsockopt(udpSock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&fl, sizeof(fl)); -#ifdef IPV6_DONTFRAG - fl = SETSOCKOPT_FLAG_FALSE; - setsockopt(udpSock, IPPROTO_IPV6, IPV6_DONTFRAG, reinterpret_cast(&fl), sizeof(fl)); -#endif - - if (bind(udpSock, reinterpret_cast(&in6), sizeof(in6)) != 0) - return errno; - - { - std::lock_guard< std::mutex > l(gn->threads_l); - ZT_GoNodeThread &gnt = gn->threads[udpSock]; - gnt.ip = ip; - gnt.port = port; - gnt.af = AF_INET6; - gnt.primary = (primary != 0); - gnt.run = true; - gnt.thr = std::thread([udpSock, gn, &gnt] { - struct sockaddr_in6 in6; - socklen_t salen; - while (gnt.run) { - salen = sizeof(in6); - void *buf = ZT_getBuffer(); - if (buf) { - int s = (int)recvfrom(udpSock, reinterpret_cast(buf), 16384, 0, reinterpret_cast(&in6), &salen); - if (s > 0) { - ZT_Node_processWirePacket( - reinterpret_cast(gn->node), - nullptr, - OSUtils::now(), - (int64_t)udpSock, - reinterpret_cast(&in6), - buf, - (unsigned int)s, - 1, - &(gn->nextBackgroundTaskDeadline)); - } else { - ZT_freeBuffer(buf); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - } - }); - } - } else { - struct sockaddr_in in; - memset(&in, 0, sizeof(in)); - in.sin_family = AF_INET; - if (inet_pton(AF_INET, ip, &(in.sin_addr)) <= 0) - return errno; - in.sin_port = htons((uint16_t)port); - - ZT_SOCKET udpSock = socket(AF_INET, SOCK_DGRAM, 0); - if (udpSock == ZT_INVALID_SOCKET) - return errno; - setCommonUdpSocketSettings(udpSock, dev); -#ifdef SO_NO_CHECK - SETSOCKOPT_FLAG_TYPE fl = SETSOCKOPT_FLAG_TRUE; - setsockopt(udpSock,SOL_SOCKET,SO_NO_CHECK,&fl,sizeof(fl)); -#endif - - if (bind(udpSock, reinterpret_cast(&in), sizeof(in)) != 0) - return errno; - - { - std::lock_guard< std::mutex > l(gn->threads_l); - ZT_GoNodeThread &gnt = gn->threads[udpSock]; - gnt.ip = ip; - gnt.port = port; - gnt.af = AF_INET6; - gnt.primary = (primary != 0); - gnt.run = true; - gnt.thr = std::thread([udpSock, gn, &gnt] { - struct sockaddr_in in4; - socklen_t salen; - while (gnt.run) { - salen = sizeof(in4); - void *buf = ZT_getBuffer(); - if (buf) { - int s = (int)recvfrom(udpSock, reinterpret_cast(buf), sizeof(buf), 0, reinterpret_cast(&in4), &salen); - if (s > 0) { - ZT_Node_processWirePacket( - reinterpret_cast(gn->node), - nullptr, - OSUtils::now(), - (int64_t)udpSock, - reinterpret_cast(&in4), - buf, - (unsigned int)s, - 1, - &(gn->nextBackgroundTaskDeadline)); - } else { - ZT_freeBuffer(buf); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - } - }); - } - } - - return 0; -} - -extern "C" int ZT_GoNode_phyStopListen(ZT_GoNode *gn, const char *dev, const char *ip, const int port) -{ - { - std::lock_guard< std::mutex > l(gn->threads_l); - for (auto t = gn->threads.begin(); t != gn->threads.end();) { - if ((t->second.ip == ip) && (t->second.port == port)) { - t->second.run = false; -#ifdef __WINDOWS__ - shutdown(t->first, SD_BOTH); - closesocket(t->first); -#else - shutdown(t->first, SHUT_RDWR); - close(t->first); -#endif - t->second.thr.join(); - gn->threads.erase(t++); - } else ++t; - } - } - return 0; -} - -static void tapFrameHandler(void *uptr, void *tptr, uint64_t nwid, const MAC &from, const MAC &to, unsigned int etherType, unsigned int vlanId, const void *data, unsigned int len) -{ - ZT_Node_processVirtualNetworkFrame( - reinterpret_cast(reinterpret_cast(uptr)->node), - tptr, - OSUtils::now(), - nwid, - from.toInt(), - to.toInt(), - etherType, - vlanId, - data, - len, - 0, - &(reinterpret_cast(uptr)->nextBackgroundTaskDeadline)); -} - -extern "C" ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn, uint64_t nwid, const ZT_Fingerprint *const controllerFingerprint) -{ - try { - std::lock_guard< std::mutex > l(gn->taps_l); - auto existingTap = gn->taps.find(nwid); - if (existingTap != gn->taps.end()) - return (ZT_GoTap *)existingTap->second.get(); - char tmp[256]; - OSUtils::ztsnprintf(tmp, sizeof(tmp), "ZeroTier Network %.16llx", (unsigned long long)nwid); - std::shared_ptr< EthernetTap > tap(EthernetTap::newInstance(nullptr, gn->path.c_str(), MAC(Address(gn->node->address()), nwid), ZT_DEFAULT_MTU, 0, nwid, tmp, &tapFrameHandler, gn)); - if (!tap) - return nullptr; - gn->taps[nwid] = tap; - gn->node->join(nwid, controllerFingerprint, tap.get(), nullptr); - return (ZT_GoTap *)tap.get(); - } catch (...) { - return nullptr; - } -} - -extern "C" void ZT_GoNode_leave(ZT_GoNode *gn, uint64_t nwid) -{ - std::lock_guard< std::mutex > l(gn->taps_l); - auto existingTap = gn->taps.find(nwid); - if (existingTap != gn->taps.end()) { - gn->node->leave(nwid, nullptr, nullptr); - gn->taps.erase(existingTap); - } -} - -extern "C" void ZT_GoTap_setEnabled(ZT_GoTap *tap, int enabled) -{ - reinterpret_cast(tap)->setEnabled(enabled != 0); -} - -extern "C" int ZT_GoTap_addIp(ZT_GoTap *tap, int af, const void *ip, int netmaskBits) -{ - switch (af) { - case AF_INET: - return (reinterpret_cast(tap)->addIp(InetAddress(ip, 4, (unsigned int)netmaskBits)) ? 1 : 0); - case AF_INET6: - return (reinterpret_cast(tap)->addIp(InetAddress(ip, 16, (unsigned int)netmaskBits)) ? 1 : 0); - } - return 0; -} - -extern "C" int ZT_GoTap_removeIp(ZT_GoTap *tap, int af, const void *ip, int netmaskBits) -{ - switch (af) { - case AF_INET: - return (reinterpret_cast(tap)->removeIp(InetAddress(ip, 4, (unsigned int)netmaskBits)) ? 1 : 0); - case AF_INET6: - return (reinterpret_cast(tap)->removeIp(InetAddress(ip, 16, (unsigned int)netmaskBits)) ? 1 : 0); - } - return 0; -} - -extern "C" int ZT_GoTap_ips(ZT_GoTap *tap, void *buf, unsigned int bufSize) -{ - auto ips = reinterpret_cast(tap)->ips(); - unsigned int p = 0; - uint8_t *const b = reinterpret_cast(buf); - for (auto ip = ips.begin(); ip != ips.end(); ++ip) { - if ((p + 6) > bufSize) - break; - const uint8_t *const ipd = reinterpret_cast(ip->rawIpData()); - if (ip->isV4()) { - b[p++] = AF_INET; - b[p++] = ipd[0]; - b[p++] = ipd[1]; - b[p++] = ipd[2]; - b[p++] = ipd[3]; - b[p++] = (uint8_t)ip->netmaskBits(); - } else if (ip->isV6()) { - if ((p + 18) <= bufSize) { - b[p++] = AF_INET6; - for (int j = 0; j < 16; ++j) - b[p++] = ipd[j]; - b[p++] = (uint8_t)ip->netmaskBits(); - } - } - } - return (int)p; -} - -extern "C" void ZT_GoTap_deviceName(ZT_GoTap *tap, char nbuf[256]) -{ - Utils::scopy(nbuf, 256, reinterpret_cast(tap)->deviceName().c_str()); -} - -extern "C" void ZT_GoTap_setFriendlyName(ZT_GoTap *tap, const char *friendlyName) -{ - reinterpret_cast(tap)->setFriendlyName(friendlyName); -} - -extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap, unsigned int mtu) -{ - reinterpret_cast(tap)->setMtu(mtu); -} - -#if defined(IFA_F_SECONDARY) && !defined(IFA_F_TEMPORARY) -#define IFA_F_TEMPORARY IFA_F_SECONDARY -#endif - -extern "C" int ZT_isTemporaryV6Address(const char *ifname, const struct sockaddr_storage *a) -{ -#if defined(IN6_IFF_TEMPORARY) && defined(SIOCGIFAFLAG_IN6) - static ZT_SOCKET s_tmpV6Socket = ZT_INVALID_SOCKET; - static std::mutex s_lock; - std::lock_guard< std::mutex > l(s_lock); - if (s_tmpV6Socket == ZT_INVALID_SOCKET) { - s_tmpV6Socket = socket(AF_INET6, SOCK_DGRAM, 0); - if (s_tmpV6Socket <= 0) - return 0; - } - struct in6_ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - memcpy(&(ifr.ifr_addr), a, sizeof(sockaddr_in6)); - if (ioctl(s_tmpV6Socket, SIOCGIFAFLAG_IN6, &ifr) < 0) { - return 0; - } - return ((ifr.ifr_ifru.ifru_flags6 & IN6_IFF_TEMPORARY) != 0) ? 1 : 0; -#else - return 0; -#endif -} - -extern "C" void *ZT_malloc(unsigned long s) -{ return (void *)malloc((size_t)s); } diff --git a/attic/go/serviceiocore/GoGlue.h b/attic/go/serviceiocore/GoGlue.h deleted file mode 100644 index 705e29af1..000000000 --- a/attic/go/serviceiocore/GoGlue.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c)2013-2021 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: 2026-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_GONODE_H -#define ZT_GONODE_H - -#include -#include -#include -#include - -#include "../core/zerotier.h" -#include "../core/Constants.hpp" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void ZT_GoTap; -struct ZT_GoNode_Impl; -typedef struct ZT_GoNode_Impl ZT_GoNode; - -extern const char *const ZT_PLATFORM_DEFAULT_HOMEPATH; - -ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr); -void ZT_GoNode_delete(ZT_GoNode *gn); -ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn); -int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,int port,int primary); -int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,int port); -ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint); -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); -int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits); -int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize); -void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]); -void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName); -void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu); - -int ZT_isTemporaryV6Address(const char *ifname,const struct sockaddr_storage *a); - -void *ZT_malloc(unsigned long s); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/attic/go/serviceiocore/README.md b/attic/go/serviceiocore/README.md deleted file mode 100644 index 7cc930d5e..000000000 --- a/attic/go/serviceiocore/README.md +++ /dev/null @@ -1,4 +0,0 @@ -ZeroTier Service I/O Core ------- - -This contains the I/O core and other helper methods for the ZeroTier Service. Most of the actual service beyond this is implemented in Go. diff --git a/core/Blob.hpp b/core/Blob.hpp index 018bb1ac6..959e46670 100644 --- a/core/Blob.hpp +++ b/core/Blob.hpp @@ -65,55 +65,7 @@ struct SHA384Hash { return (memcmp(data, b.data, 48) >= 0); } }; -/** - * Blob type for 128-bit GUIDs, UUIDs, etc. - */ -struct UniqueID -{ - uint64_t data[2]; - - ZT_INLINE UniqueID() noexcept - { Utils::zero< sizeof(data) >(data); } - - ZT_INLINE UniqueID(const uint64_t a, const uint64_t b) noexcept - { - data[0] = a; - data[1] = b; - } - - explicit ZT_INLINE UniqueID(const void *const d) noexcept - { Utils::copy< 16 >(data, d); } - - ZT_INLINE const uint8_t *bytes() const noexcept - { return reinterpret_cast(data); } - - ZT_INLINE unsigned long hashCode() const noexcept - { return (unsigned long)(data[0] ^ data[1]); } - - ZT_INLINE operator bool() const noexcept - { return ((data[0] != 0) && (data[1] != 0)); } - - ZT_INLINE bool operator==(const UniqueID &b) const noexcept - { return ((data[0] == b.data[0]) && (data[1] == b.data[1])); } - - ZT_INLINE bool operator!=(const UniqueID &b) const noexcept - { return !(*this == b); } - - ZT_INLINE bool operator<(const UniqueID &b) const noexcept - { return (memcmp(data, b.data, 16) < 0); } - - ZT_INLINE bool operator>(const UniqueID &b) const noexcept - { return (memcmp(data, b.data, 16) > 0); } - - ZT_INLINE bool operator<=(const UniqueID &b) const noexcept - { return (memcmp(data, b.data, 16) <= 0); } - - ZT_INLINE bool operator>=(const UniqueID &b) const noexcept - { return (memcmp(data, b.data, 16) >= 0); } -}; - static_assert(sizeof(SHA384Hash) == 48, "SHA384Hash contains unnecessary padding"); -static_assert(sizeof(UniqueID) == 16, "UniqueID contains unnecessary padding"); template< unsigned long S > struct Blob diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index a19ef03a5..81f292e6f 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -56,6 +56,7 @@ set(core_headers Topology.hpp Trace.hpp TriviallyCopyable.hpp + TrustStore.hpp Utils.hpp VL1.hpp VL2.hpp @@ -96,6 +97,7 @@ set(core_src TagCredential.cpp Topology.cpp Trace.cpp + TrustStore.cpp Utils.cpp VL1.cpp VL2.cpp diff --git a/core/Certificate.cpp b/core/Certificate.cpp index 544a60995..f4a8a3c1a 100644 --- a/core/Certificate.cpp +++ b/core/Certificate.cpp @@ -443,13 +443,22 @@ bool Certificate::sign(const Identity &issuer) return false; } -ZT_CertificateError Certificate::verify() const +ZT_CertificateError Certificate::verify(const int64_t clock, const bool checkSignatures) const { try { + if (this->validity[0] > this->validity[1]) { + return ZT_CERTIFICATE_ERROR_INVALID_FORMAT; + } + if ((clock >= 0) && ((this->validity[0] > clock) || (this->validity[1] < clock))) { + return ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW; + } + if (this->issuer) { - const Vector< uint8_t > enc(encode(true)); - if (!reinterpret_cast(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize)) - return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + if (checkSignatures) { + const Vector< uint8_t > enc(encode(true)); + if (!reinterpret_cast(this->issuer)->verify(enc.data(), (unsigned int)enc.size(), this->signature, this->signatureSize)) + return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; + } } else { return ZT_CERTIFICATE_ERROR_INVALID_PRIMARY_SIGNATURE; } @@ -461,16 +470,18 @@ ZT_CertificateError Certificate::verify() const (this->subject.uniqueId[0] != ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384)) return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; - Dictionary d; - m_encodeSubject(this->subject, d, true); - Vector< uint8_t > enc; - d.encode(enc); + if (checkSignatures) { + Dictionary d; + m_encodeSubject(this->subject, d, true); + Vector< uint8_t > enc; + d.encode(enc); - uint8_t h[ZT_SHA384_DIGEST_SIZE]; - SHA384(h, enc.data(), (unsigned int)enc.size()); - static_assert(ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE == (ZT_ECC384_PUBLIC_KEY_SIZE + 1), "incorrect size"); - if (!ECC384ECDSAVerify(this->subject.uniqueId + 1, h, this->subject.uniqueIdProofSignature)) - return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + uint8_t h[ZT_SHA384_DIGEST_SIZE]; + SHA384(h, enc.data(), (unsigned int)enc.size()); + static_assert(ZT_CERTIFICATE_UNIQUE_ID_TYPE_NIST_P_384_SIZE == (ZT_ECC384_PUBLIC_KEY_SIZE + 1), "incorrect size"); + if (!ECC384ECDSAVerify(this->subject.uniqueId + 1, h, this->subject.uniqueIdProofSignature)) + return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; + } } else if (this->subject.uniqueIdSize > 0) { return ZT_CERTIFICATE_ERROR_INVALID_UNIQUE_ID_PROOF; } @@ -478,11 +489,13 @@ ZT_CertificateError Certificate::verify() const for (unsigned int i = 0; i < this->subject.identityCount; ++i) { if (!this->subject.identities[i].identity) return ZT_CERTIFICATE_ERROR_MISSING_REQUIRED_FIELDS; - if (!reinterpret_cast(this->subject.identities[i].identity)->locallyValidate()) - return ZT_CERTIFICATE_ERROR_INVALID_IDENTITY; - if (this->subject.identities[i].locator) { - if (!reinterpret_cast(this->subject.identities[i].locator)->verify(*reinterpret_cast(this->subject.identities[i].identity))) - return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; + if (checkSignatures) { + if (!reinterpret_cast(this->subject.identities[i].identity)->locallyValidate()) + return ZT_CERTIFICATE_ERROR_INVALID_IDENTITY; + if (this->subject.identities[i].locator) { + if (!reinterpret_cast(this->subject.identities[i].locator)->verify(*reinterpret_cast(this->subject.identities[i].identity))) + return ZT_CERTIFICATE_ERROR_INVALID_COMPONENT_SIGNATURE; + } } } diff --git a/core/Certificate.hpp b/core/Certificate.hpp index 32db7cfc9..3dd97e9d7 100644 --- a/core/Certificate.hpp +++ b/core/Certificate.hpp @@ -18,7 +18,6 @@ #include "SHA512.hpp" #include "C25519.hpp" #include "ECC384.hpp" -#include "SharedPtr.hpp" #include "Identity.hpp" #include "Locator.hpp" #include "Dictionary.hpp" @@ -47,9 +46,6 @@ namespace ZeroTier { */ class Certificate : public ZT_Certificate { - friend class SharedPtr< Certificate >; - friend class SharedPtr< const Certificate >; - public: Certificate() noexcept; explicit Certificate(const ZT_Certificate &apiCert); @@ -159,9 +155,11 @@ public: * This doesn't check the entire certificate chain, just the validity of * the certificate's internal signature and fields. * + * @param clock If non-negative, check that certificate is in valid time window + * @param checkSignatures If true, perform full signature check (which is more expensive than other checks) * @return OK (0) or error code indicating why certificate failed verification. */ - ZT_CertificateError verify() const; + ZT_CertificateError verify(int64_t clock, bool checkSignatures) const; /** * Create a CSR that encodes the subject of this certificate @@ -230,8 +228,6 @@ private: Vector< uint8_t > m_subjectUniqueId; Vector< uint8_t > m_subjectUniqueIdProofSignature; Vector< uint8_t > m_signature; - - std::atomic< int > __refCount; }; } // namespace ZeroTier diff --git a/core/InetAddress.hpp b/core/InetAddress.hpp index 4b4db028d..312a404a8 100644 --- a/core/InetAddress.hpp +++ b/core/InetAddress.hpp @@ -19,7 +19,6 @@ #include "MAC.hpp" #include "Containers.hpp" #include "TriviallyCopyable.hpp" -#include "Blob.hpp" namespace ZeroTier { @@ -507,34 +506,6 @@ public: ZT_INLINE bool operator>=(const InetAddress &a) const noexcept { return !(*this < a); } - /** - * Generate a local unique key for this address - * - * This key is not comparable across instances or architectures. - * - * @return Local unique key - */ - ZT_INLINE UniqueID key() const noexcept - { - if (as.ss.ss_family == AF_INET) { - // For IPv4 we can just pack the IP and port into the first element. - return UniqueID(((uint64_t)as.sa_in.sin_addr.s_addr << 16U) ^ (uint64_t)as.sa_in.sin_port, 0); - } else if (likely(as.ss.ss_family == AF_INET6)) { - // The OR with (a2 == 0) is to make sure the second part of the UniqueID - // can never be zero, otherwise it could be made to collide with an IPv4 - // IP address. We also construct this to make it so someone in a /64 - // can't collide another address in the same /64. - const uint64_t a1 = Utils::loadMachineEndian< uint64_t >(as.sa_in6.sin6_addr.s6_addr); - const uint64_t a2 = Utils::hash64(Utils::s_mapNonce ^ Utils::loadMachineEndian< uint64_t >(as.sa_in6.sin6_addr.s6_addr + 8)) + (uint64_t)as.sa_in6.sin6_port; - return UniqueID(a1, a2 | (uint64_t)(a2 == 0)); - } else if (likely(as.ss.ss_family == 0)) { - return UniqueID(0, 0); - } else { - // This should never be reached, but handle it somehow. - return UniqueID(as.ss.ss_family, Utils::fnv1a32(&as, sizeof(as))); - } - } - /** * Compute an IPv6 link-local address * diff --git a/core/OS.hpp b/core/OS.hpp index 000aebefc..64c6b6ae8 100644 --- a/core/OS.hpp +++ b/core/OS.hpp @@ -168,13 +168,16 @@ #endif #endif -/* Right now we fail if no C++11. The core could be ported to old C++ compilers - * if a shim for were included. */ -#ifndef __CPP11__ -#error TODO: to build on pre-c++11 compilers we will need to make a subset of std::atomic for integers -#define nullptr (0) -#define constexpr ZT_INLINE -#define noexcept throw() +#if defined(ZT_ARCH_X64) || defined(__aarch64__) +#ifndef ZT_ARCH_APPEARS_64BIT +#define ZT_ARCH_APPEARS_64BIT 1 +#endif +#endif +#ifdef UINTPTR_MAX +#if UINTPTR_MAX == UINT64_MAX +#ifndef ZT_ARCH_APPEARS_64BIT +#define ZT_ARCH_APPEARS_64BIT 1 +#endif #endif #endif @@ -190,6 +193,17 @@ #endif #endif +/* Right now we fail if no C++11. The core could be ported to old C++ compilers + * if a shim for were included. */ +#ifndef __CPP11__ +#error TODO: to build on pre-c++11 compilers we will need to make a subset of std::atomic for integers +#define nullptr (0) +#define constexpr ZT_INLINE +#define noexcept throw() +#define explicit +#endif +#endif + #ifndef restrict #if defined(__GNUC__) || defined(__clang__) #define restrict __restrict__ diff --git a/core/Path.hpp b/core/Path.hpp index a9b62f778..4b3d55652 100644 --- a/core/Path.hpp +++ b/core/Path.hpp @@ -42,6 +42,93 @@ class Path class Defragmenter; public: + /** + * Map key for paths designed for very fast lookup + */ + class Key + { + public: + /** + * Construct key with undefined value + */ + ZT_INLINE Key() noexcept + {} + + ZT_INLINE Key(const Key &k) noexcept: m_hashCode(k.m_hashCode), m_v664(k.m_v664), m_port(k.m_port) + {} + + ZT_INLINE Key(const InetAddress &ip) noexcept + { + const unsigned int family = ip.as.sa.sa_family; + if (family == AF_INET) { + const uint16_t p = (uint16_t)ip.as.sa_in.sin_port; + m_hashCode = Utils::hash64((((uint64_t)ip.as.sa_in.sin_addr.s_addr) << 16U) ^ ((uint64_t)p) ^ Utils::s_mapNonce); + m_v664 = 0; // IPv6 /64 is 0 for IPv4 + m_port = p; + } else { + if (likely(family == AF_INET6)) { + const uint64_t a = Utils::loadMachineEndian< uint64_t >(reinterpret_cast(ip.as.sa_in6.sin6_addr.s6_addr)); + const uint64_t b = Utils::loadMachineEndian< uint64_t >(reinterpret_cast(ip.as.sa_in6.sin6_addr.s6_addr) + 8); + const uint16_t p = ip.as.sa_in6.sin6_port; + m_hashCode = Utils::hash64(a ^ b ^ ((uint64_t)p) ^ Utils::s_mapNonce); + m_v664 = a; // IPv6 /64 + m_port = p; + } else { + // This isn't reachable since only IPv4 and IPv6 are used with InetAddress, but implement + // something here for technical completeness. + m_hashCode = Utils::fnv1a32(&ip, sizeof(InetAddress)); + m_v664 = Utils::fnv1a32(ip.as.sa.sa_data, sizeof(ip.as.sa.sa_data)); + m_port = (uint16_t)family; + } + } + } + + ZT_INLINE Key &operator=(const Key &k) noexcept + { + m_hashCode = k.m_hashCode; + m_v664 = k.m_v664; + m_port = k.m_port; + return *this; + } + + ZT_INLINE unsigned long hashCode() const noexcept + { return (unsigned long)m_hashCode; } + + ZT_INLINE bool operator==(const Key &k) const noexcept + { return (m_hashCode == k.m_hashCode) && (m_v664 == k.m_v664) && (m_port == k.m_port); } + + ZT_INLINE bool operator!=(const Key &k) const noexcept + { return (!(*this == k)); } + + ZT_INLINE bool operator<(const Key &k) const noexcept + { + if (m_hashCode < k.m_hashCode) { + return true; + } else if (m_hashCode == k.m_hashCode) { + if (m_v664 < k.m_v664) { + return true; + } else if (m_v664 == k.m_v664) { + return (m_port < k.m_port); + } + } + return false; + } + + ZT_INLINE bool operator>(const Key &k) const noexcept + { return (k < *this); } + + ZT_INLINE bool operator<=(const Key &k) const noexcept + { return !(k < *this); } + + ZT_INLINE bool operator>=(const Key &k) const noexcept + { return !(*this < k); } + + private: + uint64_t m_hashCode; + uint64_t m_v664; + uint16_t m_port; + }; + ZT_INLINE Path(const int64_t l, const InetAddress &r) noexcept: m_localSocket(l), m_lastIn(0), diff --git a/core/Peer.hpp b/core/Peer.hpp index 9ff11d887..f68f1ad7e 100644 --- a/core/Peer.hpp +++ b/core/Peer.hpp @@ -106,17 +106,17 @@ public: * This checks the locator's timestamp against the current locator and * replace it if newer. * - * SECURITY: note that this does NOT validate the locator's signature - * or structural validity. This MUST be done before calling this. - * * @param loc Locator update + * @param verify If true, verify locator's signature and structure * @return New locator or previous if it was not replaced. */ - ZT_INLINE SharedPtr< const Locator > setLocator(const SharedPtr< const Locator > &loc) noexcept + ZT_INLINE SharedPtr< const Locator > setLocator(const SharedPtr< const Locator > &loc, bool verify) noexcept { RWMutex::Lock l(m_lock); - if ((loc) && ((!m_locator) || (m_locator->timestamp() < loc->timestamp()))) - m_locator = loc; + if ((loc) && ((!m_locator) || (m_locator->timestamp() < loc->timestamp()))) { + if ((!verify) || loc->verify(m_id)) + m_locator = loc; + } return m_locator; } diff --git a/core/SharedPtr.hpp b/core/SharedPtr.hpp index 6a7fc5a99..2fee9e8bf 100644 --- a/core/SharedPtr.hpp +++ b/core/SharedPtr.hpp @@ -146,6 +146,14 @@ public: return 0; } + /** + * Cast this SharedPtr<> to one that holds a const instance of the type + * + * @return "this" casted in place to hold "const T" + */ + ZT_INLINE const SharedPtr &constify() const noexcept + { return reinterpret_cast< const SharedPtr >(*this); } + ZT_INLINE unsigned long hashCode() const noexcept { return (unsigned long)Utils::hash64((uint64_t)((uintptr_t)m_ptr)); } diff --git a/core/Topology.cpp b/core/Topology.cpp index bcf9dafa4..cfc12d984 100644 --- a/core/Topology.cpp +++ b/core/Topology.cpp @@ -18,34 +18,7 @@ namespace ZeroTier { Topology::Topology(const RuntimeEnvironment *renv, void *tPtr, const int64_t now) : RR(renv) -{ - char tmp[32]; - Dictionary d; - - Vector< uint8_t > trustData(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256, 0)); - if (trustData.empty() || (!d.decode(trustData.data(), (unsigned int)trustData.size()))) { - if (!d.decode(Defaults::CERTIFICATES, Defaults::CERTIFICATES_BYTES)) - d.clear(); - } - - if (!d.empty()) { - const unsigned long certCount = (unsigned long)d.getUI("c$"); - for (unsigned long idx = 0; idx < certCount; ++idx) { - uint64_t id[6]; - const Vector< uint8_t > &serialNo = d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)]; - if (serialNo.size() == ZT_SHA384_DIGEST_SIZE) { - Utils::copy< 48 >(id, serialNo.data()); - Certificate cert; - Vector< uint8_t > enc(RR->node->stateObjectGet(tPtr, ZT_STATE_OBJECT_CERT, id, 6)); - if (cert.decode(enc.data(), (unsigned int)enc.size())) - addCertificate(tPtr, cert, now, (unsigned int)d.getUI(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx)), false, false, false); - } - } - } - - m_cleanCertificates(tPtr, now); - m_updateRootPeers(tPtr, now); -} +{} SharedPtr< Peer > Topology::add(void *tPtr, const SharedPtr< Peer > &peer) { @@ -77,19 +50,6 @@ void Topology::allPeers(Vector< SharedPtr< Peer > > &allPeers, Vector< SharedPtr void Topology::doPeriodicTasks(void *tPtr, const int64_t now) { - // Clean any expired certificates, updating roots if they have changed. - { - Mutex::Lock l1(m_certs_l); - if (m_cleanCertificates(tPtr, now)) { - m_writeTrustStore(tPtr); - { - Mutex::Lock l2(m_roots_l); - RWMutex::Lock l3(m_peers_l); - m_updateRootPeers(tPtr, now); - } - } - } - // Get a list of root peer pointer addresses for filtering during peer cleanup. Vector< uintptr_t > rootLookup; { @@ -137,19 +97,19 @@ void Topology::doPeriodicTasks(void *tPtr, const int64_t now) // and delete those that actually are GC'd. Write lock is aquired only briefly on delete // just as with peers. { - Vector< UniqueID > possibleDelete; + Vector< Path::Key > possibleDelete; { RWMutex::RLock l1(m_paths_l); - for (Map< UniqueID, SharedPtr< Path > >::iterator i(m_paths.begin()); i != m_paths.end(); ++i) { + for (Map< Path::Key, SharedPtr< Path > >::iterator i(m_paths.begin()); i != m_paths.end(); ++i) { if (i->second.references() <= 1) possibleDelete.push_back(i->first); } } if (!possibleDelete.empty()) { ZT_SPEW("garbage collecting (likely) %u orphaned paths", (unsigned int)possibleDelete.size()); - for (Vector< UniqueID >::const_iterator i(possibleDelete.begin()); i != possibleDelete.end(); ++i) { + for (Vector< Path::Key >::const_iterator i(possibleDelete.begin()); i != possibleDelete.end(); ++i) { RWMutex::Lock l1(m_paths_l); - Map< UniqueID, SharedPtr< Path > >::iterator p(m_paths.find(*i)); + Map< Path::Key, SharedPtr< Path > >::iterator p(m_paths.find(*i)); if ((p != m_paths.end()) && p->second.weakGC()) m_paths.erase(p); } @@ -159,122 +119,9 @@ void Topology::doPeriodicTasks(void *tPtr, const int64_t now) void Topology::saveAll(void *tPtr) { - { - RWMutex::RLock l(m_peers_l); - for (Map< Address, SharedPtr< Peer > >::iterator i(m_peers.begin()); i != m_peers.end(); ++i) - i->second->save(tPtr); - } - { - Mutex::Lock l(m_certs_l); - m_writeTrustStore(tPtr); - } -} - -ZT_CertificateError Topology::addCertificate(void *tPtr, const Certificate &cert, const int64_t now, const unsigned int localTrust, const bool writeToLocalStore, const bool refreshRootSets, const bool verify) -{ - { - const SHA384Hash serial(cert.serialNo); - p_CertEntry certEntry; - Mutex::Lock l1(m_certs_l); - - { - Map< SHA384Hash, p_CertEntry >::iterator c(m_certs.find(serial)); - if (c != m_certs.end()) { - if (c->second.localTrust == localTrust) - return ZT_CERTIFICATE_ERROR_NONE; - certEntry.certificate = c->second.certificate; - } - } - if (!certEntry.certificate) { - certEntry.certificate.set(new Certificate(cert)); - if (verify) { - m_cleanCertificates(tPtr, now); - const ZT_CertificateError err = m_verifyCertificate(cert, now, localTrust, false); - if (err != ZT_CERTIFICATE_ERROR_NONE) - return err; - } - } - - certEntry.localTrust = localTrust; - - if ((cert.subject.uniqueId) && (cert.subject.uniqueIdSize > 0)) { - SHA384Hash uniqueIdHash; - SHA384(uniqueIdHash.data, cert.subject.uniqueId, cert.subject.uniqueIdSize); - p_CertEntry &bySubjectUniqueId = m_certsBySubjectUniqueID[uniqueIdHash]; - if (bySubjectUniqueId.certificate) { - if (bySubjectUniqueId.certificate->subject.timestamp >= cert.subject.timestamp) - return ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT; - m_eraseCertificate(tPtr, bySubjectUniqueId.certificate, &uniqueIdHash); - m_certsBySubjectUniqueID[uniqueIdHash] = certEntry; - } else { - bySubjectUniqueId = certEntry; - } - } - - for (unsigned int i = 0; i < cert.subject.identityCount; ++i) { - const Identity *const ii = reinterpret_cast(cert.subject.identities[i].identity); - if (ii) - m_certsBySubjectIdentity[ii->fingerprint()][certEntry.certificate] = localTrust; - } - - m_certs[serial] = certEntry; - - if (refreshRootSets) { - Mutex::Lock l2(m_roots_l); - RWMutex::Lock l3(m_peers_l); - m_updateRootPeers(tPtr, now); - } - - if (writeToLocalStore) - m_writeTrustStore(tPtr); - } - - if (writeToLocalStore) { - Vector< uint8_t > certData(cert.encode()); - uint64_t id[6]; - Utils::copy< 48 >(id, cert.serialNo); - RR->node->stateObjectPut(tPtr, ZT_STATE_OBJECT_CERT, id, 6, certData.data(), (unsigned int)certData.size()); - } - - return ZT_CERTIFICATE_ERROR_NONE; -} - -unsigned int Topology::deleteCertificate(void *tPtr,const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE]) -{ - Mutex::Lock l(m_certs_l); - const unsigned long origCertCount = (unsigned long)m_certs.size(); - Map< SHA384Hash, p_CertEntry >::const_iterator c(m_certs.find(SHA384Hash(serialNo))); - if (c != m_certs.end()) { - if ((c->second.certificate->subject.uniqueId) && (c->second.certificate->subject.uniqueIdSize > 0)) { - SHA384Hash uniqueIdHash; - SHA384(uniqueIdHash.data, c->second.certificate->subject.uniqueId, c->second.certificate->subject.uniqueIdSize); - m_eraseCertificate(tPtr, c->second.certificate, &uniqueIdHash); - } else { - m_eraseCertificate(tPtr, c->second.certificate, nullptr); - } - - const int64_t now = RR->node->now(); - m_cleanCertificates(tPtr, now); - m_writeTrustStore(tPtr); - { - Mutex::Lock l2(m_roots_l); - RWMutex::Lock l3(m_peers_l); - m_updateRootPeers(tPtr, now); - } - } - return (unsigned int)(origCertCount - (unsigned long)m_certs.size()); -} - -void Topology::allCerts(Vector< SharedPtr > &c,Vector< unsigned int > &t) const noexcept -{ - Mutex::Lock l(m_certs_l); - const unsigned long cs = (unsigned long)m_certs.size(); - c.reserve(cs); - t.reserve(cs); - for(Map< SHA384Hash, p_CertEntry >::const_iterator i(m_certs.begin());i!=m_certs.end();++i) { - c.push_back(i->second.certificate); - t.push_back(i->second.localTrust); - } + RWMutex::RLock l(m_peers_l); + for (Map< Address, SharedPtr< Peer > >::iterator i(m_peers.begin()); i != m_peers.end(); ++i) + i->second->save(tPtr); } struct p_RootRankingComparisonOperator @@ -315,115 +162,6 @@ void Topology::m_rankRoots(const int64_t now) } } -void Topology::m_eraseCertificate(void *tPtr, const SharedPtr< const Certificate > &cert, const SHA384Hash *uniqueIdHash) -{ - // assumes m_certs is locked for writing - - const SHA384Hash serialNo(cert->serialNo); - m_certs.erase(serialNo); - - if (uniqueIdHash) - m_certsBySubjectUniqueID.erase(*uniqueIdHash); - - for (unsigned int i = 0; i < cert->subject.identityCount; ++i) { - const Identity *const ii = reinterpret_cast(cert->subject.identities[i].identity); - Map< Fingerprint, Map< SharedPtr< const Certificate >, unsigned int > >::iterator bySubjectIdentity(m_certsBySubjectIdentity.find(ii->fingerprint())); - if (bySubjectIdentity != m_certsBySubjectIdentity.end()) { - bySubjectIdentity->second.erase(cert); - if (bySubjectIdentity->second.empty()) - m_certsBySubjectIdentity.erase(bySubjectIdentity); - } - } - - RR->node->stateObjectDelete(tPtr, ZT_STATE_OBJECT_CERT, serialNo.data, 6); -} - -bool Topology::m_cleanCertificates(void *tPtr, int64_t now) -{ - // assumes m_certs is locked for writing - - bool deleted = false; - Vector< SharedPtr< const Certificate >> toDelete; - for (;;) { - for (Map< SHA384Hash, p_CertEntry >::iterator c(m_certs.begin()); c != m_certs.end(); ++c) { - // Verify, but the last boolean option tells it to skip signature checks as this would - // already have been done. This will therefore just check the path and validity times - // of the certificate. - const ZT_CertificateError err = m_verifyCertificate(*(c->second.certificate), now, c->second.localTrust, true); - if (err != ZT_CERTIFICATE_ERROR_NONE) - toDelete.push_back(c->second.certificate); - } - - if (toDelete.empty()) - break; - deleted = true; - - SHA384Hash uniqueIdHash; - for (Vector< SharedPtr< const Certificate > >::iterator c(toDelete.begin()); c != toDelete.end(); ++c) { - if ((*c)->subject.uniqueId) { - SHA384(uniqueIdHash.data, (*c)->subject.uniqueId, (*c)->subject.uniqueIdSize); - m_eraseCertificate(tPtr, *c, &uniqueIdHash); - } else { - m_eraseCertificate(tPtr, *c, nullptr); - } - } - toDelete.clear(); - } - - return deleted; -} - -bool Topology::m_verifyCertificateChain(const Certificate *current, const int64_t now) const -{ - // assumes m_certs is at least locked for reading - - Map< Fingerprint, Map< SharedPtr< const Certificate >, unsigned int > >::const_iterator c(m_certsBySubjectIdentity.find(reinterpret_cast(current->issuer)->fingerprint())); - if (c != m_certsBySubjectIdentity.end()) { - for (Map< SharedPtr< const Certificate >, unsigned int >::const_iterator cc(c->second.begin()); cc != c->second.end(); ++cc) { - if ( - (cc->first->maxPathLength > current->maxPathLength) && - (cc->first->validity[0] <= now) && // not before now - (cc->first->validity[1] >= now) && // not after now - (cc->first->validity[0] <= current->timestamp) && // not before child cert's timestamp - (cc->first->validity[1] >= current->timestamp) // not after child cert's timestamp - ) { - if ((cc->second & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) != 0) - return true; - if (m_verifyCertificateChain(cc->first.ptr(), now)) - return true; - } - } - } - - return false; -} - -ZT_CertificateError Topology::m_verifyCertificate(const Certificate &cert, const int64_t now, unsigned int localTrust, bool skipSignatureCheck) const -{ - // assumes m_certs is at least locked for reading - - // Check certificate time window against current time. - if ((cert.validity[0] > now) || (cert.validity[1] < now)) - return ZT_CERTIFICATE_ERROR_OUT_OF_VALID_TIME_WINDOW; - - // Verify primary and internal signatures and other objects unless the caller - // elected to skip, which is done to re-check certs already in the DB. - if (!skipSignatureCheck) { - const ZT_CertificateError err = cert.verify(); - if (err != ZT_CERTIFICATE_ERROR_NONE) - return err; - } - - // If this is a root CA, we can skip this as we're already there. Otherwise we - // recurse up the tree until we hit a root CA. - if ((localTrust & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) == 0) { - if (!m_verifyCertificateChain(&cert, now)) - return ZT_CERTIFICATE_ERROR_INVALID_CHAIN; - } - - return ZT_CERTIFICATE_ERROR_NONE; -} - void Topology::m_loadCached(void *tPtr, const Address &zta, SharedPtr< Peer > &peer) { // does not require any locks to be held @@ -469,7 +207,7 @@ SharedPtr< Peer > Topology::m_peerFromCached(void *tPtr, const Address &zta) return p; } -SharedPtr< Path > Topology::m_newPath(const int64_t l, const InetAddress &r, const UniqueID &k) +SharedPtr< Path > Topology::m_newPath(const int64_t l, const InetAddress &r, const Path::Key &k) { SharedPtr< Path > p(new Path(l, r)); RWMutex::Lock lck(m_paths_l); @@ -480,53 +218,4 @@ SharedPtr< Path > Topology::m_newPath(const int64_t l, const InetAddress &r, con return p; } -void Topology::m_updateRootPeers(void *tPtr, const int64_t now) -{ - // assumes m_certs_l, m_peers_l, and m_roots_l are locked for write - - Set< Identity > rootIdentities; - for (Map< Fingerprint, Map< SharedPtr< const Certificate >, unsigned int > >::const_iterator c(m_certsBySubjectIdentity.begin()); c != m_certsBySubjectIdentity.end(); ++c) { - for (Map< SharedPtr< const Certificate >, unsigned int >::const_iterator cc(c->second.begin()); cc != c->second.end(); ++cc) { - if ((cc->second & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) != 0) { - for (unsigned int i = 0; i < cc->first->subject.identityCount; ++i) { - if (cc->first->subject.identities[i].identity) - rootIdentities.insert(*reinterpret_cast(cc->first->subject.identities[i].identity)); - } - } - } - } - - m_roots.clear(); - for (Set< Identity >::const_iterator i(rootIdentities.begin()); i != rootIdentities.end(); ++i) { - SharedPtr< Peer > &p = m_peers[i->address()]; - if ((!p) || (p->identity() != *i)) { - p.set(new Peer(RR)); - p->init(*i); - } - m_roots.push_back(p); - } - - m_rankRoots(now); -} - -void Topology::m_writeTrustStore(void *tPtr) -{ - // assumes m_certs is locked - - char tmp[32]; - Dictionary d; - - unsigned long idx = 0; - d.add("c$", (uint64_t)m_certs.size()); - for (Map< SHA384Hash, p_CertEntry >::const_iterator c(m_certs.begin()); c != m_certs.end(); ++c) { - d[Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.s", idx)].assign(c->first.data, c->first.data + ZT_SHA384_DIGEST_SIZE); - d.add(Dictionary::arraySubscript(tmp, sizeof(tmp), "c$.lt", idx), (uint64_t)c->second.localTrust); - ++idx; - } - - Vector< uint8_t > trustStore; - d.encode(trustStore); - RR->node->stateObjectPut(tPtr, ZT_STATE_OBJECT_TRUST_STORE, Utils::ZERO256, 0, trustStore.data(), (unsigned int)trustStore.size()); -} - } // namespace ZeroTier diff --git a/core/Topology.hpp b/core/Topology.hpp index 33c8c8877..2da7f840a 100644 --- a/core/Topology.hpp +++ b/core/Topology.hpp @@ -84,10 +84,10 @@ public: */ ZT_INLINE SharedPtr< Path > path(const int64_t l, const InetAddress &r) { - const UniqueID k(r.key()); + const Path::Key k(r); { RWMutex::RLock lck(m_paths_l); - Map< UniqueID, SharedPtr< Path > >::const_iterator p(m_paths.find(k)); + Map< Path::Key, SharedPtr< Path > >::const_iterator p(m_paths.find(k)); if (likely(p != m_paths.end())) return p->second; } @@ -150,79 +150,21 @@ public: */ void saveAll(void *tPtr); - /** - * Add a certificate to the local certificate store - * - * @param tPtr Thread pointer - * @param cert Certificate to add (a copy will be made if added) - * @param now Current time - * @param localTrust Local trust bit flags - * @param writeToLocalStore If true, write to local object store (via API callbacks) - * @param refreshRootSets If true, refresh root sets in case a root set changed (default: true) - * @param verify If true, verify certificate and certificate chain (default: true) - * @return Error or 0 on success - */ - ZT_CertificateError addCertificate( - void *tPtr, - const Certificate &cert, - int64_t now, - unsigned int localTrust, - bool writeToLocalStore, - bool refreshRootSets = true, - bool verify = true); - - /** - * Delete certificate - * - * @param tPtr Thread pointer - * @param serialNo Serial number to delete - * @return Number of deleted certificates - */ - unsigned int deleteCertificate(void *tPtr,const uint8_t serialNo[ZT_SHA384_DIGEST_SIZE]); - - /** - * Fill vectors with all certificates and their corresponding local trust flags - * - * @param c Certificate vector - * @param t Local trust vector - */ - void allCerts(Vector< SharedPtr > &c,Vector< unsigned int > &t) const noexcept; - private: void m_rankRoots(int64_t now); - void m_eraseCertificate(void *tPtr, const SharedPtr< const Certificate > &cert, const SHA384Hash *uniqueIdHash); - bool m_cleanCertificates(void *tPtr, int64_t now); - bool m_verifyCertificateChain(const Certificate *current, int64_t now) const; - ZT_CertificateError m_verifyCertificate(const Certificate &cert, int64_t now, unsigned int localTrust, bool skipSignatureCheck) const; void m_loadCached(void *tPtr, const Address &zta, SharedPtr< Peer > &peer); SharedPtr< Peer > m_peerFromCached(void *tPtr, const Address &zta); - SharedPtr< Path > m_newPath(int64_t l, const InetAddress &r, const UniqueID &k); - void m_updateRootPeers(void *tPtr, int64_t now); - void m_writeTrustStore(void *tPtr); + SharedPtr< Path > m_newPath(int64_t l, const InetAddress &r, const Path::Key &k); const RuntimeEnvironment *const RR; Vector< SharedPtr< Peer > > m_roots; Map< Address, SharedPtr< Peer > > m_peers; - Map< UniqueID, SharedPtr< Path > > m_paths; - - struct p_CertEntry - { - ZT_INLINE p_CertEntry() : - certificate(), - localTrust(0) - {} - SharedPtr< const Certificate > certificate; - unsigned int localTrust; - }; - Map< SHA384Hash, p_CertEntry > m_certs; - Map< SHA384Hash, p_CertEntry > m_certsBySubjectUniqueID; - Map< Fingerprint, Map< SharedPtr< const Certificate >, unsigned int > > m_certsBySubjectIdentity; + Map< Path::Key, SharedPtr< Path > > m_paths; RWMutex m_peers_l; // m_peers RWMutex m_paths_l; // m_paths Mutex m_roots_l; // m_roots and m_lastRankedRoots - Mutex m_certs_l; // m_certs and friends SharedPtr< Peer > m_bestRoot; Spinlock l_bestRoot; diff --git a/core/TrustStore.cpp b/core/TrustStore.cpp new file mode 100644 index 000000000..ae4f35a43 --- /dev/null +++ b/core/TrustStore.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c)2013-2021 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: 2026-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 "TrustStore.hpp" +#include "Topology.hpp" + +namespace ZeroTier { + +TrustStore::TrustStore() +{} + +TrustStore::~TrustStore() +{} + +SharedPtr< const TrustStore::Entry > TrustStore::get(const SHA384Hash &serial) const +{ + RWMutex::RLock l(m_lock); + Map< SHA384Hash, SharedPtr< Entry > >::const_iterator i(m_bySerial.find(serial)); + return (i == m_bySerial.end()) ? SharedPtr< const TrustStore::Entry >() : i->second.constify(); +} + +Vector< SharedPtr< Peer > > TrustStore::roots(void *const tPtr, const RuntimeEnvironment *RR) +{ + RWMutex::RLock l(m_lock); + + Vector< SharedPtr< Peer > > r; + r.reserve(m_bySerial.size()); + + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { + if ((c->second->localTrust() & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ZEROTIER_ROOT_SET) != 0) { + for (unsigned int j = 0; j < c->second->certificate().subject.identityCount; ++j) { + const Identity *const id = reinterpret_cast(c->second->certificate().subject.identities[j].identity); + if ((id != nullptr) && (*id)) { // sanity check + SharedPtr< Peer > peer(RR->topology->peer(tPtr, id->address(), true)); + if (!peer) { + peer.set(new Peer(RR)); + peer->init(*id); + peer = RR->topology->add(tPtr, peer); + } + + const Locator *const loc = reinterpret_cast(c->second->certificate().subject.identities[j].locator); + if (loc) + peer->setLocator(SharedPtr< const Locator >(new Locator(*loc)), true); + + r.push_back(peer); + } + } + } + } + + return r; +} + +Vector< SharedPtr< const TrustStore::Entry > > TrustStore::all() const +{ + Vector< SharedPtr< const TrustStore::Entry > > r; + RWMutex::RLock l(m_lock); + r.reserve(m_bySerial.size()); + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator i(m_bySerial.begin()); i != m_bySerial.end(); ++i) + r.push_back(i->second.constify()); + return r; +} + +void TrustStore::add(const Certificate &cert, const unsigned int localTrust) +{ + RWMutex::Lock l(m_lock); + m_addQueue.push_front(SharedPtr(new Entry(cert, localTrust))); +} + +// Recursive function to trace a certificate up the chain to a CA, returning true +// if the CA is reached and the path length is less than the maximum. +static bool p_validatePath(const Map< SHA384Hash, Vector< SharedPtr< TrustStore::Entry > > > &bySignedCert, const SharedPtr< TrustStore::Entry > &entry, unsigned int pathLength) +{ + if (((entry->localTrust() & ZT_CERTIFICATE_LOCAL_TRUST_FLAG_ROOT_CA) != 0) && (pathLength <= entry->certificate().maxPathLength)) + return true; + if (pathLength < ZT_CERTIFICATE_MAX_PATH_LENGTH) { + const Map< SHA384Hash, Vector< SharedPtr< TrustStore::Entry > > >::const_iterator signers(bySignedCert.find(SHA384Hash(entry->certificate().serialNo))); + if (signers != bySignedCert.end()) { + for (Vector< SharedPtr< TrustStore::Entry > >::const_iterator signer(signers->second.begin()); signer != signers->second.end(); ++signer) { + if ((*signer != entry) && (p_validatePath(bySignedCert, *signer, pathLength + 1))) + return true; + } + } + } + return false; +} + +void TrustStore::update(const int64_t clock, Vector< std::pair< SharedPtr< Entry >, ZT_CertificateError > > *const purge) +{ + RWMutex::Lock l(m_lock); + + // Re-verify existing and rejected certificates, excluding signatures which + // will have already been checked (and checking these is CPU-intensive). This + // catches certificate expiry and un-expiry if the system's clock has been + // changed. When a formerly rejected cert is revived it ends up getting + // checked twice, but optimizing this out would be about as costly as just + // doing this as verify() without signature check is cheap. + for (Map< SharedPtr< Entry >, ZT_CertificateError >::iterator c(m_rejected.begin()); c != m_rejected.end();) { + const ZT_CertificateError err = c->first->m_certificate.verify(clock, false); + if (err == ZT_CERTIFICATE_ERROR_NONE) { + m_bySerial[SHA384Hash(c->first->m_certificate.serialNo)] = c->first; + m_rejected.erase(c++); + } else { + ++c; + } + } + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { + const ZT_CertificateError err = c->second->m_certificate.verify(clock, false); + if (err == ZT_CERTIFICATE_ERROR_NONE) { + ++c; + } else { + m_rejected[c->second] = err; + m_bySerial.erase(c++); + } + } + + // Add new certificates to m_bySerial, which is the master certificate set. They still + // have yet to have their full certificate chains validated. Full signature checking is + // performed here. + while (!m_addQueue.empty()) { + const ZT_CertificateError err = m_addQueue.front()->m_certificate.verify(clock, true); + if (err == ZT_CERTIFICATE_ERROR_NONE) { + m_bySerial[SHA384Hash(m_addQueue.front()->m_certificate.serialNo)].move(m_addQueue.front()); + } else { + m_rejected[m_addQueue.front()] = err; + } + m_addQueue.pop_front(); + } + + // Verify certificate paths and replace old certificates with newer certificates + // when subject unique ID mapping dictates, repeating the process until a stable + // state is achieved. A loop is needed because deleting old certs when new + // certs (with the same subject unique ID) replace them could in theory alter + // certificate validation path checking outcomes, though in practice it should + // not since mixing certificate roles this way would be strange. + for (;;) { + // Create a reverse lookup mapping from signed certs to signer certs for + // certificate path validation. + Map< SHA384Hash, Vector< SharedPtr< Entry > > > bySignedCert; + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end(); ++c) { + for (unsigned int j = 0; j < c->second->m_certificate.subject.certificateCount; ++j) + bySignedCert[SHA384Hash(c->second->m_certificate.subject.certificates[j])].push_back(c->second); + } + + // Validate certificate paths and reject any certificates that do not trace + // back to a CA. + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { + if (p_validatePath(bySignedCert, c->second, 0)) { + ++c; + } else { + m_rejected[c->second] = ZT_CERTIFICATE_ERROR_INVALID_CHAIN; + m_bySerial.erase(c++); + } + } + + // Populate mapping of subject unique IDs to certificates and reject any + // certificates that have been superseded by newly issued certificates with + // the same subject. + bool exitLoop = true; + m_bySubjectUniqueId.clear(); + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { + const unsigned int uniqueIdSize = c->second->m_certificate.subject.uniqueIdSize; + if ((uniqueIdSize > 0) && (uniqueIdSize <= 1024)) { // 1024 is a sanity check value, actual unique IDs are <100 bytes + SharedPtr< Entry > ¤t = m_bySubjectUniqueId[Vector< uint8_t >(c->second->m_certificate.subject.uniqueId, c->second->m_certificate.subject.uniqueId + uniqueIdSize)]; + if (current) { + if (c->second->m_certificate.subject.timestamp > current->m_certificate.subject.timestamp) { + exitLoop = false; + m_rejected[current] = ZT_CERTIFICATE_ERROR_HAVE_NEWER_CERT; + m_bySerial.erase(SHA384Hash(current->m_certificate.serialNo)); + current = c->second; + } + } else { + current = c->second; + } + } + } + + if (exitLoop) + break; + } + + // Populate a mapping of identities to certificates whose subjects reference them. + m_bySubjectIdentity.clear(); + for (Map< SHA384Hash, SharedPtr< Entry > >::const_iterator c(m_bySerial.begin()); c != m_bySerial.end();) { + for (unsigned int i = 0; i < c->second->m_certificate.subject.identityCount; ++i) + m_bySubjectIdentity[reinterpret_cast(c->second->m_certificate.subject.identities[i].identity)->fingerprint()].push_back(c->second); + } + + // Purge and return purged certificates if this option is selected. + if (purge) { + purge->reserve(m_rejected.size()); + for (Map< SharedPtr< Entry >, ZT_CertificateError >::const_iterator c(m_rejected.begin()); c != m_rejected.end(); ++c) + purge->push_back(std::pair< SharedPtr< Entry >, ZT_CertificateError >(c->first, c->second)); + m_rejected.clear(); + } +} + +Vector< std::pair< SharedPtr, ZT_CertificateError > > TrustStore::rejects() const +{ + Vector< std::pair< SharedPtr, ZT_CertificateError > > r; + RWMutex::RLock l(m_lock); + r.reserve(m_rejected.size()); + for (Map< SharedPtr< Entry >, ZT_CertificateError >::const_iterator c(m_rejected.begin()); c != m_rejected.end(); ++c) + r.push_back(std::pair< SharedPtr< Entry >, ZT_CertificateError >(c->first, c->second)); + return r; +} + +} // namespace ZeroTier diff --git a/core/TrustStore.hpp b/core/TrustStore.hpp new file mode 100644 index 000000000..ce2282220 --- /dev/null +++ b/core/TrustStore.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c)2013-2021 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: 2026-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_TRUSTSTORE_HPP +#define ZT_TRUSTSTORE_HPP + +#include "Constants.hpp" +#include "RuntimeEnvironment.hpp" +#include "Containers.hpp" +#include "Certificate.hpp" +#include "Blob.hpp" +#include "SHA512.hpp" +#include "SharedPtr.hpp" +#include "Identity.hpp" +#include "Fingerprint.hpp" +#include "Mutex.hpp" +#include "Peer.hpp" + +namespace ZeroTier { + +/** + * Certificate store and chain validator + */ +class TrustStore +{ +public: + /** + * An entry in the node certificate trust store + */ + class Entry + { + friend class SharedPtr< TrustStore::Entry >; + friend class SharedPtr< const TrustStore::Entry >; + friend class TrustStore; + + private: + ZT_INLINE Entry(const Certificate &cert, const unsigned int lt) noexcept: + m_certificate(cert), + m_localTrust(lt) + {} + + public: + ZT_INLINE const Certificate &certificate() const noexcept + { return m_certificate; } + + ZT_INLINE unsigned int localTrust() const noexcept + { return m_localTrust.load(std::memory_order_relaxed); } + + private: + Certificate m_certificate; + std::atomic< unsigned int > m_localTrust; + std::atomic< int > __refCount; + }; + + TrustStore(); + + ~TrustStore(); + + /** + * Get certificate by certificate serial number + * + * @param serial SHA384 hash of certificate + * @return Entry or empty/nil if not found + */ + SharedPtr< const Entry > get(const SHA384Hash &serial) const; + + /** + * Get current root peers based on root-enumerating certs in trust store + * + * Root peers are created or obtained via this node's Topology. This should + * never be called while relevant data structures in Topology are locked. + * + * Locators in root peers are also updated if the locator present in the + * certificate is valid and newer. + * + * @param tPtr Caller pointer + * @param RR Runtime environment + * @return All roots (sort order undefined) + */ + Vector< SharedPtr< Peer > > roots(void *tPtr, const RuntimeEnvironment *RR); + + /** + * @return All certificates in asecending sort order by serial + */ + Vector< SharedPtr< const Entry > > all() const; + + /** + * Add a certificate + * + * A copy is made so it's fine if the original is freed after this call. + * + * IMPORTANT: The caller MUST also call update() after calling add() one or + * more times to actually add and revalidate certificates and their signature + * chains. + * + * @param cert Certificate to add + */ + void add(const Certificate &cert, unsigned int localTrust); + + /** + * Validate all certificates and their certificate chains + * + * This also processes any certificates added with add() since the last call to update(). + * + * @param clock Current time in milliseconds since epoch + * @param purge If non-NULL, purge rejected certificates and return them in this vector (vector should be empty) + */ + void update(int64_t clock, Vector< std::pair< SharedPtr, ZT_CertificateError > > *purge); + + /** + * Get a copy of the current rejected certificate set. + * + * @return Rejected certificates + */ + Vector< std::pair< SharedPtr, ZT_CertificateError > > rejects() const; + +private: + Map< SHA384Hash, SharedPtr< Entry > > m_bySerial; + Map< Vector< uint8_t >, SharedPtr< Entry > > m_bySubjectUniqueId; + Map< Fingerprint, Vector< SharedPtr< Entry > > > m_bySubjectIdentity; + ForwardList< SharedPtr< Entry > > m_addQueue; + Map< SharedPtr< Entry >, ZT_CertificateError > m_rejected; + RWMutex m_lock; +}; + +} // namespace ZeroTier + +#endif diff --git a/core/zerotier.h b/core/zerotier.h index 9462513d9..9ecc457fe 100644 --- a/core/zerotier.h +++ b/core/zerotier.h @@ -322,6 +322,11 @@ typedef struct */ #define ZT_CERTIFICATE_MAX_STRING_LENGTH 127 +/** + * Maximum certificate path length to CA (a sanity limit value) + */ +#define ZT_CERTIFICATE_MAX_PATH_LENGTH 256 + /** * Certificate is a root CA (local trust flag) */