diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2621b87..92f9d10 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.0)
project(zt)
find_package(Threads)
-# -----------------------------------------------------------------------------
-# | PLATFORM DETECTION |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | PLATFORM DETECTION |
+# ------------------------------------------------------------------------------
# Apple
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
@@ -42,9 +42,9 @@ endif()
# Linux
# if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-# -----------------------------------------------------------------------------
-# | SOURCE DIRECTORIES |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | SOURCE DIRECTORIES |
+# ------------------------------------------------------------------------------
set(PROJ_DIR ${PROJECT_SOURCE_DIR})
set(LWIP_SRC_DIR "${PROJ_DIR}/ext/lwip/src")
@@ -62,9 +62,9 @@ if(BUILD_WIN)
set(LWIP_PORT_DIR ${PROJ_DIR}/ext/lwip-contrib/ports/win32)
endif()
-# -----------------------------------------------------------------------------
-# | INCLUDE DIRECTORIES |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | INCLUDE DIRECTORIES |
+# ------------------------------------------------------------------------------
# ZeroTier
include_directories(${ZTO_SRC_DIR})
@@ -82,9 +82,9 @@ include_directories(${PROJ_DIR}/ext/concurrentqueue)
include_directories(${LWIP_SRC_DIR}/include)
include_directories(${LWIP_PORT_DIR}/include)
-# -----------------------------------------------------------------------------
-# | TARGET AND VARIANT SELECTION |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | TARGET AND VARIANT SELECTION |
+# ------------------------------------------------------------------------------
# Defaults
set(ALLOW_INSTALL_TARGET TRUE)
@@ -143,7 +143,6 @@ if(BUILD_HOST)
set(ALLOW_INSTALL_TARGET TRUE)
set(BUILD_HOST_SELFTEST FALSE)
set(ZTS_ENABLE_STATS TRUE)
- set(ZTS_ENABLE_CENTRAL_API FALSE)
endif()
# CI
@@ -172,7 +171,6 @@ if(BUILD_MACOS_FRAMEWORK)
set(BUILD_HOST_SELFTEST FALSE)
set(BUILD_EXAMPLES FALSE)
set(ALLOW_INSTALL_TARGET FALSE)
- set(ZTS_ENABLE_CENTRAL_API FALSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOMIT_JSON_SUPPORT=1")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOMIT_JSON_SUPPORT=1")
set(CMAKE_XCODE_ATTRIBUTE_ARCHS "$(ARCHS_STANDARD)")
@@ -187,7 +185,6 @@ if(BUILD_IOS_FRAMEWORK)
set(BUILD_HOST_SELFTEST FALSE)
set(BUILD_EXAMPLES FALSE)
set(ALLOW_INSTALL_TARGET FALSE)
- set(ZTS_ENABLE_CENTRAL_API FALSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOMIT_JSON_SUPPORT=1")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DOMIT_JSON_SUPPORT=1")
set(DEVROOT
@@ -209,13 +206,12 @@ if(BUILD_IOS_FRAMEWORK)
endif()
endif()
-# -----------------------------------------------------------------------------
-# | TESTING (and) FEATURE FLAGS |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | TESTING (and) FEATURE FLAGS |
+# ------------------------------------------------------------------------------
if(BUILD_HOST_SELFTEST)
set(ZTS_ENABLE_STATS TRUE)
- set(ZTS_ENABLE_CENTRAL_API TRUE)
endif()
# Enable specific features (eventually these will be enabled by default)
@@ -223,13 +219,13 @@ if(ZTS_ENABLE_STATS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZTS_ENABLE_STATS=1")
endif()
-if(ZTS_ENABLE_CENTRAL_API)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZTS_ENABLE_CENTRAL_API=1")
+if(ZTS_DISABLE_CENTRAL_API)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZTS_DISABLE_CENTRAL_API=1")
endif()
-# -----------------------------------------------------------------------------
-# | HACKS TO GET THIS TO WORK ON WINDOWS |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | HACKS TO GET THIS TO WORK ON WINDOWS |
+# ------------------------------------------------------------------------------
if(BUILD_WIN)
# Possibly a CMake limitation? -- Can't share target output names
@@ -244,9 +240,9 @@ else()
set(DYNAMIC_LIB_OUTPUT_NAME ${PROJECT_NAME})
endif()
-# -----------------------------------------------------------------------------
-# | BUILD TYPES |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | BUILD TYPES |
+# ------------------------------------------------------------------------------
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "debug")
set(BUILD_DEBUG ON)
@@ -261,57 +257,67 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "release")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-everything -w")
endif()
-# -----------------------------------------------------------------------------
-# | EXAMPLES |
-# -----------------------------------------------------------------------------
+# -------------------------------------------------------------------------------
+# | EXAMPLES |
+# -------------------------------------------------------------------------------
if(BUILD_HOST_EXAMPLES)
- add_executable(nonblockingclient
- ${PROJ_DIR}/examples/cpp/nonblockingclient.cpp)
- target_link_libraries(nonblockingclient ${STATIC_LIB_NAME})
+ add_executable(pingable-node
+ ${PROJ_DIR}/examples/c/pingable-node.c)
+ target_link_libraries(pingable-node ${STATIC_LIB_NAME})
- add_executable(nonblockingserver
- ${PROJ_DIR}/examples/cpp/nonblockingserver.cpp)
- target_link_libraries(nonblockingserver ${STATIC_LIB_NAME})
-
- add_executable(earthtest
- ${PROJ_DIR}/examples/cpp/earthtest.cpp)
- target_link_libraries(earthtest ${STATIC_LIB_NAME})
+ add_executable(statistics
+ ${PROJ_DIR}/examples/c/statistics.c)
+ target_link_libraries(statistics ${STATIC_LIB_NAME})
add_executable(adhoc
- ${PROJ_DIR}/examples/cpp/adhoc.cpp)
+ ${PROJ_DIR}/examples/c/adhoc.c)
target_link_libraries(adhoc ${STATIC_LIB_NAME})
- add_executable(comprehensive
- ${PROJ_DIR}/examples/cpp/comprehensive.cpp)
- target_link_libraries(comprehensive ${STATIC_LIB_NAME})
+ add_executable(centralapi
+ ${PROJ_DIR}/examples/c/centralapi.cpp)
+ target_link_libraries(centralapi ${STATIC_LIB_NAME})
+
+ add_executable(callbackapi
+ ${PROJ_DIR}/examples/c/callbackapi.c)
+ target_link_libraries(callbackapi ${STATIC_LIB_NAME})
+
+ add_executable(nostorage
+ ${PROJ_DIR}/examples/c/nostorage.c)
+ target_link_libraries(nostorage ${STATIC_LIB_NAME})
add_executable(client
- ${PROJ_DIR}/examples/cpp/client.cpp)
+ ${PROJ_DIR}/examples/c/client.c)
target_link_libraries(client ${STATIC_LIB_NAME})
add_executable(server
- ${PROJ_DIR}/examples/cpp/server.cpp)
+ ${PROJ_DIR}/examples/c/server.c)
target_link_libraries(server ${STATIC_LIB_NAME})
- add_executable(keymanagement
- ${PROJ_DIR}/examples/cpp/keymanagement.cpp)
- target_link_libraries(keymanagement ${STATIC_LIB_NAME})
+ add_executable(nonblockingclient
+ ${PROJ_DIR}/examples/c/nonblockingclient.c)
+ target_link_libraries(nonblockingclient ${STATIC_LIB_NAME})
- if(ZTS_ENABLE_CENTRAL_API)
- add_executable(centralapi ${PROJ_DIR}/examples/cpp/centralapi.cpp)
- target_link_libraries(centralapi ${STATIC_LIB_NAME})
- endif()
+ add_executable(nonblockingserver
+ ${PROJ_DIR}/examples/c/nonblockingserver.c)
+ target_link_libraries(nonblockingserver ${STATIC_LIB_NAME})
endif()
-# -----------------------------------------------------------------------------
-# | FLAGS |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | FLAGS |
+# ------------------------------------------------------------------------------
+
+set(SILENCE "-Wno-missing-field-initializers \
+ -Wno-macro-redefined \
+ -Wno-tautological-overlap-compare \
+ -Wno-unused-function \
+ -Wno-unused-parameter \
+ -Wno-macro-redefined \
+ -Wno-tautological-constant-out-of-range-compare \
+ -Wno-parentheses-equality")
set(ZT_FLAGS "${ZT_FLAGS} -DZT_USE_MINIUPNPC=1")
-set(ZT_FLAGS "${ZT_FLAGS} -DZT_SOFTWARE_UPDATE_DEFAULT=0")
set(ZT_FLAGS "${ZT_FLAGS} -D_USING_LWIP_DEFINITIONS_=0")
-set(ZT_FLAGS "${ZT_FLAGS} -DZT_SDK=1")
if(BUILD_DEBUG)
set(LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_DBG_TYPES_ON=128")
@@ -389,15 +395,14 @@ if(BUILD_WIN)
message(STATUS "WS2_32=${ws2_32_LIBRARY_PATH}")
message(STATUS "ShLwApi=${shlwapi_LIBRARY_PATH}")
message(STATUS "liphlpapi=${iphlpapi_LIBRARY_PATH}")
- add_definitions(-DZT_SDK=1)
add_definitions(-DADD_EXPORTS=1)
endif()
-# -----------------------------------------------------------------------------
-# | OPTIONAL FEATURES |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | OPTIONAL FEATURES |
+# ------------------------------------------------------------------------------
-if(ZTS_ENABLE_CENTRAL_API)
+if(NOT ZTS_DISABLE_CENTRAL_API)
set(requiredlibs)
find_package(CURL)
if(CURL_FOUND)
@@ -409,9 +414,9 @@ if(ZTS_ENABLE_CENTRAL_API)
endif(CURL_FOUND)
endif()
-# -----------------------------------------------------------------------------
-# | JNI |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | JNI |
+# ------------------------------------------------------------------------------
if(ZTS_ENABLE_JAVA OR BUILD_ANDROID)
message(STATUS "Looking for JNI")
@@ -454,13 +459,12 @@ if(ZTS_ENABLE_JAVA OR BUILD_ANDROID)
endif()
endif() # ZTS_ENABLE_JAVA
-# -----------------------------------------------------------------------------
-# | SOURCES |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | SOURCES |
+# ------------------------------------------------------------------------------
file(GLOB ztcoreSrcGlob ${ZTO_SRC_DIR}/node/*.cpp
- ${ZTO_SRC_DIR}/osdep/OSUtils.cpp ${ZTO_SRC_DIR}/osdep/PortMapper.cpp
- ${ZTO_SRC_DIR}/osdep/ManagedRoute.cpp)
+ ${ZTO_SRC_DIR}/osdep/OSUtils.cpp ${ZTO_SRC_DIR}/osdep/PortMapper.cpp)
file(GLOB libnatpmpSrcGlob ${ZTO_SRC_DIR}/ext/libnatpmp/natpmp.c
${ZTO_SRC_DIR}/ext/libnatpmp/wingettimeofday.c
@@ -504,9 +508,9 @@ file(GLOB frameworkPublicHeaderGlob include/ZeroTierSockets.h)
file(GLOB frameworkHeaderGlob ${frameworkPublicHeaderGlob}
${frameworkPrivateHeaderGlob})
-# -----------------------------------------------------------------------------
-# | OBJECT LIBRARIES (INTERMEDIATE) |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | OBJECT LIBRARIES (INTERMEDIATE) |
+# ------------------------------------------------------------------------------
if(BUILD_STATIC_LIB)
# zto_obj
@@ -596,9 +600,9 @@ set_target_properties(zt_pic PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}"
# PROPERTY STATIC_LIBRARY_FLAGS "-no_warning_for_no_symbols"
#)
-# -----------------------------------------------------------------------------
-# | STATIC LIB |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | STATIC LIB |
+# ------------------------------------------------------------------------------
if(BUILD_STATIC_LIB)
# libztcore.a
@@ -625,7 +629,7 @@ if(BUILD_STATIC_LIB)
target_link_libraries(${STATIC_LIB_NAME} ${ws2_32_LIBRARY_PATH}
${shlwapi_LIBRARY_PATH} ${iphlpapi_LIBRARY_PATH})
endif()
- if(ZTS_ENABLE_CENTRAL_API)
+ if(NOT ZTS_DISABLE_CENTRAL_API)
target_link_libraries(${STATIC_LIB_NAME} ${CURL_LIBRARIES})
endif()
endif() # BUILD_STATIC_LIB
@@ -634,9 +638,9 @@ endif() # BUILD_STATIC_LIB
-# -----------------------------------------------------------------------------
-# | SHARED LIB |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | SHARED LIB |
+# ------------------------------------------------------------------------------
if(BUILD_SHARED_LIB)
# libzt.so/dylib/dll
@@ -661,17 +665,12 @@ if(BUILD_SHARED_LIB)
zto_pic
natpmp_pic
miniupnpc_pic)
-
- if(ZTS_ENABLE_CENTRAL_API)
- target_link_libraries(${DYNAMIC_LIB_NAME} ${CURL_LIBRARIES})
- endif()
-
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
if(BUILD_ANDROID)
target_link_libraries(${DYNAMIC_LIB_NAME} android log)
endif()
- if(ZTS_ENABLE_CENTRAL_API)
+ if(NOT ZTS_DISABLE_CENTRAL_API)
target_link_libraries(${DYNAMIC_LIB_NAME} ${CURL_LIBRARIES})
endif()
endif() # BUILD_SHARED_LIB
@@ -715,21 +714,22 @@ if(IN_XCODE)
XCODE_ATTRIBUTE_CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES YES)
endif()
-# -----------------------------------------------------------------------------
-# | SELFTEST |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | SELFTEST |
+# ------------------------------------------------------------------------------
if(BUILD_HOST_SELFTEST)
- add_executable(selftest-c-api ${PROJ_DIR}/test/selftest-c-api.c)
- target_link_libraries(selftest-c-api ${STATIC_LIB_NAME})
+ add_executable(selftest-c
+ ${PROJ_DIR}/test/selftest.c)
+ target_link_libraries(selftest-c ${STATIC_LIB_NAME})
project(TEST)
enable_testing()
- add_test(NAME selftest-c-api COMMAND selftest-c-api)
+ add_test(NAME selftest-c COMMAND selftest-c)
endif()
-# -----------------------------------------------------------------------------
-# | INSTALL |
-# -----------------------------------------------------------------------------
+# ------------------------------------------------------------------------------
+# | INSTALL |
+# ------------------------------------------------------------------------------
if (ALLOW_INSTALL_TARGET)
set(PUBLIC_ZT_HEADERS ${PROJECT_SOURCE_DIR}/include/ZeroTierSockets.h)
diff --git a/README.md b/README.md
index 70e0cfd..6b7c7ea 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
ZeroTier SDK
-

+

Peer-to-peer and cross-platform encrypted connections built right into your app or service. No drivers, no root, and no host configuration.
diff --git a/examples/c/adhoc.c b/examples/c/adhoc.c
new file mode 100644
index 0000000..6ce0a7e
--- /dev/null
+++ b/examples/c/adhoc.c
@@ -0,0 +1,97 @@
+/**
+ * libzt C API example
+ *
+ * Pingable node joined to controller-less adhoc network with a 6PLANE addressing scheme
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+
+/*
+
+Ad-hoc Network:
+
+ffSSSSEEEE000000
+| | | |
+| | | Reserved for future use, must be 0
+| | End of port range (hex)
+| Start of port range (hex)
+Reserved ZeroTier address prefix indicating a controller-less network.
+
+Ad-hoc networks are public (no access control) networks that have no network controller. Instead
+their configuration and other credentials are generated locally. Ad-hoc networks permit only IPv6
+UDP and TCP unicast traffic (no multicast or broadcast) using 6plane format NDP-emulated IPv6
+addresses. In addition an ad-hoc network ID encodes an IP port range. UDP packets and TCP SYN
+(connection open) packets are only allowed to destination ports within the encoded range.
+
+For example ff00160016000000 is an ad-hoc network allowing only SSH, while ff0000ffff000000 is an
+ad-hoc network allowing any UDP or TCP port.
+
+Keep in mind that these networks are public and anyone in the entire world can join them. Care must
+be taken to avoid exposing vulnerable services or sharing unwanted files or other resources.
+
+*/
+int main(int argc, char** argv)
+{
+ if (argc != 3) {
+ printf("\nUsage:\n");
+ printf("adhoc \n");
+ exit(0);
+ }
+ int err = ZTS_ERR_OK;
+
+ uint16_t adhocStartPort = atoi(argv[1]); // Start of port range your application will use
+ uint16_t adhocEndPort = atoi(argv[2]); // End of port range your application will use
+ uint64_t net_id = zts_net_compute_adhoc_id(adhocStartPort, adhocEndPort);
+
+ // Start node and get identity
+
+ printf("Starting node...\n");
+ zts_node_start();
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+ uint64_t node_id = zts_node_get_id();
+ printf("My public identity (node ID) is %llx\n", node_id);
+ char keypair[ZTS_ID_STR_BUF_LEN] = { 0 };
+ uint16_t len = ZTS_ID_STR_BUF_LEN;
+ if (zts_node_get_id_pair(keypair, &len) != ZTS_ERR_OK) {
+ printf("Error getting identity keypair. Exiting.\n");
+ }
+ printf("Identity [public/secret pair] = %s\n", keypair);
+
+ // Join the adhoc network
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ // Get address
+
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ if ((err = zts_addr_compute_rfc4193_str(net_id, node_id, ipstr, ZTS_IP_MAX_STR_LEN))
+ != ZTS_ERR_OK) {
+ printf("Unable to compute address (error = %d). Exiting.\n", err);
+ exit(1);
+ }
+ printf("Join %llx from another machine and ping6 me at %s\n", net_id, ipstr);
+
+ // Do network stuff!
+ // zts_socket, zts_connect, etc
+
+ while (1) {
+ zts_util_delay(500); // Idle indefinitely
+ }
+
+ printf("Stopping node\n");
+ return zts_node_stop();
+}
diff --git a/examples/c/callbackapi.c b/examples/c/callbackapi.c
new file mode 100644
index 0000000..794112c
--- /dev/null
+++ b/examples/c/callbackapi.c
@@ -0,0 +1,113 @@
+/**
+ * libzt C API example
+ *
+ * Pingable node that demonstrates basic usage of the callback API. To see
+ * more exhaustive examples look at test/selftest.c
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+
+void on_zts_event(void* msgPtr)
+{
+ zts_event_msg_t* msg = (zts_event_msg_t*)msgPtr;
+ // Node events
+ if (msg->event_code == ZTS_EVENT_NODE_ONLINE) {
+ printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->node_id);
+ }
+ if (msg->event_code == ZTS_EVENT_NODE_OFFLINE) {
+ printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, "
+ "firewall, etc. What ports are you blocking?\n");
+ }
+ // Virtual network events
+ if (msg->event_code == ZTS_EVENT_NETWORK_NOT_FOUND) {
+ printf(
+ "ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n",
+ msg->network->net_id);
+ }
+ if (msg->event_code == ZTS_EVENT_NETWORK_ACCESS_DENIED) {
+ printf(
+ "ZTS_EVENT_NETWORK_ACCESS_DENIED --- Access to virtual network %llx has been denied. "
+ "Did you authorize the node yet?\n",
+ msg->network->net_id);
+ }
+ if (msg->event_code == ZTS_EVENT_NETWORK_READY_IP6) {
+ printf(
+ "ZTS_EVENT_NETWORK_READY_IP6 --- Network config received. IPv6 traffic can now be sent "
+ "over network %llx\n",
+ msg->network->net_id);
+ }
+ // Address events
+ if (msg->event_code == ZTS_EVENT_ADDR_ADDED_IP6) {
+ char ipstr[ZTS_INET6_ADDRSTRLEN] = { 0 };
+ struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr);
+ zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN);
+ printf(
+ "ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n",
+ msg->addr->net_id,
+ ipstr);
+ }
+
+ // To see more exhaustive examples look at test/selftest.c
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("\nUsage:\n");
+ printf("pingable-node \n");
+ exit(0);
+ }
+ uint64_t net_id = strtoull(argv[1], NULL, 16);
+
+ zts_init_set_event_handler(&on_zts_event);
+
+ printf("Starting node...\n");
+ zts_node_start();
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ printf("My public identity (node ID) is %llx\n", zts_node_get_id());
+ char keypair[ZTS_ID_STR_BUF_LEN] = { 0 };
+ uint16_t len = ZTS_ID_STR_BUF_LEN;
+ if (zts_node_get_id_pair(keypair, &len) != ZTS_ERR_OK) {
+ printf("Error getting identity keypair. Exiting.\n");
+ }
+ printf("Identity [public/secret pair] = %s\n", keypair);
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ printf("Waiting for address assignment from network\n");
+ int err = 0;
+ while (! (err = zts_addr_is_assigned(net_id, ZTS_AF_INET))) {
+ zts_util_delay(500);
+ }
+
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, ZTS_AF_INET, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("Join %llx from another machine and ping me at %s\n", net_id, ipstr);
+
+ // Do network stuff!
+ // zts_socket, zts_connect, etc
+
+ while (1) {
+ zts_util_delay(500); // Idle indefinitely
+ }
+
+ printf("Stopping node\n");
+ return zts_node_stop();
+}
diff --git a/examples/c/centralapi.cpp b/examples/c/centralapi.cpp
new file mode 100644
index 0000000..003d7cc
--- /dev/null
+++ b/examples/c/centralapi.cpp
@@ -0,0 +1,109 @@
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+// For optional JSON parsing
+#include "../ext/ZeroTierOne/ext/json/json.hpp"
+
+void process_response(char* response, int http_resp_code)
+{
+ if (http_resp_code == 0) {
+ // Request failed at library level, do nothing. There would be no HTTP code at this point.
+ return;
+ }
+ printf("Raw response string (%d) = %s\n", http_resp_code, response);
+ // Parse into navigable JSON object
+ if (http_resp_code < 200 || http_resp_code >= 300) {
+ return;
+ }
+ nlohmann::json res = nlohmann::json::parse(response);
+ if (! res.is_object()) {
+ fprintf(stderr, "Unable to parse (root element is not a JSON object)");
+ }
+ // Pretty print JSON blob
+ std::cout << std::setw(4) << res << std::endl;
+}
+
+int main(int argc, char** argv)
+{
+ if (argc != 3) {
+ printf("\nlibzt example central API client\n");
+ printf("centralapi \n");
+ exit(0);
+ }
+ char* central_url = argv[1]; // API endpoint
+ char* api_token = argv[2]; // User token (generate at my.zerotier.com)
+
+ /**
+ * This example demonstrates how to use the ZeroTier Central API to:
+ *
+ * - Get the status of our hosted service (or your own)
+ * - Create a network
+ * - Get the full configuration of a network
+ * - Authorize/Deauthorize nodes on a network
+ *
+ * This example does not start a node (though you can if you wish.) This portion of the
+ * libzt API is merely a wrapper around our web API endpoint (https://my.zerotier.com/help/api).
+ * The HTTP requests are done via libcurl. This API is thread-safe but not multi-threaded.
+ *
+ * Error Codes:
+ * -2 : [ZTS_ERR_SERVICE] The API may not have been initialized properly
+ * -3 : [ZTS_ERR_ARG] Invalid argument
+ * [100-500] : Standard HTTP error codes
+ *
+ * Usage example: centralapi https://my.zerotier.com e7no7nVRFItge7no7cVR5Ibge7no8nV1
+ *
+ */
+
+ int err = ZTS_ERR_OK;
+ // Buffer to store server response as JSON string blobs
+ char rbuf[ZTS_CENTRAL_RESP_BUF_DEFAULT_SZ] = { 0 };
+
+ // Provide URL to Central API server and user API token generated at https://my.zerotier.com
+ printf("Initializing Central API client...\n");
+ if ((err = zts_central_init(central_url, api_token, rbuf, ZTS_CENTRAL_RESP_BUF_DEFAULT_SZ))
+ != ZTS_ERR_OK) {
+ fprintf(stderr, "Error while initializing client's Central API parameters\n");
+ return 0;
+ }
+
+ zts_central_set_verbose(false); // (optional) Turn on reporting from libcurl
+ zts_central_set_access_mode(ZTS_CENTRAL_READ /*| ZTS_CENTRAL_WRITE*/);
+
+ int http_res_code = 0;
+
+ // Get hosted service status
+ printf("Requesting Central API server status (/api/status):\n");
+ if ((err = zts_central_status_get(&http_res_code)) != ZTS_ERR_OK) {
+ fprintf(stderr, "Error (%d) making the request.\n", err);
+ }
+ else {
+ process_response(rbuf, http_res_code);
+ }
+ // Get network config
+ int64_t nwid = 0x1234567890abcdef;
+ printf("Requesting network config: /api/network/%llx\n", nwid);
+ if ((err = zts_central_net_get(&http_res_code, nwid)) != ZTS_ERR_OK) {
+ fprintf(stderr, "Error (%d) making the request.\n", err);
+ }
+ else {
+ process_response(rbuf, http_res_code);
+ }
+ // Authorize a node on a network
+ int64_t nodeid = 0x9934343434;
+ printf("Authorizing: /api/network/%llx/member/%llx\n", nwid, nodeid);
+ if ((err = zts_central_node_auth(&http_res_code, nwid, nodeid, ZTS_CENTRAL_NODE_AUTH_TRUE))
+ != ZTS_ERR_OK) {
+ fprintf(stderr, "Error (%d) making the request.\n", err);
+ }
+ else {
+ process_response(rbuf, http_res_code);
+ }
+
+ return 0;
+}
diff --git a/examples/c/client.c b/examples/c/client.c
new file mode 100644
index 0000000..e64bbec
--- /dev/null
+++ b/examples/c/client.c
@@ -0,0 +1,105 @@
+/**
+ * libzt C API example
+ *
+ * Simple socket-based client application
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 5) {
+ printf("\nlibzt example client\n");
+ printf("client \n");
+ exit(0);
+ }
+ char* storage_path = argv[1];
+ uint64_t net_id = strtoull(argv[2], NULL, 16);
+ char* remote_addr = argv[3];
+ int remote_port = atoi(argv[4]);
+ int err = ZTS_ERR_OK;
+
+ // Initialize node
+
+ if ((err = zts_init_from_storage(storage_path)) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ // Start node
+
+ if ((err = zts_node_start()) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+ printf("Public identity (node ID) is %llx\n", zts_node_get_id());
+
+ // Join network
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+ printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n");
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ // Get assigned address (of the family type we care about)
+
+ int family = zts_util_get_ip_family(remote_addr);
+
+ printf("Waiting for address assignment from network\n");
+ while (! (err = zts_addr_is_assigned(net_id, family))) {
+ zts_util_delay(50);
+ }
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, family, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("IP address on network %llx is %s\n", net_id, ipstr);
+
+ // BEGIN Socket Stuff
+
+ char* msgStr = (char*)"Welcome to the machine";
+ int bytes = 0, fd;
+ char recvBuf[128] = { 0 };
+ memset(recvBuf, 0, sizeof(recvBuf));
+
+ // Connect to remote host
+
+ // Can also use traditional: zts_socket(), zts_connect(), etc
+
+ printf("Attempting to connect...\n");
+ while ((fd = zts_simple_tcp_client(remote_addr, remote_port)) < 0) {
+ printf("Re-attempting to connect...\n");
+ }
+
+ // Data I/O
+
+ printf("Sending message string to server...\n");
+ if ((bytes = zts_write(fd, msgStr, strlen(msgStr))) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno);
+ exit(1);
+ }
+ printf("Sent %d bytes: %s\n", bytes, msgStr);
+ printf("Reading message string from server...\n");
+ if ((bytes = zts_read(fd, recvBuf, sizeof(recvBuf))) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno);
+ exit(1);
+ }
+ printf("Read %d bytes: %s\n", bytes, recvBuf);
+
+ // Close
+
+ zts_close(fd);
+ return zts_node_stop();
+}
diff --git a/examples/c/nonblockingclient.c b/examples/c/nonblockingclient.c
new file mode 100644
index 0000000..b0943d7
--- /dev/null
+++ b/examples/c/nonblockingclient.c
@@ -0,0 +1,96 @@
+/**
+ * libzt C API example
+ *
+ * Simple socket-based client application
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 5) {
+ printf("\nlibzt example client\n");
+ printf("client \n");
+ exit(0);
+ }
+ char* storage_path = argv[1];
+ uint64_t net_id = strtoull(argv[2], NULL, 16);
+ char* remote_addr = argv[3];
+ int remote_port = atoi(argv[4]);
+ int err = ZTS_ERR_OK;
+
+ // Initialize node
+
+ if ((err = zts_init_from_storage(storage_path)) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ // Start node
+
+ if ((err = zts_node_start()) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ printf("Public identity (node ID) is %llx\n", zts_node_get_id());
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n");
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ // Sockets
+
+ char* msgStr = (char*)"Welcome to the machine";
+ int bytes = 0, fd;
+ char recvBuf[128] = { 0 };
+ memset(recvBuf, 0, sizeof(recvBuf));
+
+ // Create socket
+
+ if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) {
+ printf("Error (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno);
+ exit(1);
+ }
+
+ // Connect
+
+ // Can also use:
+ // zts_connect(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen);
+ while (zts_simple_connect(fd, remote_addr, remote_port, 0) != ZTS_ERR_OK) {
+ printf("Attempting to connect...\n");
+ }
+
+ // Data I/O
+
+ // Wait random intervals to send a message to the server
+ // The non-blocking aspect of this example is server-side
+ while (1) {
+ if ((bytes = zts_send(fd, msgStr, strlen(msgStr), 0)) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno);
+ exit(1);
+ }
+ printf("zts_send()=%d\n", bytes);
+ zts_util_delay((rand() % 100) * 50);
+ }
+
+ zts_close(fd);
+ return zts_node_stop();
+}
diff --git a/examples/c/nonblockingserver.c b/examples/c/nonblockingserver.c
new file mode 100644
index 0000000..2bdfeef
--- /dev/null
+++ b/examples/c/nonblockingserver.c
@@ -0,0 +1,169 @@
+/**
+ * libzt C API example
+ *
+ * Simple socket-based server application
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 5) {
+ printf("\nlibzt example server\n");
+ printf("server \n");
+ exit(0);
+ }
+ char* storage_path = argv[1];
+ uint64_t net_id = strtoull(argv[2], NULL, 16);
+ char* local_addr = argv[3];
+ int local_port = atoi(argv[4]);
+ int fd, accfd;
+ int err = ZTS_ERR_OK;
+
+ // Initialize node
+
+ if ((err = zts_init_from_storage(storage_path)) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ // Start node
+
+ if ((err = zts_node_start()) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ printf("Public identity (node ID) is %llx\n", zts_node_get_id());
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n");
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ printf("Waiting for address assignment from network\n");
+ while (! (err = zts_addr_is_assigned(net_id, ZTS_AF_INET))) {
+ zts_util_delay(500);
+ }
+
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, ZTS_AF_INET, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("Assigned IP address: %s\n", ipstr);
+
+ // Sockets
+
+ printf("Creating socket...\n");
+ if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno);
+ exit(1);
+ }
+ printf("Binding...\n");
+ // Can also use:
+ // zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen)
+ if ((err = zts_simple_bind(fd, local_addr, local_port) < 0)) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno);
+ exit(1);
+ }
+ printf("Listening...\n");
+ int backlog = 100;
+ if ((err = zts_listen(fd, backlog)) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno);
+ exit(1);
+ }
+
+ int bytes = 0;
+ char recvBuf[128] = { 0 };
+
+ while (1) {
+ // Accept
+ // Can also use
+ // zts_accept(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
+
+ char ipstr[ZTS_INET6_ADDRSTRLEN] = { 0 };
+ int port = 0;
+ printf("Accepting on listening socket...\n");
+ if ((accfd = zts_simple_accept(fd, ipstr, ZTS_INET6_ADDRSTRLEN, &port)) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno);
+ }
+ printf("Accepted connection from %s:%d\n", ipstr, port);
+ }
+
+ // Data I/O
+
+ // Technique 1: ZTS_O_NONBLOCK
+ if (0) {
+ zts_fcntl(fd, ZTS_F_SETFL, ZTS_O_NONBLOCK);
+ zts_fcntl(accfd, ZTS_F_SETFL, ZTS_O_NONBLOCK);
+ while (1) {
+ bytes = zts_recv(accfd, recvBuf, sizeof(recvBuf), 0);
+ printf("zts_recv(%d, ...)=%d\n", accfd, bytes);
+ zts_util_delay(100);
+ }
+ }
+
+ // Technique 2: zts_select
+ if (0) {
+ struct zts_timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 50000;
+ int result = 0;
+ zts_fd_set active_fd_set, read_fd_set;
+ ZTS_FD_ZERO(&active_fd_set);
+ ZTS_FD_SET(accfd, &active_fd_set);
+ while (1) {
+ read_fd_set = active_fd_set;
+ if ((result = zts_select(ZTS_FD_SETSIZE, &read_fd_set, NULL, NULL, &tv) < 0)) {
+ // perror ("select");
+ exit(1);
+ }
+ for (int i = 0; i < ZTS_FD_SETSIZE; i++) {
+ if (ZTS_FD_ISSET(i, &read_fd_set)) {
+ bytes = zts_recv(accfd, recvBuf, sizeof(recvBuf), 0);
+ printf("zts_recv(%d, ...)=%d\n", i, bytes);
+ }
+ // ZTS_FD_CLR(i, &active_fd_set);
+ }
+ }
+ }
+
+ // Technique 3: zts_poll
+ if (1) {
+ int numfds = 0;
+ struct zts_pollfd poll_set[16];
+ memset(poll_set, '\0', sizeof(poll_set));
+ poll_set[0].fd = accfd;
+ poll_set[0].events = ZTS_POLLIN;
+ numfds++;
+ int result = 0;
+ int timeout_ms = 50;
+ while (1) {
+ result = zts_poll(poll_set, numfds, timeout_ms);
+ printf("zts_poll()=%d\n", result);
+ for (int i = 0; i < numfds; i++) {
+ if (poll_set[i].revents & ZTS_POLLIN) {
+ bytes = zts_recv(poll_set[i].fd, recvBuf, sizeof(recvBuf), 0);
+ printf("zts_recv(%d, ...)=%d\n", i, bytes);
+ }
+ }
+ }
+ }
+
+ err = zts_close(fd);
+ return zts_node_stop();
+}
diff --git a/examples/c/nostorage.c b/examples/c/nostorage.c
new file mode 100644
index 0000000..b530f81
--- /dev/null
+++ b/examples/c/nostorage.c
@@ -0,0 +1,109 @@
+/**
+ * libzt C API example
+ *
+ * Demonstrates how to manage ZeroTier node identities (public/secret keypairs) without
+ * local storage (e.g. zts_init_from_storage().)
+ *
+ * WARNING: This prints secret keys to your terminal.
+ *
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+
+char cache_data[ZTS_STORE_DATA_LEN];
+
+void on_zts_event(void* msgPtr)
+{
+ zts_event_msg_t* msg = (zts_event_msg_t*)msgPtr;
+ int len = msg->len; // Length of message (or structure)
+
+ if (msg->event_code == ZTS_EVENT_NODE_ONLINE) {
+ printf("ZTS_EVENT_NODE_ONLINE\n");
+ }
+
+ // Copy data to a buffer that you have allocated or write it to storage.
+ // The data pointed to by msg->cache will be invalid after this function
+ // returns.
+
+ memset(cache_data, 0, ZTS_STORE_DATA_LEN);
+
+ if (msg->event_code == ZTS_EVENT_STORE_IDENTITY_PUBLIC) {
+ printf("ZTS_EVENT_STORE_IDENTITY_PUBLIC (len=%d)\n", msg->len);
+ printf("identity.public = [ %.*s ]\n", len, msg->cache);
+ memcpy(cache_data, msg->cache, len);
+ }
+ if (msg->event_code == ZTS_EVENT_STORE_IDENTITY_SECRET) {
+ printf("ZTS_EVENT_STORE_IDENTITY_SECRET (len=%d)\n", msg->len);
+ printf("identity.secret = [ %.*s ]\n", len, msg->cache);
+ memcpy(cache_data, msg->cache, len);
+ // Same data can be retrieved via: zts_node_get_id_pair()
+ }
+ if (msg->event_code == ZTS_EVENT_STORE_PLANET) {
+ printf("ZTS_EVENT_STORE_PLANET (len=%d)\n", msg->len);
+ // Binary data
+ memcpy(cache_data, msg->cache, len);
+ }
+ if (msg->event_code == ZTS_EVENT_STORE_PEER) {
+ printf("ZTS_EVENT_STORE_PEER (len=%d)\n", msg->len);
+ // Binary data
+ memcpy(cache_data, msg->cache, len);
+ }
+ if (msg->event_code == ZTS_EVENT_STORE_NETWORK) {
+ printf("ZTS_EVENT_STORE_NETWORK (len=%d)\n", msg->len);
+ // Binary data
+ memcpy(cache_data, msg->cache, len);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ int err = ZTS_ERR_OK;
+
+ // Initialize node
+
+ zts_init_set_event_handler(&on_zts_event);
+
+ // Start node
+
+ printf("Starting node...\n");
+ int generate_new_id = 1;
+ if (generate_new_id) {
+ // OPTION A
+ // Generate new automatically ID if no prior init called
+ zts_node_start();
+ }
+ else {
+ // OPTION B
+ // Copy your key here
+ char identity[ZTS_ID_STR_BUF_LEN] = { 0 };
+ int len = ZTS_ID_STR_BUF_LEN;
+
+ // Generate key (optional):
+ // int key_len;
+ // zts_id_new(identity, &key_len);
+
+ // Load pre-existing identity from buffer
+ zts_init_from_memory(identity, len);
+ zts_node_start();
+ }
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ // Do network stuff!
+ // zts_socket, zts_connect, etc
+
+ printf("Node %llx is now online. Idling.\n", zts_node_get_id());
+ while (1) {
+ zts_util_delay(500); // Idle indefinitely
+ }
+
+ printf("Stopping node\n");
+ return zts_node_stop();
+}
diff --git a/examples/c/pingable-node.c b/examples/c/pingable-node.c
new file mode 100644
index 0000000..3c098d3
--- /dev/null
+++ b/examples/c/pingable-node.c
@@ -0,0 +1,67 @@
+/**
+ * libzt C API example
+ *
+ * Pingable node
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("\nUsage:\n");
+ printf("pingable-node \n");
+ exit(0);
+ }
+ uint64_t net_id = strtoull(argv[1], NULL, 16);
+
+ printf("Starting node...\n");
+ zts_node_start();
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ printf("My public identity (node ID) is %llx\n", zts_node_get_id());
+ char keypair[ZTS_ID_STR_BUF_LEN] = { 0 };
+ uint16_t len = ZTS_ID_STR_BUF_LEN;
+ if (zts_node_get_id_pair(keypair, &len) != ZTS_ERR_OK) {
+ printf("Error getting identity keypair. Exiting.\n");
+ }
+ printf("Identity [public/secret pair] = %s\n", keypair);
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ printf("Waiting for address assignment from network\n");
+ int err = 0;
+ while (! (err = zts_addr_is_assigned(net_id, ZTS_AF_INET))) {
+ zts_util_delay(500);
+ }
+
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, ZTS_AF_INET, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("Join %llx from another machine and ping me at %s\n", net_id, ipstr);
+
+ // Do network stuff!
+ // zts_socket, zts_connect, etc
+
+ while (1) {
+ zts_util_delay(500); // Idle indefinitely
+ }
+
+ printf("Stopping node\n");
+ return zts_node_stop();
+}
diff --git a/examples/c/server.c b/examples/c/server.c
new file mode 100644
index 0000000..206ddad
--- /dev/null
+++ b/examples/c/server.c
@@ -0,0 +1,111 @@
+/**
+ * libzt C API example
+ *
+ * Simple socket-based server application
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 5) {
+ printf("\nlibzt example server\n");
+ printf("server \n");
+ exit(0);
+ }
+ char* storage_path = argv[1];
+ uint64_t net_id = strtoull(argv[2], NULL, 16);
+ char* local_addr = argv[3];
+ int local_port = atoi(argv[4]);
+ int fd, accfd;
+ int err = ZTS_ERR_OK;
+
+ // Initialize node
+
+ if ((err = zts_init_from_storage(storage_path)) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+
+ // Start node
+
+ if ((err = zts_node_start()) != ZTS_ERR_OK) {
+ printf("Unable to start service, error = %d. Exiting.\n", err);
+ exit(1);
+ }
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+ printf("Public identity (node ID) is %llx\n", zts_node_get_id());
+
+ // Join network
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+ printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n");
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ // Get assigned address (of the family type we care about)
+
+ int family = zts_util_get_ip_family(local_addr);
+
+ printf("Waiting for address assignment from network\n");
+ while (! (err = zts_addr_is_assigned(net_id, family))) {
+ zts_util_delay(50);
+ }
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, family, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("IP address on network %llx is %s\n", net_id, ipstr);
+
+ // BEGIN Socket Stuff
+
+ // Accept incoming connection
+
+ // Can also use traditional: zts_socket(), zts_bind(), zts_listen(), zts_accept(), etc.
+
+ char remote_addr[ZTS_INET6_ADDRSTRLEN] = { 0 };
+ int remote_port = 0;
+ int len = ZTS_INET6_ADDRSTRLEN;
+ if ((accfd = zts_simple_tcp_server(local_addr, local_port, remote_addr, len, &remote_port))
+ < 0) {
+ printf("Error (fd=%d, zts_errno=%d). Exiting.\n", accfd, zts_errno);
+ exit(1);
+ }
+ printf("Accepted connection from %s:%d\n", remote_addr, remote_port);
+
+ // Data I/O
+
+ int bytes = 0;
+ char recvBuf[128] = { 0 };
+
+ printf("Reading message string from client...\n");
+ if ((bytes = zts_read(accfd, recvBuf, sizeof(recvBuf))) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno);
+ exit(1);
+ }
+ printf("Read %d bytes: %s\n", bytes, recvBuf);
+ printf("Sending message string to client...\n");
+ if ((bytes = zts_write(accfd, recvBuf, bytes)) < 0) {
+ printf("Error (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno);
+ exit(1);
+ }
+ printf("Sent %d bytes: %s\n", bytes, recvBuf);
+
+ // Close
+
+ printf("Closing connection socket\n");
+ err = zts_close(accfd);
+ err = zts_close(fd);
+ return zts_node_stop();
+}
diff --git a/examples/c/statistics.c b/examples/c/statistics.c
new file mode 100644
index 0000000..05e45e4
--- /dev/null
+++ b/examples/c/statistics.c
@@ -0,0 +1,130 @@
+/**
+ * libzt C API example
+ *
+ * Pingable node that also displays protocol statistics that are
+ * useful for debugging.
+ */
+
+#include "ZeroTierSockets.h"
+
+#include
+#include
+
+int main(int argc, char** argv)
+{
+ if (argc != 2) {
+ printf("\nUsage:\n");
+ printf("pingable-node \n");
+ exit(0);
+ }
+ uint64_t net_id = strtoull(argv[1], NULL, 16);
+
+ printf("Starting node...\n");
+ zts_node_start();
+
+ printf("Waiting for node to come online\n");
+ while (! zts_node_is_online()) {
+ zts_util_delay(50);
+ }
+
+ printf("My public identity (node ID) is %llx\n", zts_node_get_id());
+ char keypair[ZTS_ID_STR_BUF_LEN] = { 0 };
+ uint16_t len = ZTS_ID_STR_BUF_LEN;
+ if (zts_node_get_id_pair(keypair, &len) != ZTS_ERR_OK) {
+ printf("Error getting identity keypair. Exiting.\n");
+ }
+ printf("Identity [public/secret pair] = %s\n", keypair);
+
+ printf("Joining network %llx\n", net_id);
+ if (zts_net_join(net_id) != ZTS_ERR_OK) {
+ printf("Unable to join network. Exiting.\n");
+ exit(1);
+ }
+
+ printf("Waiting for join to complete\n");
+ while (zts_net_count() < 1) {
+ zts_util_delay(50);
+ }
+
+ printf("Waiting for address assignment from network\n");
+ int err = 0;
+ while (! (err = zts_addr_is_assigned(net_id, ZTS_AF_INET))) {
+ zts_util_delay(500);
+ }
+
+ char ipstr[ZTS_IP_MAX_STR_LEN] = { 0 };
+ zts_addr_get_str(net_id, ZTS_AF_INET, ipstr, ZTS_IP_MAX_STR_LEN);
+ printf("Join %llx from another machine and ping me at %s\n", net_id, ipstr);
+
+ // Do network stuff!
+ // zts_socket, zts_connect, etc
+
+ // Show protocol statistics
+
+ zts_stats_counter_t s = { 0 };
+
+ while (1) {
+ zts_util_delay(1000);
+ if ((err = zts_stats_get_all(&s)) == ZTS_ERR_NO_RESULT) {
+ printf("no results\n");
+ continue;
+ }
+ printf("\n\n");
+
+ printf(
+ " link_tx=%9d, link_rx=%9d, link_drop=%9d, link_err=%9d\n",
+ s.link_tx,
+ s.link_rx,
+ s.link_drop,
+ s.link_err);
+ printf(
+ "etharp_tx=%9d, etharp_rx=%9d, etharp_drop=%9d, etharp_err=%9d\n",
+ s.etharp_tx,
+ s.etharp_rx,
+ s.etharp_drop,
+ s.etharp_err);
+ printf(
+ " ip4_tx=%9d, ip4_rx=%9d, ip4_drop=%9d, ip4_err=%9d\n",
+ s.ip4_tx,
+ s.ip4_rx,
+ s.ip4_drop,
+ s.ip4_err);
+ printf(
+ " ip6_tx=%9d, ip6_rx=%9d, ip6_drop=%9d, ip6_err=%9d\n",
+ s.ip6_tx,
+ s.ip6_rx,
+ s.ip6_drop,
+ s.ip6_err);
+ printf(
+ " icmp4_tx=%9d, icmp4_rx=%9d, icmp4_drop=%9d, icmp4_err=%9d\n",
+ s.icmp4_tx,
+ s.icmp4_rx,
+ s.icmp4_drop,
+ s.icmp4_err);
+ printf(
+ " icmp6_tx=%9d, icmp6_rx=%9d, icmp6_drop=%9d, icmp6_err=%9d\n",
+ s.icmp6_tx,
+ s.icmp6_rx,
+ s.icmp6_drop,
+ s.icmp6_err);
+ printf(
+ " udp_tx=%9d, udp_rx=%9d, udp_drop=%9d, udp_err=%9d\n",
+ s.udp_tx,
+ s.udp_rx,
+ s.udp_drop,
+ s.udp_err);
+ printf(
+ " tcp_tx=%9d, tcp_rx=%9d, tcp_drop=%9d, tcp_err=%9d\n",
+ s.tcp_tx,
+ s.tcp_rx,
+ s.tcp_drop,
+ s.tcp_err);
+ printf(
+ " nd6_tx=%9d, nd6_rx=%9d, nd6_drop=%9d, nd6_err=%9d\n",
+ s.nd6_tx,
+ s.nd6_rx,
+ s.nd6_drop,
+ s.nd6_err);
+ }
+ return zts_node_stop();
+}
diff --git a/include/README.md b/include/README.md
deleted file mode 100644
index fe36f9b..0000000
--- a/include/README.md
+++ /dev/null
@@ -1,427 +0,0 @@
-C API Documentation
-=====
-This is the externally facing plain C API. All language bindings interface with this. It provides a uniform socket interface across all supported platforms.
-
----
-
-### Table of Contents ###
-
- - [Starting ZeroTier](#starting-zerotier)
- - [Joining a Network](#joining-a-network)
- - [Connecting to peers](#connecting-to-peers)
- - [Event Handling](#event-handling)
- - [Node Events](#node-events)
- - [Network Events](#network-events)
- - [Peer Events](#peer-events)
- - [Address Events](#address-events)
- - [Error Handling](#error-handling)
- - [Common pitfalls](#common-pitfalls)
- - [API compatibility with host OS](#api-compatibility-with-host-os)
- - [Thread Model](#thread-model)
- - [Debugging](#debugging)
-
-# Starting ZeroTier
-
-The next few sections explain how to use the network control interface portion of the API. These functions are non-blocking and will return an error code specified in the [Error Handling](#error-handling) section and will result in the generation of callback events detailed in the [Event Handling](#event-handling) section. It is your responsibility to handle these events.
-
-To start the service, simply call:
-
-```c
-zts_start(const char *path, void (*callback)(void *), uint16_t port);
-zts_callback_msg*), int port)`
-```
-
-At this stage, if a cryptographic identity for this node does not already exist on your local storage medium, it will generate a new one and store it, the node's address (commonly referred to as `nodeId`) will be derived from this identity and will be presented to you upon receiving the `ZTS_EVENT_NODE_ONLINE` shown below. The first argument `path` is a path where you will direct ZeroTier to store its automatically-generated cryptographic identity files (`identity.public` and `identity.secret`), these files are your keys to communicating on the network. Keep them safe and keep them unique. If any two nodes are online using the same identities you will have a bad time. The second argument `userCallbackFunc` is a function that you specify to handle all generated events for the life of your program, see below:
-
-```c
-#include "ZeroTierSockets.h"
-
-...
-
-bool networkReady = false;
-
-void on_zts_event(struct zts_callback_msg *msg)
-{
- if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
- printf("ZTS_EVENT_NODE_ONLINE, nodeId=%llx\n", msg->node->address);
- networkReady = true;
- }
- ...
-}
-
-int main()
-{
- zts_start("configPath", &on_zts_event, 9994);
- uint64_t nwid = 0x0123456789abcdef;
- while (!networkReady) { sleep(1); }
- zts_join(nwid);
- int fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
- ...
- return 0;
-}
-
-```
-
-For more complete examples see `./examples/`
-
-
-
-After calling `zts_start()` you will receive one or more events specified in the [Node Events](#node-events) section. After receiving `ZTS_EVENT_NODE_ONLINE` you will be allowed to join or leave networks. You must authorize the node ID provided by the this callback event to join your network. This can be done manually or via our [Web API](https://my.zerotier.com/help/api). Note however that if you are using an Ad-hoc network, it has no controller and therefore requires no authorization.
-
-At the end of your program or when no more network activity is anticipated, the user application can shut down the service with `zts_stop()`. However, it is safe to leave the service running in the background indefinitely as it doesn't consume much memory or CPU while at idle. `zts_stop()` is a non-blocking call and will itself issue a series of events indicating that various aspects of the ZeroTier service have successfully shut down.
-
-It is worth noting that while `zts_stop()` will stop the service, the user-space network stack will continue operating in a headless hibernation mode. This is intended behavior due to the fact that the network stack we've chosen doesn't currently support the notion of shutdown since it was initially designed for embedded applications that are simply switched off. If you do need a way to shut everything down and free all resources you can call `zts_free()`, but please note that calling this function will prevent all subsequent `zts_start()` calls from succeeding and will require a full application restart if you want to run the service again. The events `ZTS_EVENT_NODE_ONLINE` and `ZTS_EVENT_NODE_OFFLINE` can be seen periodically throughout the lifetime of your application depending on the reliability of your underlying network link, these events are lagging indicators and are typically only triggered every thirty (30) seconds.
-
-Lastly, the function `zts_restart()` is provided as a way to restart the ZeroTier service along with all of its virtual interfaces. The network stack will remain online and undisturbed during this call. Note that this call will temporarily block until the service has fully shut down, then will return and you may then watch for the appropriate startup callbacks mentioned above.
-
-
-
-# Joining a network
- - #### [Back to Table of Contents](#table-of-contents)
-
-Joining a ZeroTier virtual network is as easy as calling `zts_join(uint64_t networkId)`. Similarly there is a `zts_leave(uint64_t networkId)`. Note that `zts_start()` must be called and a `ZTS_EVENT_NODE_ONLINE` event must have been received before these calls will succeed. After calling `zts_join()` any one of the events detailed in the [Network Events](#network-events) section may be generated.
-
-# Connecting to peers
- - #### [Back to Table of Contents](#table-of-contents)
-
-Creating a standard socket connection generally works the same as it would using an ordinary socket interface, however with ZeroTier there is a subtle difference in how connections are established which may cause confusion. Since ZeroTier employs transport-triggered link provisioning a direct connection between peers will not exist until contact has been attempted by at least one peer. During this time before a direct link is available traffic will be handled via our free relay service. The provisioning of this direct link usually only takes a couple of seconds but it is important to understand that if you attempt something like s `zts_connect(...)` call during this time it may fail due to packet loss. Therefore it is advised to repeatedly call `zts_connect(...)` until it succeeds and to wait to send additional traffic until `ZTS_EVENT_PEER_DIRECT` has been received for the peer you are attempting to communicate with. All of the above is optional, but it will improve your experience.
-
-`tl;dr: Try a few times and wait a few seconds`
-
-As a mitigation for the above behavior, ZeroTier will by default cache details about how to contact a peer in the `peers.d` subdirectory of the config path you passed to `zts_start(...)`. In scenarios where paths do not often change, this can almost completely eliminate the issue and will make connections nearly instantaneous. If however you do not wish to cache these details you can disable it via `zts_set_peer_caching(false)`.
-
-
-
-# Event handling
- - #### [Back to Table of Contents](#table-of-contents)
-
-As mentioned in previous sections, the control API works by use of non-blocking calls and the generation of a few dozen different event types. Depending on the type of event there may be additional contextual information attached to the `zts_callback_msg` object that you can use. This contextual information will be housed in one of the following structures which are defined in `include/ZeroTierSockets.h`:
-
-```c
-struct zts_callback_msg
-{
- int eventCode;
- struct zts_node_details *node;
- struct zts_network_details *network;
- struct zts_netif_details *netif;
- struct zts_virtual_network_route *route;
- struct zts_peer_details *peer;
- struct zts_addr_details *addr;
-};
-```
-
-Here's an example of a callback function:
-
-```c
-void on_zts_event(struct zts_callback_msg *msg)
-{
- if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
- printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address);
- // You can join networks now!
- }
-}
-```
-
-In this callback function you can perform additional non-blocking API calls or other work. While not returning control to the service isn't forbidden (the event messages are generated by a separate thread) it is recommended that you return control as soon as possible as not returning will prevent the user application from receiving additional callback event messages which may be time-sensitive.
-
-
-
-A typical ordering of messages may look like the following:
-
-```c
-...
-ZTS_EVENT_NODE_ONLINE // Your node is ready to be used.
-ZTS_EVENT_ADDR_ADDED_IP4 // Your node received an IP address assignment on a given network.
-ZTS_EVENT_NETWORK_UPDATE // Something about a network changed.
-ZTS_EVENT_NETWORK_READY_IP4 // Your node has joined a network, has an address, and can send/receive traffic.
-ZTS_EVENT_PEER_RELAY // A peer was discovered but no direct path exists (yet.)
-...
-ZTS_EVENT_PEER_DIRECT // One or more direct paths to a peer were discovered.
-```
-
-## Node Events
- - #### [Back to Table of Contents](#table-of-contents)
-
-Accessible via `msg->node` as a `zts_node_details` object, this message type will contain information about the status of your node. *Possible values of `msg->eventCode`:*
-
-```c
-ZTS_EVENT_NODE_OFFLINE // Your node is offline.
-ZTS_EVENT_NODE_ONLINE // Your node is online and ready to communicate!
-ZTS_EVENT_NODE_DOWN // The node is down (for any reason.)
-ZTS_EVENT_NODE_IDENTITY_COLLISION // There is another node with the same identity causing a conflict.
-ZTS_EVENT_NODE_UNRECOVERABLE_ERROR // Something went wrong internally.
-ZTS_EVENT_NODE_NORMAL_TERMINATION // Your node has terminated.
-```
-
-*Example contents of `msg->node`:*
-
-```
-id : f746d550dd
-version : 1.4.6
-primaryPort : 9995
-secondaryPort : 0
-```
-
-## Network Events
- - #### [Back to Table of Contents](#table-of-contents)
-
-Accessible via `msg->network` as a `zts_network_details` object, this message type will contain information about the status of a particular network your node has joined. *Possible values of `msg->eventCode`:*
-
-```c
-ZTS_EVENT_NETWORK_NOT_FOUND // The network does not exist. The provided networkID may be incorrect.
-ZTS_EVENT_NETWORK_CLIENT_TOO_OLD // This client is too old.
-ZTS_EVENT_NETWORK_REQ_CONFIG // Waiting for network config, this might take a few seconds.
-ZTS_EVENT_NETWORK_OK // Node successfully joined.
-ZTS_EVENT_NETWORK_ACCESS_DENIED // The network is private. Your node requires authorization.
-ZTS_EVENT_NETWORK_READY_IP4 // Your node successfully received an IPv4 address.
-ZTS_EVENT_NETWORK_READY_IP6 // Your node successfully received an IPv6 address.
-ZTS_EVENT_NETWORK_DOWN // For some reason the network is no longer available.
-ZTS_EVENT_NETWORK_UPDATE // The network's config has changed: mtu, name, managed route, etc.
-```
-
-*Example contents of `msg->network`:*
-
-```
-nwid : 8bd712bf36bdae5f
-mac : ae53fa031fcf
-name : cranky_hayes
-type : 0
-mtu : 2800
-dhcp : 0
-bridge : 0
-broadcastEnabled : 1
-portError : 0
-netconfRevision : 34
-routeCount : 1
-multicastSubscriptionCount : 1
-- mac=ffffffffffff, adi=ac1b2561
-addresses:
-- FC5D:69B6:E0F7:46D5:50DD::1
-- 172.27.37.97
-routes:
-- target : 172.27.0.0
-- via : 0.0.0.0
- - flags : 0
- - metric : 0
-```
-
-
-
-## Peer Events
- - #### [Back to Table of Contents](#table-of-contents)
-
-Accessible via `msg->peer` as a `zts_peer_details` object, this message type will contain information about a peer that was discovered by your node. These events are triggered when the reachability status of a peer has changed. *Possible values of `msg->eventCode`:*
-
-```c
-ZTS_EVENT_PEER_DIRECT // At least one direct path to this peer is known.
-ZTS_EVENT_PEER_RELAY // No direct path to this peer is known. It will be relayed, (high packet loss and jitter.)
-ZTS_EVENT_PEER_UNREACHABLE // Peer is not reachable by any means.
-ZTS_EVENT_PEER_PATH_DISCOVERED // A new direct path to this peer has been discovered.
-ZTS_EVENT_PEER_PATH_DEAD // A direct path to this peer has expired.
-```
-
-*Example contents of `msg->peer`:*
-
-```
-peer : a747d5502d
-role : 0
-latency : 4
-version : 1.4.6
-pathCount : 2
- - 172.27.37.97
- - F75D:69B6:E0C7:47D5:51DB::1
-```
-
-## Address Events
- - #### [Back to Table of Contents](#table-of-contents)
-
-Accessible via `msg->addr` as a `zts_addr_details` object, this message type will contain information about addresses assign to your node on a particular network. The information contained in these events is also available via `ZTS_EVENT_NETWORK_UPDATE` events. *Possible values of `msg->eventCode`:*
-
-```c
-ZTS_EVENT_ADDR_ADDED_IP4 // A new IPv4 address was assigned to your node on the indicated network.
-ZTS_EVENT_ADDR_REMOVED_IP4 // An IPv4 address assignment to your node was removed on the indicated network.
-ZTS_EVENT_ADDR_ADDED_IP6 // A new IPv6 address was assigned to your node on the indicated network.
-ZTS_EVENT_ADDR_REMOVED_IP6 // An IPv6 address assignment to your node was removed on the indicated network.
-```
-
-*Example contents of `msg->addr`:*
-
-```
-nwid : a747d5502d
-addr : 172.27.37.97
-```
-
-
-
-# Error handling
- - #### [Back to Table of Contents](#table-of-contents)
-
-Calling a `zts_*` function will result in one of the following return codes. Only when `ZTS_ERR` is returned will `zts_errno` be set. Its values closely mirror those used in standard socket interfaces and are defined in `include/ZeroTierSockets.h`.
-
-```c
-ZTS_ERR_OK // No error
-ZTS_ERR_SOCKET // Socket error (see zts_errno for more information)
-ZTS_ERR_SERVICE // General ZeroTier internal error. Maybe you called something out of order?
-ZTS_ERR_ARG // An argument provided is invalid.
-ZTS_ERR_NO_RESULT // Call succeeded but no result was available. Not necessarily an error.
-ZTS_ERR_GENERAL // General internal failure. Consider filing a bug report.
-```
-
-*NOTE: For Android/Java (or similar) which use JNI, the socket API's error codes are negative values encoded in the return values of function calls*
-*NOTE: For protocol-level errors (such as dropped packets) or internal network stack errors, see the section `Statistics`*
-
-
-
-# Common pitfalls
- - #### [Back to Table of Contents](#table-of-contents)
-
- - If you have started a node but have not received a `ZTS_EVENT_NODE_ONLINE`:
- - You may need to view our [Router Config Tips](https://zerotier.atlassian.net/wiki/spaces/SD/pages/6815768/Router+Configuration+Tips) knowledgebase article. Sometimes this is due to firewall/NAT settings.
-
- - If you have received a `ZTS_EVENT_NODE_ONLINE` event and attempted to join a network but do not see your node ID in the network panel on [my.zerotier.com](my.zerotier.com) after some time:
- - You may have typed in your network ID incorrectly.
- - Used an improper integer representation for your network ID (e.g. `int` instead of `uint64_t`).
-
- - If you are unable to reliably connect to peers:
- - You should first read the section on [Connecting and communicating with peers](#connecting-and-communicating-with-peers).
- - If the previous step doesn't help move onto our knowledgebase article [Router Config Tips](https://zerotier.atlassian.net/wiki/spaces/SD/pages/6815768/Router+Configuration+Tips). Sometimes this can be a transport-triggered link issue, and sometimes it can be a firewall/NAT issue.
-
- - API calls seem to fail in nonsensical ways and you're tearing your hair out:
- - Be sure to read and understand the [API compatibility with host OS](#api-compatibility-with-host-os) section.
- - See the [Debugging](#debugging) section for more advice.
-
-
-
-# API compatibility with host OS
- - #### [Back to Table of Contents](#table-of-contents)
-
-Since libzt re-implements a socket interface likely very similar to your host OS's own interface it may be tempting to mix and match host OS structures and functions with those of libzt. This may work on occasion, but you are tempting fate. Here are a few important guidelines:
-
-If you are calling a `zts_*` function, use the appropriate `ZTS_*` constants:
-```c
-zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT)
-zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT)
-```
-
-If you are calling a `zts_*` function, use the appropriate `zts_*` structure:
-```c
-struct zts_sockaddr_in in4; <------ Note the zts_ prefix
- ...
-zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0)
-```
-
-If you are calling a host OS function, use your host OS's constants (and structures!):
-
-```c
-inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT)
-inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT)
-zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (CORRECT)
-```
-
-If you are calling a host OS function but passing a `zts_*` structure, this can work sometimes but you should take care to pass the correct host OS constants:
-```c
-struct zts_sockaddr_in6 in6;
- ...
-inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN);
-```
-
-
-
-# Thread model
- - #### [Back to Table of Contents](#table-of-contents)
-
-Both the **socket** and **control** interfaces are thread-safe but are implemented differently. The socket interface is implemented using a relatively performant core locking mechanism in lwIP. This can be disabled if you know what you're doing. The control interface is implemented by a single coarse-grained lock. This lock is not a performance bottleneck since it only applies to functions that manipulate the ZeroTier service and are called seldomly. Callback events are generated by a separate thread and are independent from the rest of the API's internal locking mechanism. Not returning from a callback event won't impact the rest of the API but it will prevent your application from receiving future events so it is in your application's best interest to perform as little work as possible in the callback function and promptly return control back to ZeroTier.
-
-*Note: Internally, `libzt` will spawn a number of threads for various purposes: a thread for the core service, a thread for the network stack, a low priority thread to process callback events, and a thread for each network joined. The vast majority of work is performed by the core service and stack threads.*
-
-
-
-# Debugging
- - #### [Back to Table of Contents](#table-of-contents)
-
-If you're experiencing odd behavior or something that looks like a bug I would suggest first reading and understanding the following sections:
-
-* [Common pitfalls](#common-pitfalls)
-* [API compatibility with host OS](#api-compatibility-with-host-os)
-* [Thread model](#thread-model)
-
-If the information in those sections hasn't helped, there are a couple of ways to get debug traces out of various parts of the library.
-
-1) Build the library in debug mode with `make host_debug`. This will prevent the stripping of debug symbols from the library and will enable basic output traces from libzt.
-
-2) If you believe your problem is in the network stack you can manually enable debug traces for individual modules in `src/lwipopts.h`. Toggle the `*_DEBUG` types from `LWIP_DBG_OFF` to `LWIP_DBG_ON`. And then rebuild. This will come with a significant performance cost.
-
-3) Enabling network stack statistics. This is useful if you want to monitor the stack's receipt and handling of traffic as well as internal things like memory allocations and cache hits. Protocol and service statistics are available in debug builds of `libzt`. These statistics are detailed fully in the section of `include/ZeroTierSockets.h` that is guarded by `LWIP_STATS`.
-
- ```c
- struct zts_stats_proto stats;
- if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_ICMP, &stats) == ZTS_ERR_OK) {
- printf("icmp.recv=%d\n", stats.recv); // Count of received pings
- }
- if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_TCP, &stats) == ZTS_ERR_OK) {
- printf("tcp.drop=%d\n", stats.drop); // Count of dropped TCP packets
- }
- ```
-
-4) There are a series of additional events which can signal whether the network stack or its virtual network interfaces have been set up properly. See `ZTS_EVENT_STACK_*` and `ZTS_EVENT_NETIF_*`.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h
index 77587c3..685d6d7 100644
--- a/include/ZeroTierSockets.h
+++ b/include/ZeroTierSockets.h
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -17,8 +17,8 @@
* This defines the external C API for ZeroTier Sockets
*/
-#ifndef ZT_SOCKETS_H
-#define ZT_SOCKETS_H
+#ifndef ZTS_SOCKETS_H
+#define ZTS_SOCKETS_H
#ifdef __cplusplus
extern "C" {
@@ -29,12 +29,12 @@ extern "C" {
//----------------------------------------------------------------------------//
/** Common error return values */
-enum zts_error {
+typedef enum {
/** No error */
ZTS_ERR_OK = 0,
/** Socket error, see `zts_errno` */
ZTS_ERR_SOCKET = -1,
- /** The node service experienced a problem. Did you start the service? */
+ /** This operation is not allowed at this time. Or possibly the node hasn't been started */
ZTS_ERR_SERVICE = -2,
/** Invalid argument */
ZTS_ERR_ARG = -3,
@@ -42,34 +42,81 @@ enum zts_error {
ZTS_ERR_NO_RESULT = -4,
/** Consider filing a bug report */
ZTS_ERR_GENERAL = -5
-};
+} zts_error_t;
//----------------------------------------------------------------------------//
// Event codes //
//----------------------------------------------------------------------------//
-/** Event codes used by the callback API */
-enum zts_event {
- /** The node service started successfully (no action needed) */
+/** Event codes used by the (optional) callback API */
+typedef enum {
+ /**
+ * Node has been initialized
+ *
+ * This is the first event generated, and is always sent. It may occur
+ * before node's constructor returns.
+ *
+ */
ZTS_EVENT_NODE_UP = 200,
- /** The node can reach the Internet */
+
+ /**
+ * Node is online -- at least one upstream node appears reachable
+ *
+ */
ZTS_EVENT_NODE_ONLINE = 201,
- /** The node cannot reach the Internet */
+
+ /**
+ * Node is offline -- network does not seem to be reachable by any available
+ * strategy
+ *
+ */
ZTS_EVENT_NODE_OFFLINE = 202,
- /** The node service has stopped */
+
+ /**
+ * Node is shutting down
+ *
+ * This is generated within Node's destructor when it is being shut down.
+ * It's done for convenience, since cleaning up other state in the event
+ * handler may appear more idiomatic.
+ *
+ */
ZTS_EVENT_NODE_DOWN = 203,
- /** Multiple identities in use (undefined behavior) */
- ZTS_EVENT_NODE_IDENTITY_COLLISION = 204,
- /** Something went horribly wrong */
- ZTS_EVENT_NODE_UNRECOVERABLE_ERROR = 205,
- /** The node has been terminated */
- ZTS_EVENT_NODE_NORMAL_TERMINATION = 206,
- ZTS_EVENT_NODE_WHAT = 207,
+ /**
+ * A fatal error has occurred. One possible reason is:
+ *
+ * Your identity has collided with another node's ZeroTier address
+ *
+ * This happens if two different public keys both hash (via the algorithm
+ * in Identity::generate()) to the same 40-bit ZeroTier address.
+ *
+ * This is something you should "never" see, where "never" is defined as
+ * once per 2^39 new node initializations / identity creations. If you do
+ * see it, you're going to see it very soon after a node is first
+ * initialized.
+ *
+ * This is reported as an event rather than a return code since it's
+ * detected asynchronously via error messages from authoritative nodes.
+ *
+ * If this occurs, you must shut down and delete the node, delete the
+ * identity.secret record/file from the data store, and restart to generate
+ * a new identity. If you don't do this, you will not be able to communicate
+ * with other nodes.
+ *
+ * We'd automate this process, but we don't think silently deleting
+ * private keys or changing our address without telling the calling code
+ * is good form. It violates the principle of least surprise.
+ *
+ * You can technically get away with not handling this, but we recommend
+ * doing so in a mature reliable application. Besides, handling this
+ * condition is a good way to make sure it never arises. It's like how
+ * umbrellas prevent rain and smoke detectors prevent fires. They do, right?
+ *
+ * Meta-data: none
+ */
+ ZTS_EVENT_NODE_FATAL_ERROR = 204,
- // Network events
-
- /** The network ID does not correspond to a known network */
+ /** Network ID does not correspond to a known network */
ZTS_EVENT_NETWORK_NOT_FOUND = 210,
/** The version of ZeroTier inside libzt is too old */
ZTS_EVENT_NETWORK_CLIENT_TOO_OLD = 211,
@@ -90,15 +137,11 @@ enum zts_event {
/** Network change received from controller */
ZTS_EVENT_NETWORK_UPDATE = 219,
- // Network Stack events
-
- /** TCP/IP stack (lwIP) is up */
+ /** TCP/IP stack (lwIP) is up (for debug purposes) */
ZTS_EVENT_STACK_UP = 220,
- /** TCP/IP stack (lwIP) id down */
+ /** TCP/IP stack (lwIP) id down (for debug purposes) */
ZTS_EVENT_STACK_DOWN = 221,
- // lwIP netif events
-
/** lwIP netif up (for debug purposes) */
ZTS_EVENT_NETIF_UP = 230,
/** lwIP netif down (for debug purposes) */
@@ -110,8 +153,6 @@ enum zts_event {
/** lwIP netif link down (for debug purposes) */
ZTS_EVENT_NETIF_LINK_DOWN = 234,
- // Peer events
-
/** A direct P2P path to peer is known */
ZTS_EVENT_PEER_DIRECT = 240,
/** A direct P2P path to peer is NOT known. Traffic is now relayed */
@@ -123,15 +164,11 @@ enum zts_event {
/** A known path to a peer is now considered dead */
ZTS_EVENT_PEER_PATH_DEAD = 244,
- // Route events
-
/** A new managed network route was added */
ZTS_EVENT_ROUTE_ADDED = 250,
/** A managed network route was removed */
ZTS_EVENT_ROUTE_REMOVED = 251,
- // Address events
-
/** A new managed IPv4 address was assigned to this peer */
ZTS_EVENT_ADDR_ADDED_IP4 = 260,
/** A managed IPv4 address assignment was removed from this peer */
@@ -139,162 +176,195 @@ enum zts_event {
/** A new managed IPv4 address was assigned to this peer */
ZTS_EVENT_ADDR_ADDED_IP6 = 262,
/** A managed IPv6 address assignment was removed from this peer */
- ZTS_EVENT_ADDR_REMOVED_IP6 = 263
-};
+ ZTS_EVENT_ADDR_REMOVED_IP6 = 263,
+
+ /** The node's secret key (identity) */
+ ZTS_EVENT_STORE_IDENTITY_SECRET = 270,
+ /** The node's public key (identity) */
+ ZTS_EVENT_STORE_IDENTITY_PUBLIC = 271,
+ /** The node has received an updated planet config */
+ ZTS_EVENT_STORE_PLANET = 272,
+ /** New reachability hints and peer configuration */
+ ZTS_EVENT_STORE_PEER = 273,
+ /** New network config */
+ ZTS_EVENT_STORE_NETWORK = 274
+} zts_event_t;
//----------------------------------------------------------------------------//
// zts_errno Error codes //
//----------------------------------------------------------------------------//
-/** Error variable set after each `zts_*` call. Provides additional information. */
+/**
+ * Error variable set after each `zts_*` socket call. Provides additional error context.
+ */
extern int zts_errno;
-#define ZTS_EPERM 1 /* Operation not permitted */
-#define ZTS_ENOENT 2 /* No such file or directory */
-#define ZTS_ESRCH 3 /* No such process */
-#define ZTS_EINTR 4 /* Interrupted system call */
-#define ZTS_EIO 5 /* I/O error */
-#define ZTS_ENXIO 6 /* No such device or address */
-#define ZTS_E2BIG 7 /* Arg list too long */
-#define ZTS_ENOEXEC 8 /* Exec format error */
-#define ZTS_EBADF 9 /* Bad file number */
-#define ZTS_ECHILD 10 /* No child processes */
-#define ZTS_EAGAIN 11 /* Try again */
-#define ZTS_ENOMEM 12 /* Out of memory */
-#define ZTS_EACCES 13 /* Permission denied */
-#define ZTS_EFAULT 14 /* Bad address */
-#define ZTS_ENOTBLK 15 /* Block device required */
-#define ZTS_EBUSY 16 /* Device or resource busy */
-#define ZTS_EEXIST 17 /* File exists */
-#define ZTS_EXDEV 18 /* Cross-device link */
-#define ZTS_ENODEV 19 /* No such device */
-#define ZTS_ENOTDIR 20 /* Not a directory */
-#define ZTS_EISDIR 21 /* Is a directory */
-#define ZTS_EINVAL 22 /* Invalid argument */
-#define ZTS_ENFILE 23 /* File table overflow */
-#define ZTS_EMFILE 24 /* Too many open files */
-#define ZTS_ENOTTY 25 /* Not a typewriter */
-#define ZTS_ETXTBSY 26 /* Text file busy */
-#define ZTS_EFBIG 27 /* File too large */
-#define ZTS_ENOSPC 28 /* No space left on device */
-#define ZTS_ESPIPE 29 /* Illegal seek */
-#define ZTS_EROFS 30 /* Read-only file system */
-#define ZTS_EMLINK 31 /* Too many links */
-#define ZTS_EPIPE 32 /* Broken pipe */
-#define ZTS_EDOM 33 /* Math argument out of domain of func */
-#define ZTS_ERANGE 34 /* Math result not representable */
-#define ZTS_EDEADLK 35 /* Resource deadlock would occur */
-#define ZTS_ENAMETOOLONG 36 /* File name too long */
-#define ZTS_ENOLCK 37 /* No record locks available */
-#define ZTS_ENOSYS 38 /* Function not implemented */
-#define ZTS_ENOTEMPTY 39 /* Directory not empty */
-#define ZTS_ELOOP 40 /* Too many symbolic links encountered */
-#define ZTS_EWOULDBLOCK ZTS_EAGAIN /* Operation would block */
-#define ZTS_ENOMSG 42 /* No message of desired type */
-#define ZTS_EIDRM 43 /* Identifier removed */
-#define ZTS_ECHRNG 44 /* Channel number out of range */
-#define ZTS_EL2NSYNC 45 /* Level 2 not synchronized */
-#define ZTS_EL3HLT 46 /* Level 3 halted */
-#define ZTS_EL3RST 47 /* Level 3 reset */
-#define ZTS_ELNRNG 48 /* Link number out of range */
-#define ZTS_EUNATCH 49 /* Protocol driver not attached */
-#define ZTS_ENOCSI 50 /* No CSI structure available */
-#define ZTS_EL2HLT 51 /* Level 2 halted */
-#define ZTS_EBADE 52 /* Invalid exchange */
-#define ZTS_EBADR 53 /* Invalid request descriptor */
-#define ZTS_EXFULL 54 /* Exchange full */
-#define ZTS_ENOANO 55 /* No anode */
-#define ZTS_EBADRQC 56 /* Invalid request code */
-#define ZTS_EBADSLT 57 /* Invalid slot */
-#define ZTS_EDEADLOCK ZTS_EDEADLK
-#define ZTS_EBFONT 59 /* Bad font file format */
-#define ZTS_ENOSTR 60 /* Device not a stream */
-#define ZTS_ENODATA 61 /* No data available */
-#define ZTS_ETIME 62 /* Timer expired */
-#define ZTS_ENOSR 63 /* Out of streams resources */
-#define ZTS_ENONET 64 /* Machine is not on the network */
-#define ZTS_ENOPKG 65 /* Package not installed */
-#define ZTS_EREMOTE 66 /* Object is remote */
-#define ZTS_ENOLINK 67 /* Link has been severed */
-#define ZTS_EADV 68 /* Advertise error */
-#define ZTS_ESRMNT 69 /* Srmount error */
-#define ZTS_ECOMM 70 /* Communication error on send */
-#define ZTS_EPROTO 71 /* Protocol error */
-#define ZTS_EMULTIHOP 72 /* Multihop attempted */
-#define ZTS_EDOTDOT 73 /* RFS specific error */
-#define ZTS_EBADMSG 74 /* Not a data message */
-#define ZTS_EOVERFLOW 75 /* Value too large for defined data type */
-#define ZTS_ENOTUNIQ 76 /* Name not unique on network */
-#define ZTS_EBADFD 77 /* File descriptor in bad state */
-#define ZTS_EREMCHG 78 /* Remote address changed */
-#define ZTS_ELIBACC 79 /* Can not access a needed shared library */
-#define ZTS_ELIBBAD 80 /* Accessing a corrupted shared library */
-#define ZTS_ELIBSCN 81 /* .lib section in a.out corrupted */
-#define ZTS_ELIBMAX 82 /* Attempting to link in too many shared libraries */
-#define ZTS_ELIBEXEC 83 /* Cannot exec a shared library directly */
-#define ZTS_EILSEQ 84 /* Illegal byte sequence */
-#define ZTS_ERESTART 85 /* Interrupted system call should be restarted */
-#define ZTS_ESTRPIPE 86 /* Streams pipe error */
-#define ZTS_EUSERS 87 /* Too many users */
-#define ZTS_ENOTSOCK 88 /* Socket operation on non-socket */
-#define ZTS_EDESTADDRREQ 89 /* Destination address required */
-#define ZTS_EMSGSIZE 90 /* Message too long */
-#define ZTS_EPROTOTYPE 91 /* Protocol wrong type for socket */
-#define ZTS_ENOPROTOOPT 92 /* Protocol not available */
-#define ZTS_EPROTONOSUPPORT 93 /* Protocol not supported */
-#define ZTS_ESOCKTNOSUPPORT 94 /* Socket type not supported */
-#define ZTS_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
-#define ZTS_EPFNOSUPPORT 96 /* Protocol family not supported */
-#define ZTS_EAFNOSUPPORT 97 /* Address family not supported by protocol */
-#define ZTS_EADDRINUSE 98 /* Address already in use */
-#define ZTS_EADDRNOTAVAIL 99 /* Cannot assign requested address */
-#define ZTS_ENETDOWN 100 /* Network is down */
-#define ZTS_ENETUNREACH 101 /* Network is unreachable */
-#define ZTS_ENETRESET 102 /* Network dropped connection because of reset */
-#define ZTS_ECONNABORTED 103 /* Software caused connection abort */
-#define ZTS_ECONNRESET 104 /* Connection reset by peer */
-#define ZTS_ENOBUFS 105 /* No buffer space available */
-#define ZTS_EISCONN 106 /* Transport endpoint is already connected */
-#define ZTS_ENOTCONN 107 /* Transport endpoint is not connected */
-#define ZTS_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
-#define ZTS_ETOOMANYREFS 109 /* Too many references: cannot splice */
-#define ZTS_ETIMEDOUT 110 /* Connection timed out */
-#define ZTS_ECONNREFUSED 111 /* Connection refused */
-#define ZTS_EHOSTDOWN 112 /* Host is down */
-#define ZTS_EHOSTUNREACH 113 /* No route to host */
-#define ZTS_EALREADY 114 /* Operation already in progress */
-#define ZTS_EINPROGRESS 115 /* Operation now in progress */
-#define ZTS_ESTALE 116 /* Stale NFS file handle */
-#define ZTS_EUCLEAN 117 /* Structure needs cleaning */
-#define ZTS_ENOTNAM 118 /* Not a XENIX named type file */
-#define ZTS_ENAVAIL 119 /* No XENIX semaphores available */
-#define ZTS_EISNAM 120 /* Is a named type file */
-#define ZTS_EREMOTEIO 121 /* Remote I/O error */
-#define ZTS_EDQUOT 122 /* Quota exceeded */
-#define ZTS_ENOMEDIUM 123 /* No medium found */
-#define ZTS_EMEDIUMTYPE 124 /* Wrong medium type */
+typedef enum {
+ /** Operation not permitted (`zts_errno` value) */
+ ZTS_EPERM = 1,
+ /** No such file or directory */
+ ZTS_ENOENT = 2,
+ /** No such process */
+ ZTS_ESRCH = 3,
+ /** Interrupted system call */
+ ZTS_EINTR = 4,
+ /** I/O error */
+ ZTS_EIO = 5,
+ /** No such device or address */
+ ZTS_ENXIO = 6,
+ /** Bad file number */
+ ZTS_EBADF = 9,
+ /** Try again */
+ ZTS_EAGAIN = 11,
+ /** Operation would block */
+ ZTS_EWOULDBLOCK = ZTS_EAGAIN,
+ /** Out of memory */
+ ZTS_ENOMEM = 12,
+ /** Permission denied */
+ ZTS_EACCES = 13,
+ /** Bad address */
+ ZTS_EFAULT = 14,
+ /** Device or resource busy */
+ ZTS_EBUSY = 16,
+ /** File exists */
+ ZTS_EEXIST = 17,
+ /** No such device */
+ ZTS_ENODEV = 19,
+ /** Invalid argument */
+ ZTS_EINVAL = 22,
+ /** File table overflow */
+ ZTS_ENFILE = 23,
+ /** Too many open files */
+ ZTS_EMFILE = 24,
+ /** Function not implemented */
+ ZTS_ENOSYS = 38,
+ /** Socket operation on non-socket */
+ ZTS_ENOTSOCK = 88,
+ /** Destination address required */
+ ZTS_EDESTADDRREQ = 89,
+ /** Message too long */
+ ZTS_EMSGSIZE = 90,
+ /** Protocol wrong type for socket */
+ ZTS_EPROTOTYPE = 91,
+ /** Protocol not available */
+ ZTS_ENOPROTOOPT = 92,
+ /** Protocol not supported */
+ ZTS_EPROTONOSUPPORT = 93,
+ /** Socket type not supported */
+ ZTS_ESOCKTNOSUPPORT = 94,
+ /** Operation not supported on transport endpoint */
+ ZTS_EOPNOTSUPP = 95,
+ /** Protocol family not supported */
+ ZTS_EPFNOSUPPORT = 96,
+ /** Address family not supported by protocol */
+ ZTS_EAFNOSUPPORT = 97,
+ /** Address already in use */
+ ZTS_EADDRINUSE = 98,
+ /** Cannot assign requested address */
+ ZTS_EADDRNOTAVAIL = 99,
+ /** Network is down */
+ ZTS_ENETDOWN = 100,
+ /** Network is unreachable */
+ ZTS_ENETUNREACH = 101,
+ /** Software caused connection abort */
+ ZTS_ECONNABORTED = 103,
+ /** Connection reset by peer */
+ ZTS_ECONNRESET = 104,
+ /** No buffer space available */
+ ZTS_ENOBUFS = 105,
+ /** Transport endpoint is already connected */
+ ZTS_EISCONN = 106,
+ /** Transport endpoint is not connected */
+ ZTS_ENOTCONN = 107,
+ /** Connection timed out */
+ ZTS_ETIMEDOUT = 110,
+ /** No route to host */
+ ZTS_EHOSTUNREACH = 113,
+ /** Operation already in progress */
+ ZTS_EALREADY = 114,
+ /** Operation now in progress */
+ ZTS_EINPROGRESS = 115
+} zts_errno_t;
+
+//----------------------------------------------------------------------------//
+// Misc definitions //
+//----------------------------------------------------------------------------//
+
+/**
+ * Length of human-readable MAC address string
+ */
+#define ZTS_MAC_ADDRSTRLEN 18
+
+/**
+ * Max length of human-readable IPv4 string
+ */
+#define ZTS_INET_ADDRSTRLEN 16
+
+/**
+ * Max length of human-readable IPv6 string
+ */
+#define ZTS_INET6_ADDRSTRLEN 46
+
+/**
+ * Maximum (and required) length of string buffers used to receive
+ * string-format IP addresses from the API. This is set to `ZTS_INET6_ADDRSTRLEN`
+ * to handle all cases: `ZTS_AF_INET` and `ZTS_AF_INET6`
+ */
+#define ZTS_IP_MAX_STR_LEN ZTS_INET6_ADDRSTRLEN
+
+/**
+ * Required buffer length to safely receive data store items
+ */
+#define ZTS_STORE_DATA_LEN 4096
+
+/**
+ * Maximum length of network short name
+ */
+#define ZTS_MAX_NETWORK_SHORT_NAME_LENGTH 127
+
+/**
+ * Maximum number of pushed routes on a network
+ */
+#define ZTS_MAX_NETWORK_ROUTES 32
+
+/**
+ * Maximum number of statically assigned IP addresses per network endpoint
+ * using ZT address management (not DHCP)
+ */
+#define ZTS_MAX_ASSIGNED_ADDRESSES 16
+
+/**
+ * Maximum number of direct network paths to a given peer
+ */
+#define ZTS_MAX_PEER_NETWORK_PATHS 16
+
+/**
+ * Maximum number of multicast groups a device / network interface can be
+ * subscribed to at once
+ */
+#define ZTS_MAX_MULTICAST_SUBSCRIPTIONS 1024
//----------------------------------------------------------------------------//
// Misc //
//----------------------------------------------------------------------------//
#if ! defined(ZTS_ENABLE_PYTHON) && ! defined(ZTS_ENABLE_PINVOKE)
- #define ZTS_C_API_ONLY 1
+#define ZTS_C_API_ONLY 1
#endif
#if ! ZTS_NO_STDINT_H
- #include
+#include
#endif
#if defined(_MSC_VER)
- #ifndef ssize_t
+#ifndef ssize_t
// TODO: Should be SSIZE_T, would require lwIP patch
// #include
// typedef SSIZE_T ssize_t;
typedef int ssize_t;
- #endif
+#endif
#else
- #include
+#include
#endif
#ifdef ZTS_ENABLE_PINVOKE
@@ -317,8 +387,8 @@ typedef void (*CppCallback)(void* msg);
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
@@ -330,14 +400,14 @@ typedef void (*CppCallback)(void* msg);
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
@@ -345,14 +415,6 @@ typedef void (*CppCallback)(void* msg);
*
*/
-/**
- * Length of human-readable MAC address string
- */
-#define ZTS_MAC_ADDRSTRLEN 18
-
-#define ZTS_INET_ADDRSTRLEN 16
-#define ZTS_INET6_ADDRSTRLEN 46
-
/** 255.255.255.255 */
#define ZTS_IPADDR_NONE ((uint32_t)0xffffffffUL)
/** 127.0.0.1 */
@@ -439,6 +501,9 @@ struct zts_in6_addr {
//#define s6_addr un.u8_addr
};
+/**
+ * Address structure to specify an IPv4 endpoint
+ */
struct zts_sockaddr_in {
uint8_t sin_len;
zts_sa_family_t sin_family;
@@ -448,6 +513,9 @@ struct zts_sockaddr_in {
char sin_zero[SIN_ZERO_LEN];
};
+/**
+ * Address structure to specify an IPv6 endpoint
+ */
struct zts_sockaddr_in6 {
uint8_t sin6_len; // length of this structure
zts_sa_family_t sin6_family; // ZTS_AF_INET6
@@ -457,12 +525,18 @@ struct zts_sockaddr_in6 {
uint32_t sin6_scope_id; // Set of interfaces for scope
};
+/**
+ * Pointers to socket address structures are often cast to this type
+ */
struct zts_sockaddr {
uint8_t sa_len;
zts_sa_family_t sa_family;
char sa_data[14];
};
+/**
+ * Address structure large enough to hold IPv4 and IPv6 addresses
+ */
struct zts_sockaddr_storage {
uint8_t s2_len;
zts_sa_family_t ss_family;
@@ -472,153 +546,61 @@ struct zts_sockaddr_storage {
};
//----------------------------------------------------------------------------//
-// Structures used to convey details during various callback events //
+// Callback Structures //
//----------------------------------------------------------------------------//
/**
- * Maximum address assignments per network
+ * Runtime details about the current node
*/
-#define ZTS_MAX_ASSIGNED_ADDRESSES 16
-
-/**
- * Maximum routes per network
- */
-#define ZTS_MAX_NETWORK_ROUTES 32
-
-/**
- * Maximum number of direct network paths to a given peer
- */
-#define ZTS_MAX_PEER_NETWORK_PATHS 16
-
-/**
- * What trust hierarchy role does this peer have?
- */
-enum zts_peer_role {
- ZTS_PEER_ROLE_LEAF = 0, // ordinary node
- ZTS_PEER_ROLE_MOON = 1, // moon root
- ZTS_PEER_ROLE_PLANET = 2 // planetary root
-};
-
-/**
- * A structure used to convey details about the current node
- * to the user application
- */
-struct zts_node_details {
+typedef struct {
/**
- * The node ID
+ * Node ID
*/
- uint64_t address;
+ uint64_t node_id;
/**
- * The port used by the service to send and receive
- * all encapsulated traffic
+ * Port used by ZeroTier to send and receive traffic
*/
- uint16_t primaryPort;
- uint16_t secondaryPort;
- uint16_t tertiaryPort;
+ uint16_t port_primary;
/**
- * ZT version
+ * Port used by ZeroTier to send and receive traffic
*/
- uint8_t versionMajor;
- uint8_t versionMinor;
- uint8_t versionRev;
-};
+ uint16_t port_secondary;
+
+ /**
+ * Port used by ZeroTier to send and receive traffic
+ */
+ uint16_t port_tertiary;
+
+ /**
+ * ZT Major version
+ */
+ uint8_t ver_major;
+
+ /**
+ * ZT Minor version
+ */
+ uint8_t ver_minor;
+
+ /**
+ * ZT Patch revision
+ */
+ uint8_t ver_rev;
+} zts_node_info_t;
/**
- * A structure used to convey information to a user application via
- * a callback function.
+ * Details about an assigned address that was added or removed
*/
-struct zts_callback_msg {
- /**
- * Event identifier
- */
- int16_t eventCode;
-
- struct zts_node_details* node;
- struct zts_network_details* network;
- struct zts_netif_details* netif;
- struct zts_virtual_network_route* route;
- struct zts_peer_details* peer;
- struct zts_addr_details* addr;
-};
-
-struct zts_addr_details {
- uint64_t nwid;
+typedef struct {
+ uint64_t net_id;
struct zts_sockaddr_storage addr;
-};
-
-/**
- * A structure used to convey information about a virtual network
- * interface (netif) to a user application.
- */
-struct zts_netif_details {
- /**
- * The virtual network that this interface was commissioned for.
- */
- uint64_t nwid;
-
- /**
- * The hardware address assigned to this interface
- */
- uint64_t mac;
-
- /**
- * The MTU for this interface
- */
- int mtu;
-};
-
-/**
- * A structure used to represent a virtual network route
- */
-struct zts_virtual_network_route {
- /**
- * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default
- */
- struct zts_sockaddr_storage target;
-
- /**
- * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway)
- */
- struct zts_sockaddr_storage via;
-
- /**
- * Route flags
- */
- uint16_t flags;
-
- /**
- * Route metric (not currently used)
- */
- uint16_t metric;
-};
-
-/**
- * Maximum length of network short name
- */
-#define ZTS_MAX_NETWORK_SHORT_NAME_LENGTH 127
-
-/**
- * Maximum number of pushed routes on a network
- */
-#define ZTS_MAX_NETWORK_ROUTES 32
-
-/**
- * Maximum number of statically assigned IP addresses per network endpoint using ZT address
- * management (not DHCP)
- */
-#define ZTS_MAX_ZT_ASSIGNED_ADDRESSES 16
-
-/**
- * Maximum number of multicast groups a device / network interface can be subscribed to at once
- */
-#define ZTS_MAX_MULTICAST_SUBSCRIPTIONS 1024
+} zts_addr_info_t;
/**
* Virtual network status codes
*/
-enum ZTS_VirtualNetworkStatus {
+typedef enum {
/**
* Waiting for network configuration (also means revision == 0)
*/
@@ -648,12 +630,12 @@ enum ZTS_VirtualNetworkStatus {
* ZeroTier core version too old
*/
ZTS_NETWORK_STATUS_CLIENT_TOO_OLD = 5
-};
+} zts_network_status_t;
/**
* Virtual network type codes
*/
-enum ZTS_VirtualNetworkType {
+typedef enum {
/**
* Private networks are authorized via certificates of membership
*/
@@ -663,19 +645,21 @@ enum ZTS_VirtualNetworkType {
* Public networks have no access control -- they'll always be AUTHORIZED
*/
ZTS_NETWORK_TYPE_PUBLIC = 1
-};
+} zts_net_info_type_t;
/**
* A route to be pushed on a virtual network
*/
typedef struct {
/**
- * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default
+ * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0
+ * for default
*/
struct zts_sockaddr_storage target;
/**
- * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway)
+ * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local
+ * (no gateway)
*/
struct zts_sockaddr_storage via;
@@ -688,16 +672,49 @@ typedef struct {
* Route metric (not currently used)
*/
uint16_t metric;
-} ZTS_VirtualNetworkRoute;
+} zts_route_info_t;
+
+/**
+ * An Ethernet multicast group
+ */
+typedef struct {
+ /**
+ * MAC address (least significant 48 bits)
+ */
+ uint64_t mac;
+
+ /**
+ * Additional distinguishing information (usually zero)
+ */
+ unsigned long adi;
+} zts_multicast_group_t;
+
+/**
+ * The peer's trust hierarchy role
+ */
+typedef enum {
+ /**
+ * Ordinary node
+ */
+ ZTS_PEER_ROLE_LEAF = 0,
+ /**
+ * Moon root
+ */
+ ZTS_PEER_ROLE_MOON = 1,
+ /**
+ * Planetary root
+ */
+ ZTS_PEER_ROLE_PLANET = 2
+} zts_peer_role_t;
/**
* Virtual network configuration
*/
-struct zts_network_details {
+typedef struct {
/**
* 64-bit ZeroTier network ID
*/
- uint64_t nwid;
+ uint64_t net_id;
/**
* Ethernet MAC (48 bits) that should be assigned to port
@@ -712,12 +729,12 @@ struct zts_network_details {
/**
* Network configuration request status
*/
- enum ZTS_VirtualNetworkStatus status;
+ zts_network_status_t status;
/**
* Network type
*/
- enum ZTS_VirtualNetworkType type;
+ zts_net_info_type_t type;
/**
* Maximum interface MTU
@@ -742,25 +759,27 @@ struct zts_network_details {
int bridge;
/**
- * If nonzero, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic
+ * If nonzero, this network supports and allows broadcast
+ * (ff:ff:ff:ff:ff:ff) traffic
*/
- int broadcastEnabled;
+ int broadcast_enabled;
/**
- * If the network is in PORT_ERROR state, this is the (negative) error code most recently
- * reported
+ * If the network is in PORT_ERROR state, this is the (negative) error code
+ * most recently reported
*/
- int portError;
+ int port_error;
/**
- * Revision number as reported by controller or 0 if still waiting for config
+ * Revision number as reported by controller or 0 if still waiting for
+ * config
*/
- unsigned long netconfRevision;
+ unsigned long netconf_rev;
/**
* Number of assigned addresses
*/
- unsigned int assignedAddressCount;
+ unsigned int assigned_addr_count;
/**
* ZeroTier-assigned addresses (in sockaddr_storage structures)
@@ -772,37 +791,37 @@ struct zts_network_details {
* This is only used for ZeroTier-managed address assignments sent by the
* virtual network's configuration master.
*/
- struct zts_sockaddr_storage assignedAddresses[ZTS_MAX_ZT_ASSIGNED_ADDRESSES];
+ struct zts_sockaddr_storage assigned_addrs[ZTS_MAX_ASSIGNED_ADDRESSES];
/**
* Number of ZT-pushed routes
*/
- unsigned int routeCount;
+ unsigned int route_count;
/**
* Routes (excluding those implied by assigned addresses and their masks)
*/
- ZTS_VirtualNetworkRoute routes[ZTS_MAX_NETWORK_ROUTES];
+ zts_route_info_t routes[ZTS_MAX_NETWORK_ROUTES];
/**
* Number of multicast groups subscribed
*/
- unsigned int multicastSubscriptionCount;
+ unsigned int multicast_sub_count;
/**
* Multicast groups to which this network's device is subscribed
*/
struct {
uint64_t mac; /* MAC in lower 48 bits */
- uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP
- groups */
- } multicastSubscriptions[ZTS_MAX_MULTICAST_SUBSCRIPTIONS];
-};
+ uint32_t adi; /* Additional distinguishing information, usually zero
+ except for IPv4 ARP groups */
+ } multicast_subs[ZTS_MAX_MULTICAST_SUBSCRIPTIONS];
+} zts_net_info_t;
/**
* Physical network path to a peer
*/
-struct zts_physical_path {
+typedef struct {
/**
* Address of endpoint
*/
@@ -811,17 +830,36 @@ struct zts_physical_path {
/**
* Time of last send in milliseconds or 0 for never
*/
- uint64_t lastSend;
+ uint64_t last_tx;
/**
* Time of last receive in milliseconds or 0 for never
*/
- uint64_t lastReceive;
+ uint64_t last_rx;
/**
* Is this a trusted path? If so this will be its nonzero ID.
*/
- uint64_t trustedPathId;
+ uint64_t trusted_path_id;
+
+ /**
+ * One-way latency
+ */
+ float latency;
+
+ float unused_0;
+ float unused_1;
+ float unused_2;
+ float unused_3;
+ float unused_4;
+ uint64_t unused_5;
+ uint64_t unused_6;
+ float unused_7;
+
+ /**
+ * Name of physical interface (for monitoring)
+ */
+ char* ifname;
/**
* Is path expired?
@@ -832,12 +870,12 @@ struct zts_physical_path {
* Is path preferred?
*/
int preferred;
-};
+} zts_path_t;
/**
* Peer status result buffer
*/
-struct zts_peer_details {
+typedef struct {
/**
* ZeroTier address (40 bits)
*/
@@ -846,17 +884,17 @@ struct zts_peer_details {
/**
* Remote major version or -1 if not known
*/
- int versionMajor;
+ int ver_major;
/**
* Remote minor version or -1 if not known
*/
- int versionMinor;
+ int ver_minor;
/**
* Remote revision or -1 if not known
*/
- int versionRev;
+ int ver_rev;
/**
* Last measured latency in milliseconds or -1 if unknown
@@ -866,34 +904,93 @@ struct zts_peer_details {
/**
* What trust hierarchy role does this device have?
*/
- enum zts_peer_role role;
+ zts_peer_role_t role;
/**
* Number of paths (size of paths[])
*/
- unsigned int pathCount;
+ unsigned int path_count;
+
+ /**
+ * Whether this peer was ever reachable via an aggregate link
+ */
+ int unused_0;
/**
* Known network paths to peer
*/
- struct zts_physical_path paths[ZTS_MAX_PEER_NETWORK_PATHS];
-};
+ zts_path_t paths[ZTS_MAX_PEER_NETWORK_PATHS];
+} zts_peer_info_t;
/**
- * List of peers
+ * A structure used to convey information about a virtual network
+ * interface (netif) to a user application.
*/
-struct zts_peer_list {
- struct zts_peer_details* peers;
- unsigned long peerCount;
-};
+typedef struct {
+ /**
+ * The virtual network that this interface was created for
+ */
+ uint64_t net_id;
+
+ /**
+ * The hardware address assigned to this interface
+ */
+ uint64_t mac;
+
+ /**
+ * The MTU for this interface
+ */
+ int mtu;
+} zts_netif_info_t;
+
+/**
+ * Callback message
+ */
+typedef struct {
+ /**
+ * Event identifier
+ */
+ int16_t event_code;
+ /**
+ * Node status
+ */
+ zts_node_info_t* node;
+ /**
+ * Network information
+ */
+ zts_net_info_t* network;
+ /**
+ * Netif status
+ */
+ zts_netif_info_t* netif;
+ /**
+ * Managed routes
+ */
+ zts_route_info_t* route;
+ /**
+ * Peer info
+ */
+ zts_peer_info_t* peer;
+ /**
+ * Assigned address
+ */
+ zts_addr_info_t* addr;
+ /**
+ * Binary data (identities, planets, network configs, peer hints, etc)
+ */
+ void* cache;
+ /**
+ * Length of data message or structure
+ */
+ int len;
+} zts_event_msg_t;
//----------------------------------------------------------------------------//
-// Python Bindings (Subset of regular socket API) //
+// Python Bindings (Subset of regular socket API) //
//----------------------------------------------------------------------------//
#ifdef ZTS_ENABLE_PYTHON
-
- #include "Python.h"
+#include "Python.h"
/**
* Abstract class used as a director. Pointer to an instance of this class
@@ -904,20 +1001,28 @@ class PythonDirectorCallbackClass {
/**
* Called by native code on event. Implemented in Python
*/
- virtual void on_zerotier_event(struct zts_callback_msg* msg);
+ virtual void on_zerotier_event(zts_event_msg_t* msg);
virtual ~PythonDirectorCallbackClass() {};
};
extern PythonDirectorCallbackClass* _userEventCallback;
int zts_py_bind(int fd, int family, int type, PyObject* addro);
+
int zts_py_connect(int fd, int family, int type, PyObject* addro);
+
PyObject* zts_py_accept(int fd);
+
int zts_py_listen(int fd, int backlog);
+
PyObject* zts_py_recv(int fd, int len, int flags);
+
int zts_py_send(int fd, PyObject* buf, int flags);
+
int zts_py_close(int fd);
+
int zts_py_setblocking(int fd, int flag);
+
int zts_py_getblocking(int fd);
#endif // ZTS_ENABLE_PYTHON
@@ -927,63 +1032,69 @@ int zts_py_getblocking(int fd);
//----------------------------------------------------------------------------//
#if defined(_WIN32)
- #ifdef ADD_EXPORTS
- #define ZTS_API __declspec(dllexport)
- #else
- #define ZTS_API __declspec(dllimport)
- #endif
- #define ZTCALL __cdecl
+#ifdef ADD_EXPORTS
+#define ZTS_API __declspec(dllexport)
#else
- #define ZTS_API
- #define ZTCALL
+#define ZTS_API __declspec(dllimport)
+#endif
+#define ZTCALL __cdecl
+#else
+#define ZTS_API
+#define ZTCALL
#endif
//----------------------------------------------------------------------------//
// Central API //
//----------------------------------------------------------------------------//
-#ifdef ZTS_ENABLE_CENTRAL_API
+#ifndef ZTS_DISABLE_CENTRAL_API
- #define ZTS_CENTRAL_DEFAULT_URL "https://my.zerotier.com"
- #define ZTS_CENRTAL_MAX_URL_LEN 128
- #define ZTS_CENTRAL_TOKEN_LEN 32
- #define ZTS_CENTRAL_RESP_BUF_DEFAULT_SZ (128 * 1024)
+#define ZTS_CENTRAL_DEFAULT_URL "https://my.zerotier.com"
+#define ZTS_CENRTAL_MAX_URL_LEN 128
+#define ZTS_CENTRAL_TOKEN_LEN 32
+#define ZTS_CENTRAL_RESP_BUF_DEFAULT_SZ (128 * 1024)
- #define ZTS_HTTP_GET 0
- #define ZTS_HTTP_POST 1
- #define ZTS_HTTP_DELETE 2
+#define ZTS_HTTP_GET 0
+#define ZTS_HTTP_POST 1
+#define ZTS_HTTP_DELETE 2
- #define ZTS_CENTRAL_NODE_AUTH_FALSE 0
- #define ZTS_CENTRAL_NODE_AUTH_TRUE 1
+#define ZTS_CENTRAL_NODE_AUTH_FALSE 0
+#define ZTS_CENTRAL_NODE_AUTH_TRUE 1
- #define ZTS_CENTRAL_READ 1
- #define ZTS_CENTRAL_WRITE 2
+#define ZTS_CENTRAL_READ 1
+#define ZTS_CENTRAL_WRITE 2
/**
- * @brief Enables read/write capability. Default before calling this is
+ * @brief Enable read/write capability. Default before calling this is
* read-only: `ZTS_CENTRAL_READ`
*
- * @param modes Whether the API allows read, write, or both
+ * @param modes `ZTS_CENTRAL_READ` and/or `ZTS_CENTRAL_WRITE`. Whether the API allows read, write,
+ * or both
+ *
+ * @return `ZTS_ERR_OK` if successful. `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API void ZTCALL zts_central_set_access_mode(int8_t modes);
+ZTS_API int ZTCALL zts_central_set_access_mode(int8_t modes);
/**
- * @brief Enables or disables libcurl verbosity
+ * @brief Enable or disable libcurl verbosity
*
- * @param is_verbose Whether debug information is desired
+ * @param is_verbose `[1, 0]`, Whether debug information is desired
+ *
+ * @return `ZTS_ERR_OK` if successful. `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API void ZTCALL zts_central_set_verbose(int8_t is_verbose);
+ZTS_API int ZTCALL zts_central_set_verbose(int8_t is_verbose);
ZTS_API void ZTCALL zts_central_clear_resp_buf();
/**
- * @brief Set the Central API URL and user API token.
+ * @brief Set the Central API `URL` and user API token.
*
* @param url_str The URL to the Central API server
* @param token_str User API token
- * @param resp_buf Destination buffer for raw JSON output
- * @param buf_len Size of buffer for server response (specify `0` for default size)
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * @param resp_buf Destination buffer for raw `JSON` output
+ * @param buf_len Size of buffer for server response (specify `0` for default
+ * size)
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
*/
ZTS_API int ZTCALL
zts_central_init(const char* url_str, const char* token_str, char* resp_buf, uint32_t buf_len);
@@ -991,48 +1102,48 @@ zts_central_init(const char* url_str, const char* token_str, char* resp_buf, uin
ZTS_API void ZTCALL zts_central_cleanup();
/**
- * @brief Copies the JSON-formatted string buffer from the last request into
+ * @brief Copies the `JSON`-formatted string buffer from the last request into
* a user-provided buffer.
*
- * @param dest_buffer User-provided destination buffer
- * @param dest_buf_len Length of aforementioned buffer
+ * @param dst User-provided destination buffer
+ * @param len Length of aforementioned buffer
* @return `ZTS_ERR_OK` if all contents were copied successfully.
* `ZTS_ERR_ARG` if provided buffer was too small.
*/
-ZTS_API int ZTCALL zts_central_get_last_response_buf(char* dest_buffer, int dest_buf_len);
+ZTS_API int ZTCALL zts_central_get_last_resp_buf(char* dst, int len);
/**
* @brief Get the status of the Central API server.
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_status(int* http_response_code);
+ZTS_API int ZTCALL zts_central_status_get(int* http_resp_code);
/**
- * @brief Get the currently authenticated user’s user record.
+ * @brief Get the currently authenticated user’s record.
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_self(int* http_response_code);
+ZTS_API int ZTCALL zts_central_self_get(int* http_resp_code);
/**
- * @brief Retrieve a Network.
+ * @brief Retrieve a `Network`.
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_network(int* http_response_code, uint64_t nwid);
+ZTS_API int ZTCALL zts_central_net_get(int* http_resp_code, uint64_t net_id);
/**
- * @brief Update or create a Network.
+ * @brief Update or create a `Network`.
*
* Only fields marked as [rw] can be directly modified. If other fields are
* present in the posted request they are ignored. New networks can be
- * created by POSTing to /api/network with no networkId parameter. The server
+ * created by POSTing to /api/network with no net_id parameter. The server
* will create a random unused network ID and return the new network record.
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_update_network(int* http_response_code, uint64_t nwid);
+ZTS_API int ZTCALL zts_central_net_update(int* http_resp_code, uint64_t net_id);
/**
* @brief Delete a Network.
@@ -1042,7 +1153,7 @@ ZTS_API int ZTCALL zts_central_update_network(int* http_response_code, uint64_t
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_delete_network(int* http_response_code, uint64_t nwid);
+ZTS_API int ZTCALL zts_central_net_delete(int* http_resp_code, uint64_t net_id);
/**
* @brief Get All Viewable Networks.
@@ -1051,13 +1162,13 @@ ZTS_API int ZTCALL zts_central_delete_network(int* http_response_code, uint64_t
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_networks(int* http_response_code);
+ZTS_API int ZTCALL zts_central_net_get_all(int* http_resp_code);
/**
* @brief Retrieve a Member.
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_member(int* http_response_code, uint64_t nwid, uint64_t nodeid);
+ZTS_API int ZTCALL zts_central_member_get(int* http_resp_code, uint64_t net_id, uint64_t node_id);
/**
* @brief Update or add a Member.
@@ -1067,22 +1178,19 @@ ZTS_API int ZTCALL zts_central_get_member(int* http_response_code, uint64_t nwid
* @return Standard HTTP response codes.
*/
ZTS_API int ZTCALL
-zts_central_update_member(int* http_response_code, uint64_t nwid, uint64_t nodeid, char* post_data);
+zts_central_member_update(int* http_resp_code, uint64_t net_id, uint64_t node_id, char* post_data);
/**
* @brief Authorize or (De)authorize a node on a network. This operation
* is idempotent.
*
- * @param nwid The network ID
- * @param nodeid The node ID
+ * @param net_id Network ID
+ * @param node_id Node ID
* @param is_authed Boolean value for whether this node should be authorized
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_central_set_node_auth(
- int* http_response_code,
- uint64_t nwid,
- uint64_t nodeid,
- uint8_t is_authed);
+ZTS_API int ZTCALL
+zts_central_node_auth(int* http_resp_code, uint64_t net_id, uint64_t node_id, uint8_t is_authed);
/**
* @brief Get All Members of a Network.
@@ -1091,251 +1199,272 @@ ZTS_API int ZTCALL zts_central_set_node_auth(
*
* @return Standard HTTP response codes.
*/
-ZTS_API int ZTCALL zts_central_get_members_of_network(int* http_response_code, uint64_t nwid);
+ZTS_API int ZTCALL zts_central_net_get_members(int* http_resp_code, uint64_t net_id);
-#endif // NO_CENTRAL_API
+#endif // ZTS_DISABLE_CENTRAL_API
//----------------------------------------------------------------------------//
// Identity Management //
//----------------------------------------------------------------------------//
/**
- * @brief Generates a node identity (public/secret key-pair) and stores it in a user-provided
- * buffer.
- *
- * @param key_pair_str User-provided destination buffer
- * @param key_buf_len Length of user-provided destination buffer. Will be set to number of bytes
- * copied.
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * The length of a human-friendly identity key pair string
*/
-ZTS_API int ZTCALL zts_generate_orphan_identity(char* key_pair_str, uint16_t* key_buf_len);
+#define ZTS_ID_STR_BUF_LEN 384
/**
- * @brief Verifies that a key-pair is valid for use.
+ * @brief Generates a node identity (public/secret key-pair) and stores it in a
+ * user-provided buffer.
*
- * @param key_pair_str Buffer containing key-pair
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * @param key User-provided destination buffer
+ * @param key_buf_len Length of user-provided destination buffer. Will be set
+ * to the number of bytes copied.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_verify_identity(const char* key_pair_str);
+ZTS_API int ZTCALL zts_id_new(char* key, uint16_t* key_buf_len);
/**
- * @brief Copies the current node's identity into a buffer
+ * @brief Verifies that a key-pair is valid. Checks formatting and pairing of
+ * key to address.
*
- * @param key_pair_str User-provided destination buffer
- * @param key_buf_len Length of user-provided destination buffer. Will be set to number of bytes
- * copied.
+ * @param key Buffer containing key-pair
+ * @param len Length of key-pair buffer
+ * @return `1` if true, `0` if false.
+ */
+ZTS_API int ZTCALL zts_id_pair_is_valid(const char* key, int len);
+
+/**
+ * @brief Instruct ZeroTier to look for node identity files at the given location. This is an
+ * initialization function that can only be called before `zts_node_start()`.
+ *
+ * Note that calling this function is not mandatory and if it is not called the node's keys will be
+ * kept in memory and retrievable via `zts_node_get_id_pair()`.
+ *
+ * See also: `zts_init_from_memory()`
+ *
+ * @param port Path Null-terminated file-system path string
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_get_node_identity(char* key_pair_str, uint16_t* key_buf_len);
+ZTS_API int ZTCALL zts_init_from_storage(const char* path);
/**
- * @brief Starts the ZeroTier service and notifies user application of events via callback. This
- * variant will assign a user-provided identity to the node.
+ * @brief Instruct ZeroTier to use the identity provided in `key`. This is an initialization
+ * function that can only be called before `zts_node_start()`.
*
- * @param path path directory where configuration files are stored
- * @param callback User-specified callback for `ZTS_EVENT_*` events
- * @param port Port that the library should use for talking to other ZeroTier nodes
+ * Note that calling this function is not mandatory and if it is not called the node's keys will be
+ * kept in memory and retrievable via `zts_node_get_id_pair()`.
+ *
+ * See also: `zts_init_from_storage()`
+ *
+ * @param key Path Null-terminated file-system path string
+ * @param len Length of `key` buffer
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_init_from_memory(const char* key, uint16_t len);
+
+/**
+ * @brief Set the event handler function. This is an initialization function that can only be called
+ * before `zts_node_start()`.
+ *
+ * @param callback A function pointer to the event handler function
+ * @param family `ZTS_AF_INET`, or `ZTS_AF_INET6`
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
#ifdef ZTS_ENABLE_PYTHON
-int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- PythonDirectorCallbackClass* callback,
- uint16_t port);
+ZTS_API int ZTCALL zts_init_set_event_handler(PythonDirectorCallbackClass* callback);
#endif
#ifdef ZTS_ENABLE_PINVOKE
-int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- CppCallback callback,
- uint16_t port);
+ZTS_API int ZTCALL zts_init_set_event_handler(CppCallback callback);
#endif
#ifdef ZTS_C_API_ONLY
-int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- void (*callback)(void*),
- uint16_t port);
+ZTS_API int ZTCALL zts_init_set_event_handler(void (*callback)(void*));
#endif
/**
- * @brief Enable or disable whether the service will cache network details (enabled by default)
+ * @brief Blacklist an interface prefix (or name). This prevents ZeroTier from
+ * sending traffic over matching interfaces. This is an initialization function that can
+ * only be called before `zts_node_start()`.
*
- * This can potentially shorten (startup) times. This allows the service to nearly instantly
- * inform the network stack of an address to use for this peer so that it can
- * create an interface. This can be disabled for cases where one may not want network
- * config details to be written to storage. This is especially useful for situations where
- * address assignments do not change often.
+ * @param prefix Null-terminated interface prefix string
+ * @param len Length of prefix string
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_init_blacklist_if(const char* prefix, int len);
+
+/**
+ * @brief Present a planet definition for ZeroTier to use instead of the default.
+ * This is an initialization function that can only be called before `zts_node_start()`.
*
- * Should be called before `zts_start()` if you intend on changing its state.
+ * @param planet_data Array of planet definition data (binary)
+ * @param len Length of binary data
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_init_set_planet(const char* planet_data, int len);
+
+/**
+ * @brief Set the port to which the node should bind. This is an initialization function that can
+ * only be called before `zts_node_start()`.
+ *
+ * @param port Port number
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_init_set_port(unsigned short port);
+
+/**
+ * @brief Enable or disable whether the node will cache network details
+ * (enabled by default when `zts_init_from_storage()` is used.) Must be called before
+ * `zts_node_start()`.
+ *
+ * This can potentially shorten (startup) times between node restarts. This allows the service to
+ * nearly instantly inform the network stack of an address to use for this peer
+ * so that it can create a transport service. This can be disabled for cases where one
+ * may not want network config details to be written to storage. This is
+ * especially useful for situations where address assignments do not change
+ * often.
+ *
+ * See also: `zts_init_allow_peer_cache()`
*
* @param enabled Whether or not this feature is enabled
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_allow_network_caching(uint8_t allowed);
+ZTS_API int ZTCALL zts_init_allow_net_cache(int allowed);
/**
- * @brief Enable or disable whether the service will cache peer details (enabled by default)
+ * @brief Enable or disable whether the node will cache peer details (enabled
+ * by default when `zts_init_from_storage()` is used.) Must be called before `zts_node_start()`.
*
- * This can potentially shorten (connection) times. This allows the service to
- * re-use previously discovered paths to a peer, this prevents the service from having
- * to go through the entire transport-triggered link provisioning process. This is especially
- * useful for situations where paths to peers do not change often. This is enabled by default
- * and can be disabled for cases where one may not want peer details to be written to storage.
+ * This can potentially shorten (connection) times between node restarts. This allows the service to
+ * re-use previously discovered paths to a peer, this prevents the service from
+ * having to go through the entire transport-triggered link provisioning
+ * process. This is especially useful for situations where paths to peers do not
+ * change often. This is enabled by default and can be disabled for cases where
+ * one may not want peer details to be written to storage.
*
- * Should be called before `zts_start()` if you intend on changing its state.
+ * See also: `zts_init_allow_net_cache()`
*
* @param enabled Whether or not this feature is enabled
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_allow_peer_caching(uint8_t allowed);
+ZTS_API int ZTCALL zts_init_allow_peer_cache(int allowed);
/**
- * @brief Enable or disable whether the service will read node configuration settings from a
- * local.conf
+ * @brief Clear all initialization settings. This is an initialization function that can
+ * only be called before `zts_node_start()` or after `zts_node_stop()`.
*
- * Should be called before `zts_start()` if you intend on changing its state.
- *
- * @param enabled Whether or not this feature is enabled
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem.
*/
-ZTS_API int ZTCALL zts_allow_local_conf(uint8_t allowed);
+ZTS_API int ZTCALL zts_init_clear();
/**
- * @brief Enable or disable whether the service will read or write config data to local storage
+ * @brief Return whether an address of the given family has been assigned by the network
*
- * Should be called before zts_start() if you intend on changing its state.
- *
- * @param enabled Whether or not this feature is enabled
- * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure.
+ * @param net_id Network ID
+ * @param family `ZTS_AF_INET`, or `ZTS_AF_INET6`
+ * @return `1` if true, `0` if false.
*/
-ZTS_API int ZTCALL zts_disable_local_storage(uint8_t disabled);
+ZTS_API int ZTCALL zts_addr_is_assigned(uint64_t net_id, int family);
/**
- * @brief Starts the ZeroTier service and notifies user application of events via callback
+ * @brief Get the first-assigned IP on the given network.
*
- * @param path path directory where configuration files are stored
- * @param callback User-specified callback for ZTS_EVENT_* events
- * @param port Port that the library should use for talking to other ZeroTier nodes
+ * To get *all* assigned addresses on a given network, use `zts_addr_get_all()`.
+ *
+ * @param net_id Network ID
+ * @param family `ZTS_AF_INET`, or `ZTS_AF_INET6`
+ * @param addr Destination buffer to hold address
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-#ifdef ZTS_ENABLE_PYTHON
-ZTS_API int ZTCALL
-zts_start(const char* path, PythonDirectorCallbackClass* callback, uint16_t port);
-#endif
-#ifdef ZTS_ENABLE_PINVOKE
-ZTS_API int ZTCALL zts_start(const char* path, CppCallback callback, uint16_t port);
-#endif
-#ifdef ZTS_C_API_ONLY
-ZTS_API int ZTCALL zts_start(const char* path, void (*callback)(void*), uint16_t port);
-#endif
+ZTS_API int ZTCALL zts_addr_get(uint64_t net_id, int family, struct zts_sockaddr_storage* addr);
/**
- * @brief Stops the ZeroTier service and brings down all virtual network interfaces
+ * @brief Get the first-assigned IP on the given network as a null-terminated human-readable string
*
- * While the ZeroTier service will stop, the stack driver (with associated timers)
- * will remain active in case future traffic processing is required. To stop all activity
- * and free all resources use `zts_free()` instead.
+ * To get *all* assigned addresses on a given network, use `zts_addr_get_all()`.
+ *
+ * @param net_id Network ID
+ * @param family `ZTS_AF_INET`, or `ZTS_AF_INET6`
+ * @param dst Destination buffer
+ * @param len Length of destination buffer (must be exactly `ZTS_IP_MAX_STR_LEN`)
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_stop();
+ZTS_API int ZTCALL zts_addr_get_str(uint64_t net_id, int family, char* dst, int len);
/**
- * @brief Restart the ZeroTier service.
+ * @brief Get all IP addresses assigned to this node by the given network
*
- * This call will block until the service has been brought offline. Then
- * it will return and the user application can then watch for the appropriate
- * startup callback events.
+ * @param net_id Network ID
+ * @param addr Destination buffer to hold address
+ * @param count Number of addresses returned
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_restart();
-
-/**
- * @brief Stop all background services, bring down all interfaces, free all resources. After
- * calling this function an application restart will be required before the library can be
- * used again.
- *
- * This should be called at the end of your program or when you do not anticipate
- * communicating over ZeroTier
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem. Sets `zts_errno`
- */
-ZTS_API int ZTCALL zts_free();
-
-/**
- * @brief Join a network
- *
- * @param networkId A `16-digit hexadecimal` virtual network ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
- */
-ZTS_API int ZTCALL zts_join(const uint64_t networkId);
-
-/**
- * @brief Leave a network
- *
- * @param networkId A `16-digit hexadecimal` virtual network ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
- */
-ZTS_API int ZTCALL zts_leave(const uint64_t networkId);
-
-/**
- * @brief Orbit a given moon (user-defined root server)
- *
- * @param moonWorldId A `16-digit hexadecimal` world ID
- * @param moonSeed A `16-digit hexadecimal` seed ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
- */
-ZTS_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed);
-
-/**
- * @brief De-orbit a given moon (user-defined root server)
- *
- * @param moonWorldId A `16-digit` world ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg.
- */
-ZTS_API int ZTCALL zts_deorbit(uint64_t moonWorldId);
+ZTS_API int ZTCALL zts_addr_get_all(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count);
/**
* @brief Compute a `6PLANE` IPv6 address for the given Network ID and Node ID
*
+ * @param net_id Network ID
+ * @param node_id Node ID
* @param addr Destination structure for address
- * @param networkId Network ID
- * @param nodeId Node ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_get_6plane_addr(
- struct zts_sockaddr_storage* addr,
- const uint64_t networkId,
- const uint64_t nodeId);
+ZTS_API int ZTCALL zts_addr_compute_6plane(
+ const uint64_t net_id,
+ const uint64_t node_id,
+ struct zts_sockaddr_storage* addr);
/**
- * @brief Compute a `RFC4193` IPv6 address for the given Network ID and Node ID
+ * @brief Compute `RFC4193` IPv6 address for the given Network ID and Node ID
*
+ * @param net_id Network ID
+ * @param node_id Node ID
* @param addr Destination structure for address
- * @param networkId Network ID
- * @param nodeId Node ID
- * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid arg.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API int ZTCALL zts_get_rfc4193_addr(
- struct zts_sockaddr_storage* addr,
- const uint64_t networkId,
- const uint64_t nodeId);
+ZTS_API int ZTCALL zts_addr_compute_rfc4193(
+ const uint64_t net_id,
+ const uint64_t node_id,
+ struct zts_sockaddr_storage* addr);
/**
- * @brief Compute a `RFC4193` IPv6 address for the given Network ID and Node ID
+ * @brief Compute `RFC4193` IPv6 address for the given Network ID and Node ID and copy its
+ * null-terminated human-readable string representation into destination buffer.
+ *
+ * @param net_id Network ID
+ * @param node_id Node ID
+ * @param dst Destination string buffer
+ * @param len Length of destination string buffer (must be exactly `ZTS_IP_MAX_STR_LEN`)
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL
+zts_addr_compute_rfc4193_str(uint64_t net_id, uint64_t node_id, char* dst, int len);
+
+/**
+ * @brief Compute `6PLANE` IPv6 address for the given Network ID and Node ID and copy its
+ * null-terminated human-readable string representation into destination buffer.
+ *
+ * @param net_id Network ID
+ * @param node_id Node ID
+ * @param dst Destination string buffer
+ * @param len Length of destination string buffer (must be exactly `ZTS_IP_MAX_STR_LEN`)
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL
+zts_addr_compute_6plane_str(uint64_t net_id, uint64_t node_id, char* dst, int len);
+
+/**
+ * @brief Compute `RFC4193` IPv6 address for the given Network ID and Node ID
*
* Ad-hoc Network:
* ```
@@ -1346,148 +1475,341 @@ ZTS_API int ZTCALL zts_get_rfc4193_addr(
* | Start of port range (hex)
* Reserved ZeroTier address prefix indicating a controller-less network.
* ```
- * Ad-hoc networks are public (no access control) networks that have no network controller. Instead
- * their configuration and other credentials are generated locally. Ad-hoc networks permit only IPv6
- * UDP and TCP unicast traffic (no multicast or broadcast) using 6plane format NDP-emulated IPv6
- * addresses. In addition an ad-hoc network ID encodes an IP port range. UDP packets and TCP SYN
- * (connection open) packets are only allowed to destination ports within the encoded range.
+ * Ad-hoc networks are public (no access control) networks that have no network
+ * controller. Instead their configuration and other credentials are generated
+ * locally. Ad-hoc networks permit only IPv6 UDP and TCP unicast traffic
+ * (no multicast or broadcast) using 6plane format NDP-emulated IPv6 addresses.
+ * In addition an ad-hoc network ID encodes an IP port range. UDP packets and
+ * TCP SYN (connection open) packets are only allowed to destination ports
+ * within the encoded range.
*
- * For example `ff00160016000000` is an ad-hoc network allowing only SSH, while `ff0000ffff000000`
- * is an ad-hoc network allowing any UDP or TCP port.
+ * For example `ff00160016000000` is an ad-hoc network allowing only SSH,
+ * while `ff0000ffff000000` is an ad-hoc network allowing any UDP or TCP port.
*
- * Keep in mind that these networks are public and anyone in the entire world can join them. Care
- * must be taken to avoid exposing vulnerable services or sharing unwanted files or other resources.
+ * Keep in mind that these networks are public and anyone in the entire world
+ * can join them. Care must be taken to avoid exposing vulnerable services or
+ * sharing unwanted files or other resources.
*
*
- * @param startPortOfRange Start of port allowed port range
- * @param endPortOfRange End of allowed port range
+ * @param start_port Start of port allowed port range
+ * @param end_port End of allowed port range
* @return An Ad-hoc network ID
*/
-ZTS_API uint64_t ZTCALL
-zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange);
+ZTS_API uint64_t ZTCALL zts_net_compute_adhoc_id(uint16_t start_port, uint16_t end_port);
/**
- * @brief Platform-agnostic delay (provided for convenience)
+ * @brief Join a network
*
- * @param interval_ms Number of milliseconds to delay
+ * @param net_id Network ID
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
*/
-ZTS_API void ZTCALL zts_delay_ms(long interval_ms);
+ZTS_API int ZTCALL zts_net_join(uint64_t net_id);
+
+/**
+ * @brief Leave a network
+ *
+ * @param net_id Network ID
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_net_leave(uint64_t net_id);
+
+/**
+ * @brief Return number of joined networks
+ *
+ * @return Number of joined networks
+ */
+ZTS_API int ZTCALL zts_net_count();
+
+/**
+ * @brief Get the MAC Address for this node on the given network
+ *
+ * @param net_id Network ID
+ *
+ * @return MAC address in numerical format
+ */
+ZTS_API uint64_t ZTCALL zts_net_get_mac(uint64_t net_id);
+
+/**
+ * @brief Get the MAC Address for this node on the given network
+ *
+ * @param net_id Network ID
+ * @param dst Destination string buffer
+ * @param len Length of destination string buffer. Must be exactly `ZTS_MAC_ADDRSTRLEN`
+ *
+ * @return MAC address in string format
+ */
+ZTS_API int ZTCALL zts_net_get_mac_str(uint64_t net_id, char* dst, int len);
+
+/**
+ * @brief Return whether broadcast is enabled on this network
+ *
+ * @param net_id Network ID
+ *
+ * @return `1` if true, `0` if false.
+ */
+ZTS_API int ZTCALL zts_net_get_broadcast(uint64_t net_id);
+
+/**
+ * @brief Get the MTU of the given network
+ *
+ * @param net_id Network ID
+ *
+ * @return MTU
+ */
+ZTS_API int ZTCALL zts_net_get_mtu(uint64_t net_id);
+
+/**
+ * @brief Get the nickname of the network
+ *
+ * @param net_id Network ID
+ * @param dst Destination string buffer
+ * @param len Length of destination string buffer
+ *
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_net_get_name(uint64_t net_id, char* dst, int len);
+
+/**
+ * @brief Get the status of the network
+ *
+ * @param net_id Network ID
+ *
+ * @return Status
+ */
+ZTS_API int ZTCALL zts_net_get_status(uint64_t net_id);
+
+/**
+ * @brief Get the type of network (public or private.)
+ *
+ * @param net_id Network ID
+ *
+ * @return Type
+ */
+ZTS_API int ZTCALL zts_net_get_type(uint64_t net_id);
+
+/**
+ * @brief Return whether a managed route of the given address family has been assigned by the
+ * network
+ *
+ * @param net_id Network ID
+ * @param family `ZTS_AF_INET`, or `ZTS_AF_INET6`
+ * @return `1` if true, `0` if false.
+ */
+ZTS_API int ZTCALL zts_route_is_assigned(uint64_t net_id, int family);
+
+/**
+ * @brief Start the ZeroTier node. Should be called after calling the relevant
+ * `zts_init_*` functions for your application. To enable storage call
+ * `zts_init_from_storage()` before this function. To enable event callbacks
+ * call `zts_init_set_event_handler()` before this function.
+ *
+ * Note: If neither `zts_init_from_storage()` or `zts_init_from_memory()` are
+ * called a new identity will be generated and will be retrievable via
+ * `zts_node_get_id_pair()` *after* the node has started.
+ *
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem.
+ */
+ZTS_API int ZTCALL zts_node_start();
+
+/**
+ * @brief Return whether the node is online (Can reach the Internet)
+ *
+ * @return `1` if true, `0` if false.
+ */
+ZTS_API int ZTCALL zts_node_is_online();
+
+/**
+ * @brief Get the public node identity (aka `node_id`). Callable only after the node has been
+ * started.
+ *
+ * @return Identity in numerical form
+ */
+ZTS_API uint64_t ZTCALL zts_node_get_id();
+
+/**
+ * @brief Copy the current node's public (and secret!) identity into a buffer.
+ *
+ * `WARNING`: This function exports your secret key and should be used carefully.
+ *
+ * @param key User-provided destination buffer
+ * @param key_buf_len Length of user-provided destination buffer. Will be set to
+ * number of bytes copied.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_node_get_id_pair(char* key, uint16_t* key_buf_len);
+
+/**
+ * @brief Get the primary port to which the node is bound. Callable only after the node has been
+ * started.
+ *
+ * @return Port number
+ */
+ZTS_API int ZTCALL zts_node_get_port();
+
+/**
+ * @brief Stop the ZeroTier node and bring down all virtual network
+ * transport services. Callable only after the node has been started.
+ *
+ * While the ZeroTier will stop, the stack driver (with associated
+ * timers) will remain active in case future traffic processing is required.
+ * To stop all activity and free all resources use `zts_free()` instead.
+ *
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem.
+ */
+ZTS_API int ZTCALL zts_node_stop();
+
+/**
+ * @brief Restart the ZeroTier node. Callable only after the node has been started.
+ *
+ * This call will block until the node has been brought offline. Then
+ * it will return and the user application can then watch for the appropriate
+ * startup callback events.
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem.
+ */
+ZTS_API int ZTCALL zts_node_restart();
+
+/**
+ * @brief Stop all background threads, bring down all transport services, free all
+ * resources. After calling this function an application restart will be
+ * required before the library can be used again. Callable only after the node
+ * has been started.
+ *
+ * This should be called at the end of your program or when you do not
+ * anticipate communicating over ZeroTier again.
+ *
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem.
+ */
+ZTS_API int ZTCALL zts_node_free();
+
+/**
+ * @brief Orbit a given moon (user-defined root server)
+ *
+ * @param moon_world_id World ID
+ * @param moon_seed Seed ID
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_moon_orbit(uint64_t moon_world_id, uint64_t moon_seed);
+
+/**
+ * @brief De-orbit a given moon (user-defined root server)
+ *
+ * @param moon_world_id World ID
+ * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument.
+ */
+ZTS_API int ZTCALL zts_moon_deorbit(uint64_t moon_world_id);
//----------------------------------------------------------------------------//
// Statistics //
//----------------------------------------------------------------------------//
-#ifdef ZTS_ENABLE_STATS
+/**
+ * Structure containing counters for various protocol statistics
+ */
+typedef struct {
+ /** Number of link packets transmitted */
+ uint32_t link_tx;
+ /** Number of link packets received */
+ uint32_t link_rx;
+ /** Number of link packets dropped */
+ uint32_t link_drop;
+ /** Aggregate number of link-level errors */
+ uint32_t link_err;
- #define ZTS_STATS_PROTOCOL_LINK 0
- #define ZTS_STATS_PROTOCOL_ETHARP 1
- #define ZTS_STATS_PROTOCOL_IP 2
- #define ZTS_STATS_PROTOCOL_UDP 3
- #define ZTS_STATS_PROTOCOL_TCP 4
- #define ZTS_STATS_PROTOCOL_ICMP 5
- #define ZTS_STATS_PROTOCOL_IP_FRAG 6
- #define ZTS_STATS_PROTOCOL_IP6 7
- #define ZTS_STATS_PROTOCOL_ICMP6 8
- #define ZTS_STATS_PROTOCOL_IP6_FRAG 9
+ /** Number of etharp packets transmitted */
+ uint32_t etharp_tx;
+ /** Number of etharp packets received */
+ uint32_t etharp_rx;
+ /** Number of etharp packets dropped */
+ uint32_t etharp_drop;
+ /** Aggregate number of etharp errors */
+ uint32_t etharp_err;
-/** Protocol related stats */
-struct zts_stats_proto {
- uint32_t xmit; /* Transmitted packets. */
- uint32_t recv; /* Received packets. */
- uint32_t fw; /* Forwarded packets. */
- uint32_t drop; /* Dropped packets. */
- uint32_t chkerr; /* Checksum error. */
- uint32_t lenerr; /* Invalid length error. */
- uint32_t memerr; /* Out of memory error. */
- uint32_t rterr; /* Routing error. */
- uint32_t proterr; /* Protocol error. */
- uint32_t opterr; /* Error in options. */
- uint32_t err; /* Misc error. */
- uint32_t cachehit;
-};
+ /** Number of IPv4 packets transmitted */
+ uint32_t ip4_tx;
+ /** Number of IPv4 packets received */
+ uint32_t ip4_rx;
+ /** Number of IPv4 packets dropped */
+ uint32_t ip4_drop;
+ /** Aggregate number of IPv4 errors */
+ uint32_t ip4_err;
-/** IGMP stats */
-struct zts_stats_igmp {
- uint32_t xmit; /* Transmitted packets. */
- uint32_t recv; /* Received packets. */
- uint32_t drop; /* Dropped packets. */
- uint32_t chkerr; /* Checksum error. */
- uint32_t lenerr; /* Invalid length error. */
- uint32_t memerr; /* Out of memory error. */
- uint32_t proterr; /* Protocol error. */
- uint32_t rx_v1; /* Received v1 frames. */
- uint32_t rx_group; /* Received group-specific queries. */
- uint32_t rx_general; /* Received general queries. */
- uint32_t rx_report; /* Received reports. */
- uint32_t tx_join; /* Sent joins. */
- uint32_t tx_leave; /* Sent leaves. */
- uint32_t tx_report; /* Sent reports. */
-};
+ /** Number of IPv6 packets transmitted */
+ uint32_t ip6_tx;
+ /** Number of IPv6 packets received */
+ uint32_t ip6_rx;
+ /** Number of IPv6 packets dropped */
+ uint32_t ip6_drop;
+ /** Aggregate number of IPv6 errors */
+ uint32_t ip6_err;
-/** System element stats */
-struct zts_stats_syselem {
- uint32_t used;
- uint32_t max;
- uint32_t err;
-};
+ /** Number of ICMPv4 packets transmitted */
+ uint32_t icmp4_tx;
+ /** Number of ICMPv4 packets received */
+ uint32_t icmp4_rx;
+ /** Number of ICMPv4 packets dropped */
+ uint32_t icmp4_drop;
+ /** Aggregate number of ICMPv4 errors */
+ uint32_t icmp4_err;
-/** System stats */
-struct zts_stats_sys {
- struct zts_stats_syselem sem;
- struct zts_stats_syselem mutex;
- struct zts_stats_syselem mbox;
-};
+ /** Number of ICMPv6 packets transmitted */
+ uint32_t icmp6_tx;
+ /** Number of ICMPv6 packets received */
+ uint32_t icmp6_rx;
+ /** Number of ICMPv6 packets dropped */
+ uint32_t icmp6_drop;
+ /** Aggregate number of ICMPv6 errors */
+ uint32_t icmp6_err;
-/** lwIP stats container */
-struct zts_stats {
- /** Link level */
- struct zts_stats_proto link;
- /** ARP */
- struct zts_stats_proto etharp;
- /** Fragmentation */
- struct zts_stats_proto ip_frag;
- /** IP */
- struct zts_stats_proto ip;
- /** ICMP */
- struct zts_stats_proto icmp;
- /** IGMP */
- struct zts_stats_igmp igmp;
- /** UDP */
- struct zts_stats_proto udp;
- /** TCP */
- struct zts_stats_proto tcp;
- /** System */
- struct zts_stats_sys sys;
- /** IPv6 */
- struct zts_stats_proto ip6;
- /** ICMP6 */
- struct zts_stats_proto icmp6;
- /** IPv6 fragmentation */
- struct zts_stats_proto ip6_frag;
- /** Multicast listener discovery */
- struct zts_stats_igmp mld6;
- /** Neighbor discovery */
- struct zts_stats_proto nd6;
-};
+ /** Number of UDP packets transmitted */
+ uint32_t udp_tx;
+ /** Number of UDP packets received */
+ uint32_t udp_rx;
+ /** Number of UDP packets dropped */
+ uint32_t udp_drop;
+ /** Aggregate number of UDP errors */
+ uint32_t udp_err;
+
+ /** Number of TCP packets transmitted */
+ uint32_t tcp_tx;
+ /** Number of TCP packets received */
+ uint32_t tcp_rx;
+ /** Number of TCP packets dropped */
+ uint32_t tcp_drop;
+ /** Aggregate number of TCP errors */
+ uint32_t tcp_err;
+
+ /** Number of ND6 packets transmitted */
+ uint32_t nd6_tx;
+ /** Number of ND6 packets received */
+ uint32_t nd6_rx;
+ /** Number of ND6 packets dropped */
+ uint32_t nd6_drop;
+ /** Aggregate number of ND6 errors */
+ uint32_t nd6_err;
+} zts_stats_counter_t;
/**
- * @brief Return all statistical counters for all protocols (inefficient)
+ * @brief Get all statistical counters for all protocols and levels, where
+ * *all* means *most*. If you need anything more detailed you should inspect
+ * what is available in `lwip/stats.h`.
*
* This function can only be used in debug builds.
- * @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure.
- */
-ZTS_API int ZTCALL zts_get_all_stats(struct zts_stats* statsDest);
-
-/**
- * @brief Populate the given structure with the requested protocol's
- * statistical counters (from network stack)
*
- * This function can only be used in debug builds.
+ * @param dst Pointer to structure that will be populated with statistics
+ *
* @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure.
*/
-ZTS_API int ZTCALL zts_get_protocol_stats(int protocolType, void* protoStatsDest);
-
-#endif // ZTS_ENABLE_STATS
+ZTS_API int ZTCALL zts_stats_get_all(zts_stats_counter_t* dst);
//----------------------------------------------------------------------------//
// Socket API //
@@ -1496,13 +1818,13 @@ ZTS_API int ZTCALL zts_get_protocol_stats(int protocolType, void* protoStatsDest
/**
* @brief Create a socket
*
- * @param socket_family Address family (ZTS_AF_INET, ZTS_AF_INET6)
- * @param socket_type Type of socket (ZTS_SOCK_STREAM, ZTS_SOCK_DGRAM, ZTS_SOCK_RAW)
+ * @param family `ZTS_AF_INET` or `ZTS_AF_INET6`
+ * @param type `ZTS_SOCK_STREAM` or `ZTS_SOCK_DGRAM`
* @param protocol Protocols supported on this socket
* @return Numbered file descriptor on success, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_socket(const int socket_family, const int socket_type, const int protocol);
+ZTS_API int ZTCALL zts_socket(int family, int type, int protocol);
/**
* @brief Connect a socket to a remote host
@@ -1511,7 +1833,7 @@ ZTS_API int ZTCALL zts_socket(const int socket_family, const int socket_type, co
* @param addr Remote host address to connect to
* @param addrlen Length of address
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_connect(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen);
@@ -1528,7 +1850,6 @@ ZTS_API int ZTCALL zts_connect(int fd, const struct zts_sockaddr* addr, zts_sock
* failure.
*
* @param fd Socket file descriptor
- * @param family Address family: `ZTS_AF_INET` or `ZTS_AF_INET6`
* @param ipstr Human-readable IP string
* @param port Port
* @param timeout_ms (Approximate) amount of time in milliseconds before
@@ -1537,9 +1858,9 @@ ZTS_API int ZTCALL zts_connect(int fd, const struct zts_sockaddr* addr, zts_sock
*
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SOCKET` if the function times
* out with no connection made, `ZTS_ERR_SERVICE` if the node experiences a
- * problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_connect_easy(int fd, int family, char* ipstr, int port, int timeout_ms);
+ZTS_API int ZTCALL zts_simple_connect(int fd, const char* ipstr, int port, int timeout_ms);
/**
* @brief Bind a socket to a local address
@@ -1548,7 +1869,7 @@ ZTS_API int ZTCALL zts_connect_easy(int fd, int family, char* ipstr, int port, i
* @param addr Local interface address to bind to
* @param addrlen Length of address
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen);
@@ -1556,13 +1877,12 @@ ZTS_API int ZTCALL zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen
* @brief Bind a socket to a local address
*
* @param fd Socket file descriptor
- * @param family Address family: `ZTS_AF_INET` or `ZTS_AF_INET6`
* @param ipstr Human-readable IP string
* @param port Port
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_bind_easy(int fd, int family, char* ipstr, int port);
+ZTS_API int ZTCALL zts_simple_bind(int fd, const char* ipstr, int port);
/**
* @brief Listen for incoming connections on socket
@@ -1570,7 +1890,7 @@ ZTS_API int ZTCALL zts_bind_easy(int fd, int family, char* ipstr, int port);
* @param fd Socket file descriptor
* @param backlog Number of backlogged connections allowed
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_listen(int fd, int backlog);
@@ -1581,7 +1901,7 @@ ZTS_API int ZTCALL zts_listen(int fd, int backlog);
* @param addr Address of remote host for accepted connection
* @param addrlen Length of address
* @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_accept(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen);
@@ -1589,14 +1909,70 @@ ZTS_API int ZTCALL zts_accept(int fd, struct zts_sockaddr* addr, zts_socklen_t*
* @brief Accept an incoming connection
*
* @param fd Socket file descriptor
- * @param remoteIpStr Buffer that will receive remote host IP string
+ * @param remote_addr Buffer that will receive remote host IP string
* @param len Size of buffer that will receive remote host IP string
- * Must be set to `ZTS_INET6_ADDRSTRLEN`
+ * (must be exactly `ZTS_IP_MAX_STR_LEN`)
* @param port Port number of the newly connected remote host (value-result)
* @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_accept_easy(int fd, char* remoteIpStr, int len, int* port);
+ZTS_API int ZTCALL zts_simple_accept(int fd, char* remote_addr, int len, int* port);
+
+/**
+ * @brief A convenience function that takes a remote address IP string and creates
+ * the appropriate type of socket, and uses it to connect to a remote host.
+ *
+ * @param remote_ipstr Remote address string. IPv4 or IPv6
+ * @param remote_port Port to
+ *
+ * @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
+ */
+ZTS_API int ZTCALL zts_simple_tcp_client(const char* remote_ipstr, int remote_port);
+
+/**
+ * @brief A convenience function that takes a remote address IP string and creates
+ * the appropriate type of socket, binds, listens, and then accepts on it.
+ *
+ * @param local_ipstr Local address to bind
+ * @param local_port Local port to bind
+ * @param remote_ipstr String-format IP address of newly connected remote host
+ * @param len Length of `remote_ipstr`
+ * @param remote_port Port of remote host
+ *
+ * @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
+ */
+ZTS_API int ZTCALL zts_simple_tcp_server(
+ const char* local_ipstr,
+ int local_port,
+ char* remote_ipstr,
+ int len,
+ int* remote_port);
+
+/**
+ * @brief A convenience function that takes a remote address IP string and creates
+ * the appropriate type of socket, and binds to it.
+ *
+ * @param local_ipstr Local address to bind
+ * @param local_port Local port to bind
+ *
+ * @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
+ */
+ZTS_API int ZTCALL zts_simple_udp_server(const char* local_ipstr, int local_port);
+
+/**
+ * @brief This function doesn't really do anything other than be a namespace
+ * counterpart to `zts_simple_udp_server`. All this function does is create a
+ * `ZTS_SOCK_DGRAM` socket and return its file descriptor.
+ *
+ * @param remote_ipstr Remote address string. IPv4 or IPv6
+ *
+ * @return New file descriptor if successful, `ZTS_ERR_SERVICE` if the node
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
+ */
+ZTS_API int ZTCALL zts_simple_udp_client(const char* remote_ipstr);
// Socket level option number
#define ZTS_SOL_SOCKET 0x0fff
@@ -1644,10 +2020,11 @@ struct zts_linger {
#define ZTS_TCP_KEEPCNT 0x0005
// IPPROTO_IPV6 options
#define ZTS_IPV6_CHECKSUM \
- 0x0007 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */
+ 0x0007 /* RFC3542: calculate and insert the ICMPv6 checksum for raw \
+ sockets. */
#define ZTS_IPV6_V6ONLY \
- 0x001b /* RFC3493: boolean control to restrict ZTS_AF_INET6 sockets to IPv6 communications \
- only. */
+ 0x001b /* RFC3493: boolean control to restrict ZTS_AF_INET6 sockets to \
+ IPv6 communications only. */
// UDPLITE options
#define ZTS_UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */
#define ZTS_UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */
@@ -1725,7 +2102,7 @@ typedef struct zts_ipv6_mreq {
#define ZTS_IPTOS_PREC_ROUTINE 0x00
/**
- * @brief Set socket options
+ * @brief Set socket options.
*
* @param fd Socket file descriptor
* @param level Protocol level to which option name should apply
@@ -1733,13 +2110,13 @@ typedef struct zts_ipv6_mreq {
* @param optval Source of option value to set
* @param optlen Length of option value
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL
zts_setsockopt(int fd, int level, int optname, const void* optval, zts_socklen_t optlen);
/**
- * @brief Get socket options
+ * @brief Get socket options.
*
* @param fd Socket file descriptor
* @param level Protocol level to which option name should apply
@@ -1747,39 +2124,39 @@ zts_setsockopt(int fd, int level, int optname, const void* optval, zts_socklen_t
* @param optval Where option value will be stored
* @param optlen Length of value
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL
zts_getsockopt(int fd, int level, int optname, void* optval, zts_socklen_t* optlen);
/**
- * @brief Get socket name
+ * @brief Get socket name.
*
* @param fd Socket file descriptor
* @param addr Name associated with this socket
* @param addrlen Length of name
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_getsockname(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen);
/**
- * @brief Get the peer name for the remote end of a connected socket
+ * @brief Get the peer name for the remote end of a connected socket.
*
* @param fd Socket file descriptor
* @param addr Name associated with remote end of this socket
* @param addrlen Length of name
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_getpeername(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen);
/**
- * @brief Close a socket
+ * @brief Close socket.
*
* @param fd Socket file descriptor
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_close(int fd);
@@ -1789,58 +2166,59 @@ ZTS_API int ZTCALL zts_close(int fd);
#define MEMP_NUM_NETCONN 1024
#ifndef ZTS_FD_SET
- #undef ZTS_FD_SETSIZE
- // Make FD_SETSIZE match NUM_SOCKETS in socket.c
- #define ZTS_FD_SETSIZE MEMP_NUM_NETCONN
- #define ZTS_FDSETSAFESET(n, code) \
- do { \
- if (((n)-LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) \
- && (((int)(n)-LWIP_SOCKET_OFFSET) >= 0)) { \
- code; \
- } \
- } while (0)
- #define ZTS_FDSETSAFEGET(n, code) \
- (((n)-LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n)-LWIP_SOCKET_OFFSET) >= 0) \
- ? (code) \
- : 0)
- #define ZTS_FD_SET(n, p) \
- ZTS_FDSETSAFESET( \
- n, \
- (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
- #define ZTS_FD_CLR(n, p) \
- ZTS_FDSETSAFESET( \
- n, \
- (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
- #define ZTS_FD_ISSET(n, p) \
- ZTS_FDSETSAFEGET( \
- n, \
- (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
- #define ZTS_FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p)))
+#undef ZTS_FD_SETSIZE
+// Make FD_SETSIZE match NUM_SOCKETS in socket.c
+#define ZTS_FD_SETSIZE MEMP_NUM_NETCONN
+#define ZTS_FDSETSAFESET(n, code) \
+ do { \
+ if (((n)-LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n)-LWIP_SOCKET_OFFSET) >= 0)) { \
+ code; \
+ } \
+ } while (0)
+#define ZTS_FDSETSAFEGET(n, code) \
+ (((n)-LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n)-LWIP_SOCKET_OFFSET) >= 0) ? (code) \
+ : 0)
+#define ZTS_FD_SET(n, p) \
+ ZTS_FDSETSAFESET( \
+ n, \
+ (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define ZTS_FD_CLR(n, p) \
+ ZTS_FDSETSAFESET( \
+ n, \
+ (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define ZTS_FD_ISSET(n, p) \
+ ZTS_FDSETSAFEGET( \
+ n, \
+ (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET) / 8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7)))
+#define ZTS_FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p)))
#elif LWIP_SOCKET_OFFSET
- #error LWIP_SOCKET_OFFSET does not work with external FD_SET!
+#error LWIP_SOCKET_OFFSET does not work with external FD_SET!
#elif ZTS_FD_SETSIZE < MEMP_NUM_NETCONN
- #error "external ZTS_FD_SETSIZE too small for number of sockets"
+#error "external ZTS_FD_SETSIZE too small for number of sockets"
#endif // FD_SET
typedef struct zts_fd_set {
unsigned char fd_bits[(ZTS_FD_SETSIZE + 7) / 8];
} zts_fd_set;
-struct zts_timeval {
+typedef struct zts_timeval {
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
-};
+} zts_timeval;
/**
* @brief Monitor multiple file descriptors for "readiness"
*
- * @param nfds Set to the highest numbered file descriptor in any of the given sets
+ * @param nfds Set to the highest numbered file descriptor in any of the given
+ * sets
* @param readfds Set of file descriptors to monitor for READ readiness
* @param writefds Set of file descriptors to monitor for WRITE readiness
- * @param exceptfds Set of file descriptors to monitor for exceptional conditions
+ * @param exceptfds Set of file descriptors to monitor for exceptional
+ * conditions
* @param timeout How long this call should block
- * @return Number of ready file descriptors on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure.
+ * @return Number of ready file descriptors on success. `ZTS_ERR_SOCKET`,
+ * `ZTS_ERR_SERVICE` on failure. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_select(
int nfds,
@@ -1863,9 +2241,9 @@ ZTS_API int ZTCALL zts_select(
/**
* @brief Issue file control commands on a socket
*
- * @param fd File descriptor
- * @param cmd
- * @param flags
+ * @param fd Socket file descriptor
+ * @param cmd Operation to be performed
+ * @param flags Flags
* @return
*/
ZTS_API int ZTCALL zts_fcntl(int fd, int cmd, int flags);
@@ -1896,8 +2274,9 @@ struct zts_pollfd {
* @param fds Set of file descriptors to monitor
* @param nfds Number of elements in the fds array
* @param timeout How long this call should block
- * @return Number of ready file descriptors if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * @return Number of ready file descriptors if successful, `ZTS_ERR_SERVICE` if
+ * the node experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets
+ * `zts_errno`
*/
ZTS_API int ZTCALL zts_poll(struct zts_pollfd* fds, zts_nfds_t nfds, int timeout);
@@ -1905,10 +2284,10 @@ ZTS_API int ZTCALL zts_poll(struct zts_pollfd* fds, zts_nfds_t nfds, int timeout
* @brief Control a device
*
* @param fd Socket file descriptor
- * @param request
- * @param argp
+ * @param request Selects the control function to be performed
+ * @param argp Additional information
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_ioctl(int fd, unsigned long request, void* argp);
@@ -1920,7 +2299,7 @@ ZTS_API int ZTCALL zts_ioctl(int fd, unsigned long request, void* argp);
* @param len Length of data to write
* @param flags (e.g. `ZTS_MSG_DONTWAIT`, `ZTS_MSG_MORE`)
* @return Number of bytes sent if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_send(int fd, const void* buf, size_t len, int flags);
@@ -1930,11 +2309,11 @@ ZTS_API ssize_t ZTCALL zts_send(int fd, const void* buf, size_t len, int flags);
* @param fd Socket file descriptor
* @param buf Pointer to data buffer
* @param len Length of data to write
- * @param flags
+ * @param flags Specifies type of message transmission
* @param addr Destination address
* @param addrlen Length of destination address
* @return Number of bytes sent if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_sendto(
int fd,
@@ -1968,10 +2347,10 @@ struct zts_msghdr {
* @brief Send message to remote host
*
* @param fd Socket file descriptor
- * @param msg
- * @param flags
+ * @param msg Message to send
+ * @param flags Specifies type of message transmission
* @return Number of bytes sent if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_sendmsg(int fd, const struct zts_msghdr* msg, int flags);
@@ -1981,9 +2360,9 @@ ZTS_API ssize_t ZTCALL zts_sendmsg(int fd, const struct zts_msghdr* msg, int fla
* @param fd Socket file descriptor
* @param buf Pointer to data buffer
* @param len Length of data buffer
- * @param flags
+ * @param flags Specifies the type of message receipt
* @return Number of bytes received if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_recv(int fd, void* buf, size_t len, int flags);
@@ -1993,11 +2372,11 @@ ZTS_API ssize_t ZTCALL zts_recv(int fd, void* buf, size_t len, int flags);
* @param fd Socket file descriptor
* @param buf Pointer to data buffer
* @param len Length of data buffer
- * @param flags
- * @param addr
- * @param addrlen
+ * @param flags Specifies the type of message receipt
+ * @param addr Destination address buffer
+ * @param addrlen Length of destination address buffer
* @return Number of bytes received if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_recvfrom(
int fd,
@@ -2011,43 +2390,43 @@ ZTS_API ssize_t ZTCALL zts_recvfrom(
* @brief Receive a message from remote host
*
* @param fd Socket file descriptor
- * @param msg
- * @param flags
+ * @param msg Message that was received
+ * @param flags Specifies the type of message receipt
* @return Number of bytes received if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_recvmsg(int fd, struct zts_msghdr* msg, int flags);
/**
- * @brief Read bytes from socket onto buffer
+ * @brief Read data from socket onto buffer
*
* @param fd Socket file descriptor
* @param buf Pointer to data buffer
* @param len Length of data buffer to receive data
* @return Number of bytes read if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_read(int fd, void* buf, size_t len);
/**
- * @brief Read bytes from socket into multiple buffers
+ * @brief Read data from socket into multiple buffers
*
* @param fd Socket file descriptor
* @param iov Array of destination buffers
* @param iovcnt Number of buffers to read into
* @return Number of bytes read if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_readv(int fd, const struct zts_iovec* iov, int iovcnt);
/**
- * @brief Write bytes from buffer to socket
+ * @brief Write data from buffer to socket
*
* @param fd Socket file descriptor
* @param buf Pointer to data buffer
* @param len Length of buffer to write
* @return Number of bytes written if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_write(int fd, const void* buf, size_t len);
@@ -2058,7 +2437,7 @@ ZTS_API ssize_t ZTCALL zts_write(int fd, const void* buf, size_t len);
* @param iov Array of source buffers
* @param iovcnt Number of buffers to read from
* @return Number of bytes written if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec* iov, int iovcnt);
@@ -2070,9 +2449,10 @@ ZTS_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec* iov, int iovcn
* @brief Shut down some aspect of a socket
*
* @param fd Socket file descriptor
- * @param how Which aspects of the socket should be shut down
+ * @param how Which aspects of the socket should be shut down. Options are `ZTS_SHUT_RD`,
+ * `ZTS_SHUT_WR`, or `ZTS_SHUT_RDWR`.
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
ZTS_API int ZTCALL zts_shutdown(int fd, int how);
@@ -2080,13 +2460,6 @@ ZTS_API int ZTCALL zts_shutdown(int fd, int how);
// Convenience functions //
//----------------------------------------------------------------------------//
-/**
- * Helper functions that simplify API wrapper generation and usage in other
- * non-C-like languages. Use simple integer types instead of bit flags, limit
- * the number of operations each function performs, prevent the user from
- * needing to manipulate the content of structures in a non-native language.
- */
-
/**
* @brief Enable or disable `TCP_NODELAY`. Enabling this is equivalent to
* turning off Nagle's algorithm
@@ -2094,18 +2467,18 @@ ZTS_API int ZTCALL zts_shutdown(int fd, int how);
* @param fd Socket file descriptor
* @param enabled `[0, 1]` integer value
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_no_delay(int fd, int enabled);
+ZTS_API int ZTCALL zts_simple_set_no_delay(int fd, int enabled);
/**
* @brief Return whether `TCP_NODELAY` is enabled
*
* @param fd Socket file descriptor
* @return `1` if enabled, `0` if disabled, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_no_delay(int fd);
+ZTS_API int ZTCALL zts_simple_get_no_delay(int fd);
/**
* @brief Enable or disable `SO_LINGER` while also setting its value
@@ -2114,27 +2487,27 @@ ZTS_API int ZTCALL zts_get_no_delay(int fd);
* @param enabled `[0, 1]` integer value
* @param value How long socket should linger
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_linger(int fd, int enabled, int value);
+ZTS_API int ZTCALL zts_simple_set_linger(int fd, int enabled, int value);
/**
* @brief Return whether `SO_LINGER` is enabled
*
* @param fd Socket file descriptor
* @return `1` if enabled, `0` if disabled, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_linger_enabled(int fd);
+ZTS_API int ZTCALL zts_simple_get_linger_enabled(int fd);
/**
* @brief Return the value of `SO_LINGER`
*
* @param fd Socket file descriptor
* @return Value of `SO_LINGER` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_linger_value(int fd);
+ZTS_API int ZTCALL zts_simple_get_linger_value(int fd);
/**
* @brief Enable or disable `SO_REUSEADDR`
@@ -2142,56 +2515,58 @@ ZTS_API int ZTCALL zts_get_linger_value(int fd);
* @param fd Socket file descriptor
* @param enabled `[0, 1]` integer value
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_reuse_addr(int fd, int enabled);
+ZTS_API int ZTCALL zts_simple_set_reuse_addr(int fd, int enabled);
/**
* @brief Return whether `SO_REUSEADDR` is enabled
*
* @param fd Socket file descriptor
* @return `1` if enabled, `0` if disabled, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_reuse_addr(int fd);
+ZTS_API int ZTCALL zts_simple_get_reuse_addr(int fd);
/**
* @brief Set the value of `SO_RCVTIMEO`
*
* @param fd Socket file descriptor
- * @param something
+ * @param seconds Number of seconds for timeout
+ * @param microseconds Number of microseconds for timeout
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_recv_timeout(int fd, int seconds, int microseconds);
+ZTS_API int ZTCALL zts_simple_set_recv_timeout(int fd, int seconds, int microseconds);
/**
* @brief Return the value of `SO_RCVTIMEO`
*
* @param fd Socket file descriptor
* @return Value of `SO_RCVTIMEO` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_recv_timeout(int fd);
+ZTS_API int ZTCALL zts_simple_get_recv_timeout(int fd);
/**
* @brief Set the value of `SO_SNDTIMEO`
*
* @param fd Socket file descriptor
- * @param something
+ * @param seconds Number of seconds for timeout
+ * @param microseconds Number of microseconds for timeout
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_send_timeout(int fd, int seconds, int microseconds);
+ZTS_API int ZTCALL zts_simple_set_send_timeout(int fd, int seconds, int microseconds);
/**
* @brief Return the value of `SO_SNDTIMEO`
*
* @param fd Socket file descriptor
* @return Value of `SO_SNDTIMEO` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_send_timeout(int fd);
+ZTS_API int ZTCALL zts_simple_get_send_timeout(int fd);
/**
* @brief Set the value of `SO_SNDBUF`
@@ -2199,18 +2574,18 @@ ZTS_API int ZTCALL zts_get_send_timeout(int fd);
* @param fd Socket file descriptor
* @param size Size of buffer
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_send_buf_size(int fd, int size);
+ZTS_API int ZTCALL zts_simple_set_send_buf_size(int fd, int size);
/**
* @brief Return the value of `SO_SNDBUF`
*
* @param fd Socket file descriptor
* @return Value of `SO_SNDBUF` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_send_buf_size(int fd);
+ZTS_API int ZTCALL zts_simple_get_send_buf_size(int fd);
/**
* @brief Set the value of `SO_RCVBUF`
@@ -2218,18 +2593,18 @@ ZTS_API int ZTCALL zts_get_send_buf_size(int fd);
* @param fd Socket file descriptor
* @param size Size of buffer
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_recv_buf_size(int fd, int size);
+ZTS_API int ZTCALL zts_simple_set_recv_buf_size(int fd, int size);
/**
* @brief Return the value of `SO_RCVBUF`
*
* @param fd Socket file descriptor
* @return Value of `SO_RCVBUF` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_recv_buf_size(int fd);
+ZTS_API int ZTCALL zts_simple_get_recv_buf_size(int fd);
/**
* @brief Set the value of `IP_TTL`
@@ -2237,18 +2612,18 @@ ZTS_API int ZTCALL zts_get_recv_buf_size(int fd);
* @param fd Socket file descriptor
* @param ttl Value of `IP_TTL`
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_ttl(int fd, int ttl);
+ZTS_API int ZTCALL zts_simple_set_ttl(int fd, int ttl);
/**
* @brief Return the value of `IP_TTL`
*
* @param fd Socket file descriptor
- * @return Value of `IP_TTL` `[0,255]` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * @return Value of `IP_TTL` `[0,255]` if successful, `ZTS_ERR_SERVICE` if the
+ * node experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_ttl(int fd);
+ZTS_API int ZTCALL zts_simple_get_ttl(int fd);
/**
* @brief Change blocking behavior `O_NONBLOCK`
@@ -2257,18 +2632,18 @@ ZTS_API int ZTCALL zts_get_ttl(int fd);
* @param enabled `[0, 1]` integer value, `1` maintains default behavior,
* `0` sets to non-blocking mode
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_blocking(int fd, int enabled);
+ZTS_API int ZTCALL zts_simple_set_blocking(int fd, int enabled);
/**
* @brief Return whether blocking mode `O_NONBLOCK` is enabled
*
* @param fd Socket file descriptor
* @return `1` if enabled, `0` if disabled, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_blocking(int fd);
+ZTS_API int ZTCALL zts_simple_get_blocking(int fd);
/**
* @brief Enable or disable `SO_KEEPALIVE`
@@ -2276,18 +2651,18 @@ ZTS_API int ZTCALL zts_get_blocking(int fd);
* @param fd Socket file descriptor
* @param enabled `[0, 1]` integer value
* @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_set_keepalive(int fd, int enabled);
+ZTS_API int ZTCALL zts_simple_set_keepalive(int fd, int enabled);
/**
* @brief Return whether `SO_KEEPALIVE` is enabled
*
* @param fd Socket file descriptor
* @return `1` if enabled, `0` if disabled, `ZTS_ERR_SERVICE` if the node
- * experiences a problem, `ZTS_ERR_ARG` if invalid arg. Sets `zts_errno`
+ * experiences a problem, `ZTS_ERR_ARG` if invalid argument. Sets `zts_errno`
*/
-ZTS_API int ZTCALL zts_get_keepalive(int fd);
+ZTS_API int ZTCALL zts_simple_get_keepalive(int fd);
//----------------------------------------------------------------------------//
// DNS //
@@ -2295,12 +2670,13 @@ ZTS_API int ZTCALL zts_get_keepalive(int fd);
struct zts_hostent {
char* h_name; /* Official name of the host. */
- char** h_aliases; /* A pointer to an array of pointers to alternative host names,
- terminated by a null pointer. */
+ char** h_aliases; /* A pointer to an array of pointers to alternative host
+ names, terminated by a null pointer. */
int h_addrtype; /* Address type. */
int h_length; /* The length, in bytes, of the address. */
- char** h_addr_list; /* A pointer to an array of pointers to network addresses (in
- network byte order) for the host, terminated by a null pointer. */
+ char** h_addr_list; /* A pointer to an array of pointers to network
+ addresses (in network byte order) for the host,
+ terminated by a null pointer. */
#define h_addr h_addr_list[0] /* for backward compatibility */
};
@@ -2312,12 +2688,6 @@ struct zts_hostent {
*/
struct zts_hostent* zts_gethostbyname(const char* name);
-enum zts_ip_addr_type {
- ZTS_IPADDR_TYPE_V4 = 0U,
- ZTS_IPADDR_TYPE_V6 = 6U,
- ZTS_IPADDR_TYPE_ANY = 46U // Dual stack
-};
-
struct zts_ip4_addr {
uint32_t addr;
};
@@ -2346,7 +2716,7 @@ typedef struct zts_ip_addr {
/**
* Initialize one of the DNS servers.
*
- * @param index the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param index the index of the DNS server to set must be `< DNS_MAX_SERVERS`
* @param addr IP address of the DNS server to set
*/
ZTS_API int ZTCALL zts_dns_set_server(uint8_t index, const zts_ip_addr* addr);
@@ -2355,7 +2725,7 @@ ZTS_API int ZTCALL zts_dns_set_server(uint8_t index, const zts_ip_addr* addr);
* Obtain one of the currently configured DNS server.
*
* @param index the index of the DNS server
- * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * @return IP address of the indexed DNS server or `ip_addr_any` if the DNS
* server has not been configured.
*/
ZTS_API const zts_ip_addr* ZTCALL zts_dns_get_server(uint8_t index);
@@ -2363,12 +2733,13 @@ ZTS_API const zts_ip_addr* ZTCALL zts_dns_get_server(uint8_t index);
//----------------------------------------------------------------------------//
// Convenience functions pulled from lwIP //
//----------------------------------------------------------------------------//
+
/**
- * Convert numeric IP address (both versions) into ASCII representation.
- * returns ptr to static buffer; not reentrant!
+ * Convert numeric IP address (both versions) into `ASCII` representation.
+ * returns ptr to static buffer. Not reentrant.
*
* @param addr IP address in network order to convert
- * @return pointer to a global static (!) buffer that holds the ASCII
+ * @return Pointer to a global static (!) buffer that holds the `ASCII`
* representation of addr
*/
char* zts_ipaddr_ntoa(const zts_ip_addr* addr);
@@ -2390,7 +2761,8 @@ int zts_ipaddr_aton(const char* cp, zts_ip_addr* addr);
* @param src Pointer to source address structure
* @param dst Pointer to destination character array
* @param size Size of the destination buffer
- * @return On success, returns a non-null pointer to the destination character array
+ * @return On success, returns a non-null pointer to the destination character
+ * array
*/
ZTS_API const char* ZTCALL
zts_inet_ntop(int family, const void* src, char* dst, zts_socklen_t size);
@@ -2401,26 +2773,45 @@ zts_inet_ntop(int family, const void* src, char* dst, zts_socklen_t size);
* @param family Address family: `ZTS_AF_INET` or `ZTS_AF_INET6`
* @param src Pointer to source character array
* @param dst Pointer to destination address structure
- * @return return `1` on success. `0` or `-1` on failure. (Does not follow `zts_*` conventions)
+ * @return return `1` on success. `0` or `-1` on failure. (Does not follow regular
+ * `zts_*` conventions)
*/
ZTS_API int ZTCALL zts_inet_pton(int family, const char* src, void* dst);
+//----------------------------------------------------------------------------//
+// Utilities //
+//----------------------------------------------------------------------------//
+
+/**
+ * @brief Platform-agnostic delay (provided for convenience)
+ *
+ * @param interval_ms Number of milliseconds to delay
+ */
+ZTS_API void ZTCALL zts_util_delay(long interval_ms);
+
+/**
+ * @brief Return the family type of the IP string
+ *
+ * @param ipstr Either IPv4 or IPv6 string
+ * @return Either `ZTS_AF_INET` or `ZTS_AF_INET6`
+ */
+ZTS_API int ZTCALL zts_util_get_ip_family(const char* ipstr);
+
/**
* Convert human-friendly IP string to `zts_sockaddr_in` or `zts_sockaddr_in6`.
*
- * @param family Address family: `ZTS_AF_INET` or `ZTS_AF_INET6`
* @param src_ipstr Source IP string
* @param port Port
- * @param dest_addr Pointer to destination structure `zts_sockaddr_in` or `zts_sockaddr_in6`
- * @param addrlen Size of destination structure. Value-result: Will be set to actual size of data
- * available
+ * @param dstaddr Pointer to destination structure `zts_sockaddr_in` or
+ * `zts_sockaddr_in6`
+ * @param addrlen Size of destination structure. Value-result: Will be set to
+ * actual size of data available
* @return return `ZTS_ERR_OK` on success, `ZTS_ERR_ARG` if invalid argument
*/
-int ipstr2sockaddr(
- int family,
- char* src_ipstr,
+int zts_util_ipstr_to_saddr(
+ const char* src_ipstr,
int port,
- struct zts_sockaddr* dest_addr,
+ struct zts_sockaddr* dstaddr,
zts_socklen_t* addrlen);
#ifdef __cplusplus
diff --git a/src/Central.cpp b/src/Central.cpp
index bbe71a0..b6e29b8 100644
--- a/src/Central.cpp
+++ b/src/Central.cpp
@@ -4,27 +4,27 @@
* 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: 2025-01-01
+ * 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_CENTRAL_H
-#define ZT_CENTRAL_H
+#ifndef ZTS_DISABLE_CENTRAL_API
-#ifdef ZTS_ENABLE_CENTRAL_API
+#include "Debug.hpp"
+#include "Mutex.hpp"
+#include "OSUtils.hpp"
+#include "ZeroTierSockets.h"
- #include "Debug.hpp"
- #include "Mutex.hpp"
- #include "ZeroTierSockets.h"
+#include
+#include
+#include
+#include
+#include
- #include
- #include
- #include
- #include
- #include
+#define REQ_LEN 64
char api_url[ZTS_CENRTAL_MAX_URL_LEN];
char api_token[ZTS_CENTRAL_TOKEN_LEN + 1];
@@ -41,31 +41,40 @@ using namespace ZeroTier;
Mutex _responseBuffer_m;
- #ifdef __cplusplus
+#ifdef __cplusplus
extern "C" {
- #endif
+#endif
size_t on_data(void* buffer, size_t size, size_t nmemb, void* userp)
{
DEBUG_INFO("buf=%p,size=%zu,nmemb=%zu,userp=%p", buffer, size, nmemb, userp);
int byte_count = (size * nmemb);
if (_resp_buf_offset + byte_count >= _resp_buf_len) {
- DEBUG_ERROR("Out of buffer space. Cannot store response from server");
- return 0; // Signal to libcurl that our buffer is full (triggers a write error.)
+ DEBUG_INFO("Out of buffer space. Cannot store response from server");
+ return 0; // Signal to libcurl that our buffer is full (triggers a
+ // write error.)
}
memcpy(_resp_buf + _resp_buf_offset, buffer, byte_count);
_resp_buf_offset += byte_count;
return byte_count;
}
-void zts_central_set_access_mode(int8_t modes)
+int zts_central_set_access_mode(int8_t modes)
{
+ if (! (modes & ZTS_CENTRAL_READ) && ! (modes & ZTS_CENTRAL_WRITE)) {
+ return ZTS_ERR_ARG;
+ }
_access_modes = modes;
+ return ZTS_ERR_OK;
}
-void zts_central_set_verbose(int8_t is_verbose)
+int zts_central_set_verbose(int8_t is_verbose)
{
+ if (is_verbose != 1 && is_verbose != 0) {
+ return ZTS_ERR_ARG;
+ }
_bIsVerbose = is_verbose;
+ return ZTS_ERR_OK;
}
void zts_central_clear_resp_buf()
@@ -81,7 +90,7 @@ int zts_central_init(
char* resp_buf,
uint32_t resp_buf_len)
{
- _access_modes = ZTS_CENTRAL_READ; // Defauly read-only
+ _access_modes = ZTS_CENTRAL_READ; // Default read-only
_bIsVerbose = 0; // Default disable libcurl verbose output
Mutex::Lock _l(_responseBuffer_m);
if (resp_buf_len == 0) {
@@ -99,7 +108,7 @@ int zts_central_init(
}
else {
memset(api_url, 0, ZTS_CENRTAL_MAX_URL_LEN);
- memcpy(api_url, url_str, url_len);
+ strncpy(api_url, url_str, url_len);
}
int token_len = strlen(token_str);
if (token_len != ZTS_CENTRAL_TOKEN_LEN) {
@@ -107,7 +116,7 @@ int zts_central_init(
}
else {
memset(api_token, 0, ZTS_CENTRAL_TOKEN_LEN);
- memcpy(api_token, token_str, token_len);
+ strncpy(api_token, token_str, token_len);
}
_bInit = true;
return ZTS_ERR_OK;
@@ -118,7 +127,7 @@ void zts_central_cleanup()
curl_global_cleanup();
}
-int _central_req(
+int central_req(
int request_type,
char* central_str,
char* api_route_str,
@@ -128,15 +137,17 @@ int _central_req(
{
int err = ZTS_ERR_OK;
if (! _bInit) {
- DEBUG_ERROR("Error: Central API must be initialized first. Call zts_central_init()");
+ DEBUG_INFO("Error: Central API must be initialized first. Call "
+ "zts_central_init()");
return ZTS_ERR_SERVICE;
}
if (request_type == ZTS_HTTP_GET && ! (_access_modes & ZTS_CENTRAL_READ)) {
- DEBUG_ERROR("Error: Incorrect access mode. Need (ZTS_CENTRAL_READ) permission");
+ DEBUG_INFO("Error: Incorrect access mode. Need (ZTS_CENTRAL_READ) permission");
return ZTS_ERR_SERVICE;
}
if (request_type == ZTS_HTTP_POST && ! (_access_modes & ZTS_CENTRAL_WRITE)) {
- DEBUG_ERROR("Error: Incorrect access mode. Need (ZTS_CENTRAL_WRITE) permission");
+ DEBUG_INFO("Error: Incorrect access mode. Need (ZTS_CENTRAL_WRITE) "
+ "permission");
return ZTS_ERR_SERVICE;
}
zts_central_clear_resp_buf();
@@ -150,9 +161,9 @@ int _central_req(
if (url_len > ZTS_CENRTAL_MAX_URL_LEN) {
return ZTS_ERR_ARG;
}
- char req_url[ZTS_CENRTAL_MAX_URL_LEN];
- strcpy(req_url, central_str);
- strcat(req_url, api_route_str);
+ char req_url[ZTS_CENRTAL_MAX_URL_LEN] = { 0 };
+ strncpy(req_url, central_str, ZTS_CENRTAL_MAX_URL_LEN);
+ strncat(req_url, api_route_str, ZTS_CENRTAL_MAX_URL_LEN);
CURL* curl;
CURLcode res;
@@ -162,10 +173,13 @@ int _central_req(
}
struct curl_slist* hs = NULL;
- char auth_str[ZTS_CENTRAL_TOKEN_LEN + 32];
+ char auth_str[ZTS_CENTRAL_TOKEN_LEN + 32] = { 0 }; // + Authorization: Bearer
if (token_strlen == ZTS_CENTRAL_TOKEN_LEN) {
- memset(auth_str, 0, ZTS_CENTRAL_TOKEN_LEN + 32);
- sprintf(auth_str, "Authorization: Bearer %s", token_str);
+ OSUtils::ztsnprintf(
+ auth_str,
+ ZTS_CENTRAL_TOKEN_LEN + 32,
+ "Authorization: Bearer %s",
+ token_str);
}
hs = curl_slist_append(hs, auth_str);
@@ -194,7 +208,8 @@ int _central_req(
if (request_type == ZTS_HTTP_DELETE) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
}
- // curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); // Consider 400-500 series code as failures
+ // curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); // Consider 400-500
+ // series code as failures
// Perform request
res = curl_easy_perform(curl);
if (res == CURLE_OK) {
@@ -208,7 +223,7 @@ int _central_req(
// curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
}
else {
- DEBUG_ERROR("%s", curl_easy_strerror(res));
+ DEBUG_INFO("%s", curl_easy_strerror(res));
err = ZTS_ERR_SERVICE;
}
curl_easy_cleanup(curl);
@@ -225,87 +240,86 @@ int zts_get_last_resp_buf(char* dest_buffer, int dest_buf_len)
return ZTS_ERR_OK;
}
-int zts_central_get_status(int* resp_code)
+int zts_central_status_get(int* resp_code)
{
- return _central_req(ZTS_HTTP_GET, api_url, (char*)"/api/status", api_token, resp_code, NULL);
+ return central_req(ZTS_HTTP_GET, api_url, (char*)"/api/status", api_token, resp_code, NULL);
}
-int zts_central_get_self(int* resp_code)
+int zts_central_self_get(int* resp_code)
{
- return _central_req(ZTS_HTTP_GET, api_url, (char*)"/api/self", api_token, resp_code, NULL);
+ return central_req(ZTS_HTTP_GET, api_url, (char*)"/api/self", api_token, resp_code, NULL);
}
-int zts_central_get_network(int* resp_code, uint64_t nwid)
+int zts_central_net_get(int* resp_code, uint64_t net_id)
{
- char req[64];
- sprintf(req, "/api/network/%llx", nwid);
- return _central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
+ return central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
}
-int zts_central_update_network(int* resp_code, uint64_t nwid)
+int zts_central_net_update(int* resp_code, uint64_t net_id)
{
- char req[64];
- sprintf(req, "/api/network/%llx", nwid);
- return _central_req(ZTS_HTTP_POST, api_url, req, api_token, resp_code, NULL);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
+ return central_req(ZTS_HTTP_POST, api_url, req, api_token, resp_code, NULL);
}
-int zts_central_delete_network(int* resp_code, uint64_t nwid)
+int zts_central_net_delete(int* resp_code, uint64_t net_id)
{
- char req[64];
- sprintf(req, "/api/network/%llx", nwid);
- return _central_req(ZTS_HTTP_DELETE, api_url, req, api_token, resp_code, NULL);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
+ return central_req(ZTS_HTTP_DELETE, api_url, req, api_token, resp_code, NULL);
}
-int zts_central_get_networks(int* resp_code)
+int zts_central_net_get_all(int* resp_code)
{
- return _central_req(ZTS_HTTP_GET, api_url, (char*)"/api/network", api_token, resp_code, NULL);
+ return central_req(ZTS_HTTP_GET, api_url, (char*)"/api/network", api_token, resp_code, NULL);
}
-int zts_central_get_member(int* resp_code, uint64_t nwid, uint64_t nodeid)
+int zts_central_member_get(int* resp_code, uint64_t net_id, uint64_t node_id)
{
- if (nwid == 0 || nodeid == 0) {
+ if (net_id == 0 || node_id == 0) {
return ZTS_ERR_ARG;
}
- char req[64];
- sprintf(req, "/api/network/%llx/member/%llx", nwid, nodeid);
- return _central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx/member/%llx", net_id, node_id);
+ return central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
}
-int zts_central_update_member(int* resp_code, uint64_t nwid, uint64_t nodeid, char* post_data)
+int zts_central_member_update(int* resp_code, uint64_t net_id, uint64_t node_id, char* post_data)
{
- if (nwid == 0 || nodeid == 0 || post_data == NULL) {
+ if (net_id == 0 || node_id == 0 || post_data == NULL) {
return ZTS_ERR_ARG;
}
- char req[64];
- sprintf(req, "/api/network/%llx/member/%llx", nwid, nodeid);
- return _central_req(ZTS_HTTP_POST, api_url, req, api_token, resp_code, post_data);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx/member/%llx", net_id, node_id);
+ return central_req(ZTS_HTTP_POST, api_url, req, api_token, resp_code, post_data);
}
-int zts_central_set_node_auth(int* resp_code, uint64_t nwid, uint64_t nodeid, uint8_t is_authed)
+int zts_central_node_auth(int* resp_code, uint64_t net_id, uint64_t node_id, uint8_t is_authed)
{
if (is_authed != 0 && is_authed != 1) {
return ZTS_ERR_ARG;
}
- char config_data[64];
+ char config_data[REQ_LEN] = { 0 };
if (is_authed == ZTS_CENTRAL_NODE_AUTH_TRUE) {
- sprintf(config_data, "{\"config\": {\"authorized\": true} }");
+ OSUtils::ztsnprintf(config_data, REQ_LEN, "{\"config\": {\"authorized\": true} }");
}
if (is_authed == ZTS_CENTRAL_NODE_AUTH_FALSE) {
- sprintf(config_data, "{\"config\": {\"authorized\": false} }");
+ OSUtils::ztsnprintf(config_data, REQ_LEN, "{\"config\": {\"authorized\": false} }");
}
- return zts_central_update_member(resp_code, nwid, nodeid, config_data);
+ return zts_central_member_update(resp_code, net_id, node_id, config_data);
}
-int zts_central_get_members_of_network(int* resp_code, uint64_t nwid)
+int zts_central_net_get_members(int* resp_code, uint64_t net_id)
{
- char req[64];
- sprintf(req, "/api/network/%llx/member", nwid);
- return _central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
+ char req[REQ_LEN] = { 0 };
+ OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx/member", net_id);
+ return central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
}
- #ifdef __cplusplus
+#ifdef __cplusplus
} // extern "C"
- #endif
+#endif
-#endif // ZTS_ENABLE_CENTRAL_API
-#endif // _H
+#endif // ZTS_DISABLE_CENTRAL_API
diff --git a/src/Controls.cpp b/src/Controls.cpp
index f487577..acfece1 100644
--- a/src/Controls.cpp
+++ b/src/Controls.cpp
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -18,39 +18,35 @@
*/
#include "Constants.hpp"
-#include "Debug.hpp"
#include "Events.hpp"
#include "Mutex.hpp"
#include "Node.hpp"
#include "NodeService.hpp"
#include "OSUtils.hpp"
#include "Signals.hpp"
+#include "Thread.hpp"
#include "VirtualTap.hpp"
#include "ZeroTierSockets.h"
+#include "lwip/stats.h"
#include
#include
+#include
using namespace ZeroTier;
-#ifdef ZTS_ENABLE_JAVA
- #include
-#endif
-
#ifdef __WINDOWS__
- #include
+#include
WSADATA wsaData;
#endif
#ifdef ZTS_ENABLE_PYTHON
- #define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1
+#define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1
#endif
namespace ZeroTier {
-extern NodeService* service;
-extern Mutex serviceLock;
-#ifdef ZTS_ENABLE_PYTHON
+#ifdef ZTS_ENABLE_PYTHON
#endif
#ifdef ZTS_ENABLE_PINVOKE
extern void (*_userEventCallback)(void*);
@@ -60,535 +56,613 @@ extern void (*_userEventCallback)(void*);
#endif
extern uint8_t allowNetworkCaching;
extern uint8_t allowPeerCaching;
-extern uint8_t allowLocalConf;
-extern uint8_t disableLocalStorage; // Off by default
-#ifdef ZTS_ENABLE_JAVA
-// References to JNI objects and VM kept for future callbacks
-JavaVM* jvm = NULL;
-jobject objRef = NULL;
-jmethodID _userCallbackMethodRef = NULL;
-#endif
-extern uint8_t _serviceStateFlags;
-} // namespace ZeroTier
+
+NodeService* zts_service;
+Events* zts_events;
+
+extern Mutex events_m;
+Mutex service_m;
+
+/** Set up service and callback threads and tell them about one another.
+ * A separate thread is used for callbacks so that if the user fails to return
+ * control it won't affect the core service's operations.
+ */
+int init_subsystems()
+{
+ if (zts_events->getState(ZTS_STATE_FREE_CALLED)) {
+ return ZTS_ERR_SERVICE;
+ }
+#ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
+ _install_signal_handlers();
+#endif // ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
+ if (! zts_events) {
+ zts_events = new Events();
+ }
+ if (! zts_service) {
+ zts_service = new NodeService();
+ zts_service->setUserEventSystem(zts_events);
+ }
+ return ZTS_ERR_OK;
+}
#ifdef __cplusplus
extern "C" {
#endif
-int zts_generate_orphan_identity(char* key_pair_str, uint16_t* key_buf_len)
+int zts_init_from_storage(const char* path)
{
- if (*key_buf_len < ZT_IDENTITY_STRING_BUFFER_LENGTH || key_pair_str == NULL) {
- return ZTS_ERR_ARG;
- }
- Identity id;
- id.generate();
- char idtmp[1024];
- std::string idser = id.toString(true, idtmp);
- uint16_t key_pair_len = idser.length();
- if (key_pair_len > *key_buf_len) {
- return ZTS_ERR_ARG;
- }
- memcpy(key_pair_str, idser.c_str(), key_pair_len);
- *key_buf_len = key_pair_len;
+ ACQUIRE_SERVICE_OFFLINE();
+ zts_service->setHomePath(path);
return ZTS_ERR_OK;
}
-int zts_verify_identity(const char* key_pair_str)
+int zts_init_from_memory(const char* keypair, uint16_t len)
{
- if (key_pair_str == NULL || strlen(key_pair_str) > ZT_IDENTITY_STRING_BUFFER_LENGTH) {
- return false;
- }
- Identity id;
- if ((strlen(key_pair_str) > 32) && (key_pair_str[10] == ':')) {
- if (id.fromString(key_pair_str)) {
- return true;
- }
- }
- return false;
-}
-
-int zts_get_node_identity(char* key_pair_str, uint16_t* key_buf_len)
-{
- Mutex::Lock _l(serviceLock);
- if (*key_buf_len == 0 || key_pair_str == NULL) {
- return ZTS_ERR_ARG;
- }
- if (! service) {
- return ZTS_ERR_SERVICE;
- }
- service->getIdentity(key_pair_str, key_buf_len);
- return *key_buf_len > 0 ? ZTS_ERR_OK : ZTS_ERR_GENERAL;
-}
-
-// TODO: This logic should be further generalized in the next API redesign
-#ifdef ZTS_ENABLE_PYTHON
-int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- PythonDirectorCallbackClass* callback,
- uint16_t port)
-#endif
-#ifdef ZTS_ENABLE_PINVOKE
- int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- CppCallback callback,
- uint16_t port)
-#endif
-#ifdef ZTS_C_API_ONLY
- int zts_start_with_identity(
- const char* key_pair_str,
- uint16_t key_buf_len,
- void (*callback)(void*),
- uint16_t port)
-#endif
-{
- if (! zts_verify_identity(key_pair_str)) {
- return ZTS_ERR_ARG;
- }
- Mutex::Lock _l(serviceLock);
-#ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
- _install_signal_handlers();
-#endif // ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
- _lwip_driver_init();
- if (service || _getState(ZTS_STATE_NODE_RUNNING)) {
- // Service is already initialized
- return ZTS_ERR_SERVICE;
- }
- if (_getState(ZTS_STATE_FREE_CALLED)) {
- // Stack (presumably lwIP) has been dismantled,
- // an application restart is required now
- return ZTS_ERR_SERVICE;
- }
- _userEventCallback = callback;
- if (! _isCallbackRegistered()) {
- // Must have a callback
- return ZTS_ERR_ARG;
- }
-
- serviceParameters* params = new serviceParameters();
- params->port = port;
- params->path.clear();
-
- Identity id;
- if ((strlen(key_pair_str) > 32) && (key_pair_str[10] == ':')) {
- if (id.fromString(key_pair_str)) {
- id.toString(false, params->publicIdentityStr);
- id.toString(true, params->secretIdentityStr);
- }
- }
- if (! id) {
- delete params;
- return ZTS_ERR_ARG;
- }
-
- int err;
- int retval = ZTS_ERR_OK;
-
- _setState(ZTS_STATE_CALLBACKS_RUNNING);
- _setState(ZTS_STATE_NODE_RUNNING);
- // Start the ZT service thread
-#if defined(__WINDOWS__)
- WSAStartup(MAKEWORD(2, 2), &wsaData);
- HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)params, 0, NULL);
- HANDLE callbackThread = CreateThread(NULL, 0, _runCallbacks, NULL, 0, NULL);
-#else
- pthread_t service_thread;
- pthread_t callback_thread;
- if ((err = pthread_create(&service_thread, NULL, _runNodeService, (void*)params)) != 0) {
- retval = err;
- }
- if ((err = pthread_create(&callback_thread, NULL, _runCallbacks, NULL)) != 0) {
- retval = err;
- }
-#endif
-#if defined(__linux__)
- pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME);
- pthread_setname_np(callback_thread, ZTS_EVENT_CALLBACK_THREAD_NAME);
-#endif
- if (retval != ZTS_ERR_OK) {
- _clrState(ZTS_STATE_CALLBACKS_RUNNING);
- _clrState(ZTS_STATE_NODE_RUNNING);
- _clearRegisteredCallback();
- // delete params;
- }
- return retval;
-}
-
-int zts_allow_network_caching(uint8_t allowed = 1)
-{
- Mutex::Lock _l(serviceLock);
- if (! service) {
- allowNetworkCaching = allowed;
- return ZTS_ERR_OK;
- }
- return ZTS_ERR_SERVICE;
-}
-
-int zts_allow_peer_caching(uint8_t allowed = 1)
-{
- Mutex::Lock _l(serviceLock);
- if (! service) {
- allowPeerCaching = allowed;
- return ZTS_ERR_OK;
- }
- return ZTS_ERR_SERVICE;
-}
-
-int zts_allow_local_conf(uint8_t allowed = 1)
-{
- Mutex::Lock _l(serviceLock);
- if (! service) {
- allowLocalConf = allowed;
- return ZTS_ERR_OK;
- }
- return ZTS_ERR_SERVICE;
-}
-
-int zts_disable_local_storage(uint8_t disabled)
-{
- Mutex::Lock _l(serviceLock);
- if (! service) {
- disableLocalStorage = disabled;
- return ZTS_ERR_OK;
- }
- return ZTS_ERR_SERVICE;
+ ACQUIRE_SERVICE_OFFLINE();
+ return zts_service->setIdentity(keypair, len);
}
#ifdef ZTS_ENABLE_PYTHON
-int zts_start(const char* path, PythonDirectorCallbackClass* callback, uint16_t port)
+int zts_init_set_event_handler(PythonDirectorCallbackClass* callback);
#endif
#ifdef ZTS_ENABLE_PINVOKE
- int zts_start(const char* path, CppCallback callback, uint16_t port)
+int zts_init_set_event_handler(CppCallback callback);
#endif
#ifdef ZTS_C_API_ONLY
- int zts_start(const char* path, void (*callback)(void*), uint16_t port)
+int zts_init_set_event_handler(void (*callback)(void*))
#endif
{
- Mutex::Lock _l(serviceLock);
-#ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
- _install_signal_handlers();
-#endif // ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
- _lwip_driver_init();
- if (service || _getState(ZTS_STATE_NODE_RUNNING)) {
- // Service is already initialized
- return ZTS_ERR_SERVICE;
- }
- if (_getState(ZTS_STATE_FREE_CALLED)) {
- // Stack (presumably lwIP) has been dismantled,
- // an application restart is required now
- return ZTS_ERR_SERVICE;
+ ACQUIRE_SERVICE_OFFLINE();
+ if (! callback) {
+ return ZTS_ERR_ARG;
}
_userEventCallback = callback;
- if (! _isCallbackRegistered()) {
- // Must have a callback
- return ZTS_ERR_ARG;
- }
- if (! path) {
- return ZTS_ERR_ARG;
- }
- serviceParameters* params = new serviceParameters();
- params->port = port;
- params->path = std::string(path);
-
- if (params->path.length() == 0) {
- delete params;
- return ZTS_ERR_ARG;
- }
- int err;
- int retval = ZTS_ERR_OK;
-
- _setState(ZTS_STATE_CALLBACKS_RUNNING);
- _setState(ZTS_STATE_NODE_RUNNING);
- // Start the ZT service thread
-#if defined(__WINDOWS__)
- WSAStartup(MAKEWORD(2, 2), &wsaData);
- HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)params, 0, NULL);
- HANDLE callbackThread = CreateThread(NULL, 0, _runCallbacks, NULL, 0, NULL);
-#else
- pthread_t service_thread;
- pthread_t callback_thread;
- if ((err = pthread_create(&service_thread, NULL, _runNodeService, (void*)params)) != 0) {
- retval = err;
- }
- if ((err = pthread_create(&callback_thread, NULL, _runCallbacks, NULL)) != 0) {
- retval = err;
- }
-#endif
-#if defined(__linux__)
- pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME);
- pthread_setname_np(callback_thread, ZTS_EVENT_CALLBACK_THREAD_NAME);
-#endif
- if (retval != ZTS_ERR_OK) {
- _clrState(ZTS_STATE_CALLBACKS_RUNNING);
- _clrState(ZTS_STATE_NODE_RUNNING);
- _clearRegisteredCallback();
- // delete params;
- }
- return retval;
-}
-
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_start(
- JNIEnv* env,
- jobject thisObj,
- jstring path,
- jobject callback,
- jint port)
-{
- if (! path) {
- return ZTS_ERR_ARG;
- }
- jclass eventListenerClass = env->GetObjectClass(callback);
- if (eventListenerClass == NULL) {
- DEBUG_ERROR("Couldn't find class for ZeroTierEventListener instance");
- return ZTS_ERR_ARG;
- }
- jmethodID eventListenerCallbackMethod =
- env->GetMethodID(eventListenerClass, "onZeroTierEvent", "(JI)V");
- if (eventListenerCallbackMethod == NULL) {
- DEBUG_ERROR("Couldn't find onZeroTierEvent method");
- return ZTS_ERR_ARG;
- }
- // Reference used for later calls
- objRef = env->NewGlobalRef(callback);
- _userCallbackMethodRef = eventListenerCallbackMethod;
- const char* utf_string = env->GetStringUTFChars(path, NULL);
- if (! utf_string) {
- return ZTS_ERR_GENERAL;
- }
- // using _userCallbackMethodRef
- int retval = zts_start(utf_string, NULL, port);
- env->ReleaseStringUTFChars(path, utf_string);
- return retval;
-}
-#endif
-
-int zts_stop()
-{
- Mutex::Lock _l(serviceLock);
- if (_canPerformServiceOperation()) {
- _clrState(ZTS_STATE_NODE_RUNNING);
- service->terminate();
-#if defined(__WINDOWS__)
- WSACleanup();
-#endif
- return ZTS_ERR_OK;
- }
- return ZTS_ERR_SERVICE;
-}
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop(JNIEnv* env, jobject thisObj)
-{
- zts_stop();
-}
-#endif
-
-// TODO: Make sure this woks with user-provided identities
-int zts_restart()
-{
- serviceLock.lock();
- // Store callback references
-#ifdef ZTS_ENABLE_JAVA
- static jmethodID _tmpUserCallbackMethodRef = _userCallbackMethodRef;
-#endif
- int userProvidedPort = 0;
- std::string userProvidedPath;
- if (service) {
- userProvidedPort = service->_userProvidedPort;
- userProvidedPath = service->_userProvidedPath;
- }
- // Stop the service
- if (_canPerformServiceOperation()) {
- _clrState(ZTS_STATE_NODE_RUNNING);
- service->terminate();
-#if defined(__WINDOWS__)
- WSACleanup();
-#endif
- }
- else {
- serviceLock.unlock();
- return ZTS_ERR_SERVICE;
- }
- // Start again with same parameters as initial call
- serviceLock.unlock();
- while (service) {
- zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL);
- }
- /* Some of the logic in Java_com_zerotier_libzt_ZeroTier_start
- is replicated here */
-#ifdef ZTS_ENABLE_JAVA
- _userCallbackMethodRef = _tmpUserCallbackMethodRef;
- return zts_start(userProvidedPath.c_str(), NULL, userProvidedPort);
-#else
- return ZTS_ERR_OK;
-#endif
-}
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart(JNIEnv* env, jobject thisObj)
-{
- zts_restart();
-}
-#endif
-
-int zts_free()
-{
- if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
- return ZTS_ERR_SERVICE;
- }
- if (_getState(ZTS_STATE_FREE_CALLED)) {
- return ZTS_ERR_SERVICE;
- }
- _setState(ZTS_STATE_FREE_CALLED);
- int err = zts_stop();
- Mutex::Lock _l(serviceLock);
- _lwip_driver_shutdown();
- return err;
-}
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free(JNIEnv* env, jobject thisObj)
-{
- zts_free();
-}
-#endif
-
-#ifdef ZTS_ENABLE_JAVA
-/*
- * Called from Java, saves a static reference to the VM so it can be used
- * later to call a user-specified callback method from C.
- */
-JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init(JNIEnv* env, jobject thisObj)
-{
- jint rs = env->GetJavaVM(&jvm);
- return rs != JNI_OK ? ZTS_ERR_GENERAL : ZTS_ERR_OK;
-}
-#endif
-
-int zts_join(const uint64_t networkId)
-{
- Mutex::Lock _l(serviceLock);
- if (! _canPerformServiceOperation()) {
- return ZTS_ERR_SERVICE;
- }
- else {
- service->join(networkId);
- }
return ZTS_ERR_OK;
}
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT jint JNICALL
-Java_com_zerotier_libzt_ZeroTier_join(JNIEnv* env, jobject thisObj, jlong networkId)
-{
- return zts_join((uint64_t)networkId);
-}
-#endif
-int zts_leave(const uint64_t networkId)
+int zts_init_blacklist_if(const char* prefix, int len)
{
- Mutex::Lock _l(serviceLock);
- if (! _canPerformServiceOperation()) {
- return ZTS_ERR_SERVICE;
- }
- else {
- service->leave(networkId);
- }
+ ACQUIRE_SERVICE_OFFLINE();
+ return zts_service->addInterfacePrefixToBlacklist(prefix, len);
+}
+
+int zts_init_set_planet(const char* planet_data, int len)
+{
+ ACQUIRE_SERVICE_OFFLINE();
+ return zts_service->setPlanet(planet_data, len);
+}
+
+int zts_init_set_port(unsigned short port)
+{
+ ACQUIRE_SERVICE_OFFLINE();
+ zts_service->setPrimaryPort(port);
return ZTS_ERR_OK;
}
-#ifdef ZTS_ENABLE_JAVA
-JNIEXPORT jint JNICALL
-Java_com_zerotier_libzt_ZeroTier_leave(JNIEnv* env, jobject thisObj, jlong networkId)
-{
- return zts_leave((uint64_t)networkId);
-}
-#endif
-int zts_orbit(uint64_t moonWorldId, uint64_t moonSeed)
+int zts_init_allow_peer_cache(int allowed)
{
- Mutex::Lock _l(serviceLock);
- void* tptr = NULL;
- if (! _canPerformServiceOperation()) {
- return ZTS_ERR_SERVICE;
- }
- else {
- service->getNode()->orbit(tptr, moonWorldId, moonSeed);
- }
+ ACQUIRE_SERVICE_OFFLINE();
+ return zts_service->allowPeerCaching(allowed);
+}
+
+int zts_init_allow_net_cache(int allowed)
+{
+ ACQUIRE_SERVICE_OFFLINE();
+ return zts_service->allowNetworkCaching(allowed);
+}
+
+int zts_init_clear()
+{
+ ACQUIRE_SERVICE_OFFLINE();
+ ACQUIRE_EVENTS();
+ _userEventCallback = NULL;
+ zts_service->uninitialize();
return ZTS_ERR_OK;
}
-#ifdef ZTS_ENABLE_JAVA
-#endif
-int zts_deorbit(uint64_t moonWorldId)
+int zts_addr_compute_6plane(
+ const uint64_t net_id,
+ const uint64_t node_id,
+ struct zts_sockaddr_storage* addr)
{
- Mutex::Lock _l(serviceLock);
- void* tptr = NULL;
- if (! _canPerformServiceOperation()) {
- return ZTS_ERR_SERVICE;
- }
- else {
- service->getNode()->deorbit(tptr, moonWorldId);
- }
- return ZTS_ERR_OK;
-}
-#ifdef ZTS_ENABLE_JAVA
-#endif
-
-int zts_get_6plane_addr(
- struct zts_sockaddr_storage* addr,
- const uint64_t networkId,
- const uint64_t nodeId)
-{
- if (! addr || ! networkId || ! nodeId) {
+ if (! addr || ! net_id || ! node_id) {
return ZTS_ERR_ARG;
}
- InetAddress _6planeAddr = InetAddress::makeIpv66plane(networkId, nodeId);
+ InetAddress _6planeAddr = InetAddress::makeIpv66plane(net_id, node_id);
struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
memcpy(in6->sin6_addr.s6_addr, _6planeAddr.rawIpData(), sizeof(struct in6_addr));
return ZTS_ERR_OK;
}
-int zts_get_rfc4193_addr(
- struct zts_sockaddr_storage* addr,
- const uint64_t networkId,
- const uint64_t nodeId)
+int zts_addr_compute_rfc4193(
+ const uint64_t net_id,
+ const uint64_t node_id,
+ struct zts_sockaddr_storage* addr)
{
- if (! addr || ! networkId || ! nodeId) {
+ if (! addr || ! net_id || ! node_id) {
return ZTS_ERR_ARG;
}
- InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(networkId, nodeId);
+ InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(net_id, node_id);
struct sockaddr_in6* in6 = (struct sockaddr_in6*)addr;
memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr));
return ZTS_ERR_OK;
}
-uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange)
+int zts_addr_compute_rfc4193_str(uint64_t net_id, uint64_t node_id, char* dst, int len)
{
- char networkIdStr[INET6_ADDRSTRLEN];
- sprintf(networkIdStr, "ff%04x%04x000000", startPortOfRange, endPortOfRange);
- return strtoull(networkIdStr, NULL, 16);
+ if (! net_id || ! node_id || ! dst || len != ZTS_IP_MAX_STR_LEN) {
+ return ZTS_ERR_ARG;
+ }
+ struct zts_sockaddr_storage ss;
+ int err = ZTS_ERR_OK;
+ if ((err = zts_addr_compute_rfc4193(net_id, node_id, &ss)) != ZTS_ERR_OK) {
+ return err;
+ }
+ struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss;
+ zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), dst, ZTS_IP_MAX_STR_LEN);
+ return ZTS_ERR_OK;
}
-#ifdef __WINDOWS__
- #include
-#elif _POSIX_C_SOURCE >= 199309L
- #include // for nanosleep
-#else
- #include // for usleep
-#endif
-
-void zts_delay_ms(long milliseconds)
+int zts_addr_compute_6plane_str(uint64_t net_id, uint64_t node_id, char* dst, int len)
{
-#ifdef __WINDOWS__
- Sleep(milliseconds);
-#elif _POSIX_C_SOURCE >= 199309L
- struct timespec ts;
- ts.tv_sec = milliseconds / 1000;
- ts.tv_nsec = (milliseconds % 1000) * 1000000;
- nanosleep(&ts, NULL);
+ if (! net_id || ! node_id || ! dst || len != ZTS_IP_MAX_STR_LEN) {
+ return ZTS_ERR_ARG;
+ }
+ struct zts_sockaddr_storage ss;
+ int err = ZTS_ERR_OK;
+ if ((err = zts_addr_compute_6plane(net_id, node_id, &ss)) != ZTS_ERR_OK) {
+ return err;
+ }
+ struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss;
+ zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), dst, ZTS_IP_MAX_STR_LEN);
+ return ZTS_ERR_OK;
+}
+
+uint64_t zts_net_compute_adhoc_id(uint16_t start_port, uint16_t end_port)
+{
+ char net_id_str[ZTS_INET6_ADDRSTRLEN] = { 0 };
+ OSUtils::ztsnprintf(net_id_str, ZTS_INET6_ADDRSTRLEN, "ff%04x%04x000000", start_port, end_port);
+ return strtoull(net_id_str, NULL, 16);
+}
+
+int zts_id_new(char* key, uint16_t* dst_len)
+{
+ if (key == NULL || *dst_len != ZT_IDENTITY_STRING_BUFFER_LENGTH) {
+ return ZTS_ERR_ARG;
+ }
+ Identity id;
+ id.generate();
+ char idtmp[1024] = { 0 };
+ std::string idser = id.toString(true, idtmp);
+ uint16_t key_pair_len = idser.length();
+ if (key_pair_len > *dst_len) {
+ return ZTS_ERR_ARG;
+ }
+ memcpy(key, idser.c_str(), key_pair_len);
+ *dst_len = key_pair_len;
+ return ZTS_ERR_OK;
+}
+
+int zts_id_pair_is_valid(const char* key, int len)
+{
+ if (key == NULL || len != ZT_IDENTITY_STRING_BUFFER_LENGTH) {
+ return false;
+ }
+ Identity id;
+ if ((strlen(key) > 32) && (key[10] == ':')) {
+ if (id.fromString(key)) {
+ return id.locallyValidate();
+ }
+ }
+ return false;
+}
+
+int zts_node_get_id_pair(char* key, uint16_t* dst_len)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ zts_service->getIdentity(key, dst_len);
+ return *dst_len > 0 ? ZTS_ERR_OK : ZTS_ERR_GENERAL;
+}
+
+#if defined(__WINDOWS__)
+DWORD WINAPI cbRun(LPVOID arg)
#else
- usleep(milliseconds * 1000);
+void* cbRun(void* arg)
#endif
+{
+#if defined(__APPLE__)
+ pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME);
+#endif
+ zts_events->run();
+#if ZTS_ENABLE_JAVA
+ _java_detach_from_thread();
+ // pthread_exit(0);
+#endif
+ return NULL;
+}
+
+int zts_addr_is_assigned(uint64_t net_id, int family)
+{
+ ACQUIRE_SERVICE(0);
+ return zts_service->addrIsAssigned(net_id, family);
+}
+
+int zts_addr_get(uint64_t net_id, int family, struct zts_sockaddr_storage* addr)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getFirstAssignedAddr(net_id, family, addr);
+}
+
+int zts_addr_get_str(uint64_t net_id, int family, char* dst, int len)
+{
+ // No service lock required since zts_addr_get will lock it
+ if (net_id == 0) {
+ return ZTS_ERR_ARG;
+ }
+ if (len < ZTS_INET6_ADDRSTRLEN) {
+ return ZTS_ERR_ARG;
+ }
+
+ if (family == ZTS_AF_INET) {
+ struct zts_sockaddr_storage ss;
+ int err = ZTS_ERR_OK;
+ if ((err = zts_addr_get(net_id, family, &ss)) != ZTS_ERR_OK) {
+ return err;
+ }
+ struct zts_sockaddr_in* in4 = (struct zts_sockaddr_in*)&ss;
+ zts_inet_ntop(family, &(in4->sin_addr), dst, ZTS_INET6_ADDRSTRLEN);
+ }
+ if (family == ZTS_AF_INET6) {
+ struct zts_sockaddr_storage ss;
+ int err = ZTS_ERR_OK;
+ if ((err = zts_addr_get(net_id, family, &ss)) != ZTS_ERR_OK) {
+ return err;
+ }
+ struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)&ss;
+ zts_inet_ntop(family, &(in6->sin6_addr), dst, ZTS_INET6_ADDRSTRLEN);
+ }
+ return ZTS_ERR_OK;
+}
+
+int zts_addr_get_all(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getAllAssignedAddr(net_id, addr, count);
+}
+
+int zts_net_join(const uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->join(net_id);
+}
+
+int zts_net_leave(const uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->leave(net_id);
+}
+
+int zts_net_count()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->networkCount();
+}
+
+uint64_t zts_net_get_mac(uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getMACAddress(net_id);
+}
+
+ZTS_API int ZTCALL zts_net_get_mac_str(uint64_t net_id, char* dst, int len)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ if (! dst || len < ZTS_MAC_ADDRSTRLEN) {
+ return ZTS_ERR_ARG;
+ }
+ uint64_t mac = zts_service->getMACAddress(net_id);
+ OSUtils::ztsnprintf(
+ dst,
+ ZTS_MAC_ADDRSTRLEN,
+ "%x:%x:%x:%x:%x:%x",
+ (mac >> 48) & 0xFF,
+ (mac >> 32) & 0xFF,
+ (mac >> 24) & 0xFF,
+ (mac >> 16) & 0xFF,
+ (mac >> 8) & 0xFF,
+ (mac >> 0) & 0xFF);
+ return ZTS_ERR_OK;
+}
+
+int zts_net_get_broadcast(uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNetworkBroadcast(net_id);
+}
+
+int zts_net_get_mtu(uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNetworkMTU(net_id);
+}
+
+int zts_net_get_name(uint64_t net_id, char* dst, int len)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNetworkName(net_id, dst, len);
+}
+
+int zts_net_get_status(uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNetworkStatus(net_id);
+}
+
+int zts_net_get_type(uint64_t net_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNetworkType(net_id);
+}
+
+int zts_route_is_assigned(uint64_t net_id, int family)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->networkHasRoute(net_id, family);
+}
+
+// Start a ZeroTier NodeService background thread
+#if defined(__WINDOWS__)
+DWORD WINAPI _runNodeService(LPVOID arg)
+#else
+void* _runNodeService(void* arg)
+#endif
+{
+#if defined(__APPLE__)
+ pthread_setname_np(ZTS_SERVICE_THREAD_NAME);
+#endif
+ try {
+ for (;;) {
+ switch (zts_service->run()) {
+ case NodeService::ONE_STILL_RUNNING:
+ case NodeService::ONE_NORMAL_TERMINATION:
+ // zts_events->enqueue(ZTS_EVENT_NODE_NORMAL_TERMINATION, NULL);
+ break;
+ case NodeService::ONE_UNRECOVERABLE_ERROR:
+ // DEBUG_ERROR("fatal error: %s",
+ // zts_service->fatalErrorMessage().c_str());
+ // zts_events->enqueue(ZTS_EVENT_NODE_UNRECOVERABLE_ERROR, NULL);
+ break;
+ case NodeService::ONE_IDENTITY_COLLISION: {
+ delete zts_service;
+ zts_service = (NodeService*)0;
+ // zts_events->enqueue(ZTS_EVENT_NODE_IDENTITY_COLLISION, NULL);
+ }
+ continue; // restart!
+ }
+ break; // terminate loop -- normally we don't keep restarting
+ }
+ service_m.lock();
+ zts_events->clrState(ZTS_STATE_NODE_RUNNING);
+ delete zts_service;
+ zts_service = (NodeService*)0;
+ service_m.unlock();
+ // zts_events->enqueue(ZTS_EVENT_NODE_DOWN, NULL);
+ events_m.lock();
+ delete zts_events;
+ zts_events = NULL;
+ events_m.unlock();
+ }
+ catch (...) {
+ // DEBUG_ERROR("unexpected exception starting ZeroTier");
+ }
+ zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL * 2);
+#ifndef __WINDOWS__
+ pthread_exit(0);
+#endif
+ return NULL;
+}
+
+int zts_node_start()
+{
+ ACQUIRE_SERVICE_OFFLINE();
+ // Start TCP/IP stack
+ _lwip_driver_init();
+ // Start callback thread
+ int res = ZTS_ERR_OK;
+ if (zts_events->hasCallback()) {
+#if defined(__WINDOWS__)
+ HANDLE callbackThread = CreateThread(NULL, 0, cbRun, NULL, 0, NULL);
+#else
+ pthread_t cbThread;
+ if ((res = pthread_create(&cbThread, NULL, cbRun, NULL)) != 0) {}
+#endif
+#if defined(__linux__)
+ pthread_setname_np(cbThread, ZTS_EVENT_CALLBACK_THREAD_NAME);
+#endif
+ if (res != ZTS_ERR_OK) {
+ zts_events->clrState(ZTS_STATE_CALLBACKS_RUNNING);
+ zts_events->clrCallback();
+ }
+ zts_events->setState(ZTS_STATE_CALLBACKS_RUNNING);
+ }
+ // Start ZeroTier service
+#if defined(__WINDOWS__)
+ WSAStartup(MAKEWORD(2, 2), &wsaData);
+ HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)NULL, 0, NULL);
+#else
+ pthread_t service_thread;
+ if ((res = pthread_create(&service_thread, NULL, _runNodeService, (void*)NULL)) != 0) {}
+#endif
+#if defined(__linux__)
+ pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME);
+#endif
+ if (res != ZTS_ERR_OK) {
+ zts_events->clrState(ZTS_STATE_NODE_RUNNING);
+ }
+ zts_events->setState(ZTS_STATE_NODE_RUNNING);
+ return ZTS_ERR_OK;
+}
+
+int zts_node_is_online()
+{
+ ACQUIRE_SERVICE(0);
+ return zts_service->nodeIsOnline();
+}
+
+uint64_t zts_node_get_id()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getNodeId();
+}
+
+int zts_node_get_port()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ return zts_service->getPrimaryPort();
+}
+
+int zts_node_stop()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ zts_events->clrState(ZTS_STATE_NODE_RUNNING);
+ zts_service->terminate();
+#if defined(__WINDOWS__)
+ WSACleanup();
+#endif
+ return ZTS_ERR_OK;
+}
+
+int zts_node_restart()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ // Stop
+ zts_events->clrState(ZTS_STATE_NODE_RUNNING);
+ zts_service->terminate();
+#if defined(__WINDOWS__)
+ WSACleanup();
+#endif
+ RELEASE_SERVICE();
+ // Start
+ while (zts_service) {
+ zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL);
+ }
+ return zts_node_start();
+}
+
+int zts_node_free()
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ zts_events->setState(ZTS_STATE_FREE_CALLED);
+ zts_events->clrState(ZTS_STATE_NODE_RUNNING);
+ zts_service->terminate();
+#if defined(__WINDOWS__)
+ WSACleanup();
+#endif
+ _lwip_driver_shutdown();
+ return ZTS_ERR_OK;
+}
+
+int zts_moon_orbit(uint64_t moon_world_id, uint64_t moon_seed)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ zts_service->orbit(NULL, moon_world_id, moon_seed);
+ return ZTS_ERR_OK;
+}
+
+int zts_moon_deorbit(uint64_t moon_world_id)
+{
+ ACQUIRE_SERVICE(ZTS_ERR_SERVICE);
+ zts_service->deorbit(NULL, moon_world_id);
+ return ZTS_ERR_OK;
+}
+
+int zts_stats_get_all(zts_stats_counter_t* dst)
+{
+ if (! dst) {
+ return ZTS_ERR_ARG;
+ }
+ if (! transport_ok()) {
+ return ZTS_ERR_SERVICE;
+ }
+#if LWIP_STATS
+
+ extern struct stats_ lwip_stats;
+
+#define lws lwip_stats
+
+ /* Here we summarize lwIP's statistics for simplicity at the expense of specificity */
+
+ // link
+ dst->link_tx = lws.link.xmit;
+ dst->link_rx = lws.link.recv;
+ dst->link_drop = lws.link.drop;
+ dst->link_err = lws.link.chkerr + lws.link.lenerr + lws.link.memerr + lws.link.rterr
+ + lws.link.proterr + lws.link.opterr + lws.link.err;
+ // etharp
+ dst->etharp_tx = lws.etharp.xmit;
+ dst->etharp_rx = lws.etharp.recv;
+ dst->etharp_drop = lws.etharp.drop;
+ dst->etharp_err = lws.etharp.chkerr + lws.etharp.lenerr + lws.etharp.memerr + lws.etharp.rterr
+ + lws.etharp.proterr + lws.etharp.opterr + lws.etharp.err;
+ // ip4
+ dst->ip4_tx = lws.ip.xmit;
+ dst->ip4_rx = lws.ip.recv;
+ dst->ip4_drop = lws.ip.drop;
+ dst->ip4_err = lws.ip.chkerr + lws.ip.lenerr + lws.ip.memerr + lws.ip.rterr + lws.ip.proterr
+ + lws.ip.opterr + lws.ip.err + lws.ip_frag.chkerr + lws.ip_frag.lenerr
+ + lws.ip_frag.memerr + lws.ip_frag.rterr + lws.ip_frag.proterr
+ + lws.ip_frag.opterr + lws.ip_frag.err;
+ // ip6
+ dst->ip6_tx = lws.ip6.xmit;
+ dst->ip6_rx = lws.ip6.recv;
+ dst->ip6_drop = lws.ip6.drop;
+ dst->ip6_err = lws.ip6.chkerr + lws.ip6.lenerr + lws.ip6.memerr + lws.ip6.rterr
+ + lws.ip6.proterr + lws.ip6.opterr + lws.ip6.err + lws.ip6_frag.chkerr
+ + lws.ip6_frag.lenerr + lws.ip6_frag.memerr + lws.ip6_frag.rterr
+ + lws.ip6_frag.proterr + lws.ip6_frag.opterr + lws.ip6_frag.err;
+
+ // icmp4
+ dst->icmp4_tx = lws.icmp.xmit;
+ dst->icmp4_rx = lws.icmp.recv;
+ dst->icmp4_drop = lws.icmp.drop;
+ dst->icmp4_err = lws.icmp.chkerr + lws.icmp.lenerr + lws.icmp.memerr + lws.icmp.rterr
+ + lws.icmp.proterr + lws.icmp.opterr + lws.icmp.err;
+ // icmp6
+ dst->icmp6_tx = lws.icmp6.xmit;
+ dst->icmp6_rx = lws.icmp6.recv;
+ dst->icmp6_drop = lws.icmp6.drop;
+ dst->icmp6_err = lws.icmp6.chkerr + lws.icmp6.lenerr + lws.icmp6.memerr + lws.icmp6.rterr
+ + lws.icmp6.proterr + lws.icmp6.opterr + lws.icmp6.err;
+ // udp
+ dst->udp_tx = lws.udp.xmit;
+ dst->udp_rx = lws.udp.recv;
+ dst->udp_drop = lws.udp.drop;
+ dst->udp_err = lws.udp.chkerr + lws.udp.lenerr + lws.udp.memerr + lws.udp.rterr
+ + lws.udp.proterr + lws.udp.opterr + lws.udp.err;
+ // tcp
+ dst->tcp_tx = lws.tcp.xmit;
+ dst->tcp_rx = lws.tcp.recv;
+ dst->tcp_drop = lws.tcp.drop;
+ dst->tcp_err = lws.tcp.chkerr + lws.tcp.lenerr + lws.tcp.memerr + lws.tcp.rterr
+ + lws.tcp.proterr + lws.tcp.opterr + lws.tcp.err;
+ // nd6
+ dst->nd6_tx = lws.nd6.xmit;
+ dst->nd6_rx = lws.nd6.recv;
+ dst->nd6_drop = lws.nd6.drop;
+ dst->nd6_err = lws.nd6.chkerr + lws.nd6.lenerr + lws.nd6.memerr + lws.nd6.rterr
+ + lws.nd6.proterr + lws.nd6.opterr + lws.nd6.err;
+
+ // TODO: Add mem and sys stats
+
+ return ZTS_ERR_OK;
+#else
+ return ZTS_ERR_NO_RESULT;
+#endif
+#undef lws
}
#ifdef __cplusplus
}
#endif
+
+} // namespace ZeroTier
diff --git a/src/Debug.hpp b/src/Debug.hpp
index 73e4617..45b90cb 100644
--- a/src/Debug.hpp
+++ b/src/Debug.hpp
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -17,215 +17,85 @@
* Debug macros
*/
-#ifndef LIBZT_DEBUG_HPP
-#define LIBZT_DEBUG_HPP
-
-//////////////////////////////////////////////////////////////////////////////
-// Debugging Macros //
-//////////////////////////////////////////////////////////////////////////////
+#ifndef ZTS_DEBUG_HPP
+#define ZTS_DEBUG_HPP
#if defined(__linux__) || defined(__APPLE__)
- #include
- #include
- #include
+#include
#endif
#include
-#define ZT_MSG_ERROR true // Errors
-#define ZT_MSG_INFO true // Information which is generally useful to any developer
-#define ZT_MSG_TEST true // For use in selftest
-#define ZT_MSG_TRANSFER true // RX/TX specific statements
-
#define ZT_COLOR true
// Debug output colors
#if defined(__APPLE__)
- #include "TargetConditionals.h"
+#include "TargetConditionals.h"
#endif
#if defined(ZT_COLOR) && ! defined(_WIN32) && ! defined(__ANDROID__) \
&& ! defined(TARGET_OS_IPHONE) && ! defined(TARGET_IPHONE_SIMULATOR) \
&& ! defined(__APP_FRAMEWORK__)
- #define ZT_RED "\x1B[31m"
- #define ZT_GRN "\x1B[32m"
- #define ZT_YEL "\x1B[33m"
- #define ZT_BLU "\x1B[34m"
- #define ZT_MAG "\x1B[35m"
- #define ZT_CYN "\x1B[36m"
- #define ZT_WHT "\x1B[37m"
- #define ZT_RESET "\x1B[0m"
+#define ZT_RED "\x1B[31m"
+#define ZT_GRN "\x1B[32m"
+#define ZT_YEL "\x1B[33m"
+#define ZT_BLU "\x1B[34m"
+#define ZT_MAG "\x1B[35m"
+#define ZT_CYN "\x1B[36m"
+#define ZT_WHT "\x1B[37m"
+#define ZT_RESET "\x1B[0m"
#else
- #define ZT_RED
- #define ZT_GRN
- #define ZT_YEL
- #define ZT_BLU
- #define ZT_MAG
- #define ZT_CYN
- #define ZT_WHT
- #define ZT_RESET
+#define ZT_RED
+#define ZT_GRN
+#define ZT_YEL
+#define ZT_BLU
+#define ZT_MAG
+#define ZT_CYN
+#define ZT_WHT
+#define ZT_RESET
#endif
#define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short
-#if defined(__JNI_LIB__)
- #include
-#endif
#if defined(__ANDROID__)
- #include
- #define ZT_LOG_TAG "ZTSDK"
+#include
+#define ZT_LOG_TAG "ZTSDK"
#endif
-#if defined(LIBZT_DEBUG) || defined(LIBZT_TRACE) || defined(__NATIVETEST__)
- //
- #if ZT_MSG_ERROR == true
- #if defined(__ANDROID__)
- #define DEBUG_ERROR(fmt, args...) \
- ((void)__android_log_print( \
- ANDROID_LOG_VERBOSE, \
- ZT_LOG_TAG, \
- "%17s:%5d:%20s: " fmt "\n", \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args))
- #elif defined(_WIN32)
- #define DEBUG_ERROR(fmt, ...) \
- fprintf( \
- stderr, \
- ZT_RED "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- __VA_ARGS__)
- #else
- #define DEBUG_ERROR(fmt, args...) \
- fprintf( \
- stderr, \
- ZT_RED "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args)
- #endif
- #else
- #define DEBUG_ERROR(fmt, args...)
- #endif
-
- //
- #if ZT_MSG_TEST == true
- #if defined(__ANDROID__)
- #define DEBUG_TEST(fmt, args...) \
- ((void)__android_log_print( \
- ANDROID_LOG_VERBOSE, \
- ZT_LOG_TAG, \
- "%17s:%5d:%25s: " fmt "\n", \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args))
- #elif defined(_WIN32)
- #define DEBUG_TEST(fmt, ...) \
- fprintf( \
- stderr, \
- ZT_CYN "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- __VA_ARGS__)
- #else
- #define DEBUG_TEST(fmt, args...) \
- fprintf( \
- stderr, \
- ZT_CYN "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args)
- #endif
- #else
- #define DEBUG_TEST(fmt, args...)
- #endif
-
- //
- #if ZT_MSG_INFO == true
- #if defined(__ANDROID__)
- #define DEBUG_INFO(fmt, args...) \
- ((void)__android_log_print( \
- ANDROID_LOG_VERBOSE, \
- ZT_LOG_TAG, \
- "%17s:%5d:%20s: " fmt "\n", \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args))
- #elif defined(_WIN32)
- #define DEBUG_INFO(fmt, ...) \
- fprintf( \
- stderr, \
- ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- __VA_ARGS__)
- #else
- #define DEBUG_INFO(fmt, args...) \
- fprintf( \
- stderr, \
- ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args)
- #endif
- #else
- #define DEBUG_INFO(fmt, args...)
- #endif
-
- //
- #if ZT_MSG_TRANSFER == true
- #if defined(__ANDROID__)
- #define DEBUG_TRANS(fmt, args...) \
- ((void)__android_log_print( \
- ANDROID_LOG_VERBOSE, \
- ZT_LOG_TAG, \
- "%17s:%5d:%25s: " fmt "\n", \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args))
- #elif defined(_WIN32)
- #define DEBUG_TRANS(fmt, ...) \
- fprintf( \
- stderr, \
- ZT_GRN "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- __VA_ARGS__)
- #else
- #define DEBUG_TRANS(fmt, args...) \
- fprintf( \
- stderr, \
- ZT_GRN "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
- ZT_FILENAME, \
- __LINE__, \
- __FUNCTION__, \
- ##args)
- #endif
- #else
- #define DEBUG_TRANS(fmt, args...)
- #endif
-#else // !LIBZT_DEBUG || !__NATIVE_TEST__
- #if defined(_WIN32)
- #define DEBUG_ERROR(...)
- #define DEBUG_TEST(...)
- #define DEBUG_INFO(...)
- #define DEBUG_TRANS(...)
- #else
- #define DEBUG_ERROR(fmt, args...)
- #define DEBUG_TEST(fmt, args...)
- #define DEBUG_INFO(fmt, args...)
- #define DEBUG_TRANS(fmt, args...)
- #endif
+#if defined(LIBZT_DEBUG)
+#if defined(__ANDROID__)
+#define DEBUG_INFO(fmt, args...) \
+ ((void)__android_log_print( \
+ ANDROID_LOG_VERBOSE, \
+ ZT_LOG_TAG, \
+ "%17s:%5d:%20s: " fmt "\n", \
+ ZT_FILENAME, \
+ __LINE__, \
+ __FUNCTION__, \
+ ##args))
+#elif defined(_WIN32)
+#define DEBUG_INFO(fmt, ...) \
+ fprintf( \
+ stderr, \
+ ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
+ ZT_FILENAME, \
+ __LINE__, \
+ __FUNCTION__, \
+ __VA_ARGS__)
+#else
+#define DEBUG_INFO(fmt, args...) \
+ fprintf( \
+ stderr, \
+ ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
+ ZT_FILENAME, \
+ __LINE__, \
+ __FUNCTION__, \
+ ##args)
+#endif
+#else // !LIBZT_DEBUG
+#if defined(_WIN32)
+#define DEBUG_INFO(...)
+#else
+#define DEBUG_INFO(fmt, args...)
+#endif
#endif
-#endif // _H
\ No newline at end of file
+#endif // _H
diff --git a/src/Events.cpp b/src/Events.cpp
index b7bd132..e41503b 100644
--- a/src/Events.cpp
+++ b/src/Events.cpp
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -14,49 +14,57 @@
/**
* @file
*
- * Callback event processing logic
+ * Callback event creation and distribution to user application
*/
-#include "concurrentqueue.h"
-
-#ifdef ZTS_ENABLE_JAVA
- #include
-#endif
+#include "Events.hpp"
#include "Constants.hpp"
-#include "Debug.hpp"
-#include "Events.hpp"
#include "Node.hpp"
#include "NodeService.hpp"
#include "OSUtils.hpp"
#include "ZeroTierSockets.h"
+#include "concurrentqueue.h"
-#define NODE_EVENT_TYPE(code) code >= ZTS_EVENT_NODE_UP&& code <= ZTS_EVENT_NODE_NORMAL_TERMINATION
-#define NETWORK_EVENT_TYPE(code) \
- code >= ZTS_EVENT_NETWORK_NOT_FOUND&& code <= ZTS_EVENT_NETWORK_UPDATE
-#define STACK_EVENT_TYPE(code) code >= ZTS_EVENT_STACK_UP&& code <= ZTS_EVENT_STACK_DOWN
-#define NETIF_EVENT_TYPE(code) code >= ZTS_EVENT_NETIF_UP&& code <= ZTS_EVENT_NETIF_LINK_DOWN
-#define PEER_EVENT_TYPE(code) code >= ZTS_EVENT_PEER_DIRECT&& code <= ZTS_EVENT_PEER_PATH_DEAD
-#define ROUTE_EVENT_TYPE(code) code >= ZTS_EVENT_ROUTE_ADDED&& code <= ZTS_EVENT_ROUTE_REMOVED
-#define ADDR_EVENT_TYPE(code) code >= ZTS_EVENT_ADDR_ADDED_IP4&& code <= ZTS_EVENT_ADDR_REMOVED_IP6
+#ifdef ZTS_ENABLE_JAVA
+#include
+#endif
#ifdef ZTS_ENABLE_PYTHON
- #include "Python.h"
+#include "Python.h"
PythonDirectorCallbackClass* _userEventCallback = NULL;
-void PythonDirectorCallbackClass::on_zerotier_event(struct zts_callback_msg* msg)
+void PythonDirectorCallbackClass::on_zerotier_event(zts_event_msg_t* msg)
{
}
#endif
+#define ZTS_NODE_EVENT(code) code >= ZTS_EVENT_NODE_UP&& code <= ZTS_EVENT_NODE_FATAL_ERROR
+#define ZTS_NETWORK_EVENT(code) \
+ code >= ZTS_EVENT_NETWORK_NOT_FOUND&& code <= ZTS_EVENT_NETWORK_UPDATE
+#define ZTS_STACK_EVENT(code) code >= ZTS_EVENT_STACK_UP&& code <= ZTS_EVENT_STACK_DOWN
+#define ZTS_NETIF_EVENT(code) code >= ZTS_EVENT_NETIF_UP&& code <= ZTS_EVENT_NETIF_LINK_DOWN
+#define ZTS_PEER_EVENT(code) code >= ZTS_EVENT_PEER_DIRECT&& code <= ZTS_EVENT_PEER_PATH_DEAD
+#define ZTS_ROUTE_EVENT(code) code >= ZTS_EVENT_ROUTE_ADDED&& code <= ZTS_EVENT_ROUTE_REMOVED
+#define ZTS_ADDR_EVENT(code) code >= ZTS_EVENT_ADDR_ADDED_IP4&& code <= ZTS_EVENT_ADDR_REMOVED_IP6
+#define ZTS_STORE_EVENT(code) \
+ code >= ZTS_EVENT_STORE_IDENTITY_SECRET&& code <= ZTS_EVENT_STORE_NETWORK
+
namespace ZeroTier {
-extern NodeService* service;
+extern NodeService* zts_service;
-// Global state variable shared between Socket, Control, Event and NodeService logic.
-uint8_t _serviceStateFlags;
+// Global state variable shared between Socket, Control, Event and
+// NodeService logic.
+volatile uint8_t service_state = 0;
+int last_state_check;
+
+#define RESET_FLAGS() service_state = 0;
+#define SET_FLAGS(f) service_state |= f;
+#define CLR_FLAGS(f) service_state &= ~f;
+#define GET_FLAGS(f) ((service_state & f) > 0)
// Lock to guard access to callback function pointers.
-Mutex _callbackLock;
+Mutex events_m;
#ifdef ZTS_ENABLE_PINVOKE
void (*_userEventCallback)(void*);
@@ -65,45 +73,74 @@ void (*_userEventCallback)(void*);
void (*_userEventCallback)(void*);
#endif
-moodycamel::ConcurrentQueue _callbackMsgQueue;
+moodycamel::ConcurrentQueue _callbackMsgQueue;
-void _enqueueEvent(int16_t eventCode, void* arg)
+void Events::run()
{
- struct zts_callback_msg* msg = new zts_callback_msg();
- msg->eventCode = eventCode;
+ while (getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) {
+ zts_event_msg_t* msg;
+ size_t sz = _callbackMsgQueue.size_approx();
+ for (size_t j = 0; j < sz; j++) {
+ if (_callbackMsgQueue.try_dequeue(msg)) {
+ events_m.lock();
+ sendToUser(msg);
+ events_m.unlock();
+ delete msg;
+ }
+ }
+ zts_util_delay(ZTS_CALLBACK_PROCESSING_INTERVAL);
+ }
+}
- if (NODE_EVENT_TYPE(eventCode)) {
- msg->node = (struct zts_node_details*)arg;
+void Events::enqueue(int16_t event_code, const void* arg, int len)
+{
+ if (! _enabled) {
+ return;
}
- if (NETWORK_EVENT_TYPE(eventCode)) {
- msg->network = (struct zts_network_details*)arg;
+ zts_event_msg_t* msg = new zts_event_msg_t();
+ msg->event_code = event_code;
+
+ if (ZTS_NODE_EVENT(event_code)) {
+ msg->node = (zts_node_info_t*)arg;
+ msg->len = sizeof(zts_node_info_t);
}
- if (STACK_EVENT_TYPE(eventCode)) {
+ if (ZTS_NETWORK_EVENT(event_code)) {
+ msg->network = (zts_net_info_t*)arg;
+ msg->len = sizeof(zts_net_info_t);
+ }
+ if (ZTS_STACK_EVENT(event_code)) {
/* nothing to convey to user */
}
- if (NETIF_EVENT_TYPE(eventCode)) {
- msg->netif = (struct zts_netif_details*)arg;
+ if (ZTS_NETIF_EVENT(event_code)) {
+ msg->netif = (zts_netif_info_t*)arg;
+ msg->len = sizeof(zts_netif_info_t);
}
- if (ROUTE_EVENT_TYPE(eventCode)) {
- msg->route = (struct zts_virtual_network_route*)arg;
+ if (ZTS_ROUTE_EVENT(event_code)) {
+ msg->route = (zts_route_info_t*)arg;
+ msg->len = sizeof(zts_route_info_t);
}
- if (PEER_EVENT_TYPE(eventCode)) {
- msg->peer = (struct zts_peer_details*)arg;
+ if (ZTS_PEER_EVENT(event_code)) {
+ msg->peer = (zts_peer_info_t*)arg;
+ msg->len = sizeof(zts_peer_info_t);
}
- if (ADDR_EVENT_TYPE(eventCode)) {
- msg->addr = (struct zts_addr_details*)arg;
+ if (ZTS_ADDR_EVENT(event_code)) {
+ msg->addr = (zts_addr_info_t*)arg;
+ msg->len = sizeof(zts_addr_info_t);
+ }
+ if (ZTS_STORE_EVENT(event_code)) {
+ msg->cache = (void*)arg;
+ msg->len = len;
}
-
if (msg && _callbackMsgQueue.size_approx() > 1024) {
// Rate-limit number of events
- _freeEvent(msg);
+ destroy(msg);
}
else {
_callbackMsgQueue.enqueue(msg);
}
}
-void _freeEvent(struct zts_callback_msg* msg)
+void Events::destroy(zts_event_msg_t* msg)
{
if (! msg) {
return;
@@ -128,9 +165,9 @@ void _freeEvent(struct zts_callback_msg* msg)
}
}
-void _passDequeuedEventToUser(struct zts_callback_msg* msg)
+void Events::sendToUser(zts_event_msg_t* msg)
{
- bool bShouldStopCallbackThread = (msg->eventCode == ZTS_EVENT_STACK_DOWN);
+ bool bShouldStopCallbackThread = (msg->event_code == ZTS_EVENT_STACK_DOWN);
#ifdef ZTS_ENABLE_PYTHON
PyGILState_STATE state = PyGILState_Ensure();
_userEventCallback->on_zerotier_event(msg);
@@ -139,24 +176,24 @@ void _passDequeuedEventToUser(struct zts_callback_msg* msg)
#ifdef ZTS_ENABLE_JAVA
if (_userCallbackMethodRef) {
JNIEnv* env;
- #if defined(__ANDROID__)
+#if defined(__ANDROID__)
jint rs = jvm->AttachCurrentThread(&env, NULL);
- #else
+#else
jint rs = jvm->AttachCurrentThread((void**)&env, NULL);
- #endif
+#endif
assert(rs == JNI_OK);
uint64_t arg = 0;
uint64_t id = 0;
- if (NODE_EVENT_TYPE(msg->eventCode)) {
+ if (ZTS_NODE_EVENT(msg->event_code)) {
id = msg->node ? msg->node->address : 0;
}
- if (NETWORK_EVENT_TYPE(msg->eventCode)) {
+ if (ZTS_NETWORK_EVENT(msg->event_code)) {
id = msg->network ? msg->network->nwid : 0;
}
- if (PEER_EVENT_TYPE(msg->eventCode)) {
+ if (ZTS_PEER_EVENT(msg->event_code)) {
id = msg->peer ? msg->peer->address : 0;
}
- env->CallVoidMethod(objRef, _userCallbackMethodRef, id, msg->eventCode);
+ env->CallVoidMethod(objRef, _userCallbackMethodRef, id, msg->event_code);
}
#endif // ZTS_ENABLE_JAVA
#ifdef ZTS_ENABLE_PINVOKE
@@ -169,53 +206,47 @@ void _passDequeuedEventToUser(struct zts_callback_msg* msg)
_userEventCallback(msg);
}
#endif
- _freeEvent(msg);
+ destroy(msg);
if (bShouldStopCallbackThread) {
/* Ensure last possible callback ZTS_EVENT_STACK_DOWN is
delivered before callback thread is finally stopped. */
- _clrState(ZTS_STATE_CALLBACKS_RUNNING);
+ clrState(ZTS_STATE_CALLBACKS_RUNNING);
}
}
-bool _isCallbackRegistered()
+bool Events::hasCallback()
{
- _callbackLock.lock();
+ events_m.lock();
bool retval = false;
#ifdef ZTS_ENABLE_JAVA
retval = (jvm && objRef && _userCallbackMethodRef);
#else
retval = _userEventCallback;
#endif
- _callbackLock.unlock();
+ events_m.unlock();
return retval;
}
-void _clearRegisteredCallback()
+void Events::clrCallback()
{
- _callbackLock.lock();
+ events_m.lock();
#ifdef ZTS_ENABLE_JAVA
objRef = NULL;
_userCallbackMethodRef = NULL;
#else
_userEventCallback = NULL;
#endif
- _callbackLock.unlock();
+ events_m.unlock();
}
-int _canPerformServiceOperation()
+int Events::canPerformServiceOperation()
{
- return service && service->isRunning() && service->getNode() && service->getNode()->online()
- && ! _getState(ZTS_STATE_FREE_CALLED);
+ return zts_service && zts_service->isRunning() && ! getState(ZTS_STATE_FREE_CALLED);
}
-#define RESET_FLAGS() _serviceStateFlags = 0;
-#define SET_FLAGS(f) _serviceStateFlags |= f;
-#define CLR_FLAGS(f) _serviceStateFlags &= ~f;
-#define GET_FLAGS(f) ((_serviceStateFlags & f) > 0)
-
-void _setState(uint8_t newFlags)
+void Events::setState(uint8_t newFlags)
{
- if ((newFlags ^ _serviceStateFlags) & ZTS_STATE_NET_SERVICE_RUNNING) {
+ if ((newFlags ^ service_state) & ZTS_STATE_NET_SERVICE_RUNNING) {
return; // No effect. Not allowed to set this flag manually
}
SET_FLAGS(newFlags);
@@ -228,7 +259,7 @@ void _setState(uint8_t newFlags)
}
}
-void _clrState(uint8_t newFlags)
+void Events::clrState(uint8_t newFlags)
{
if (newFlags & ZTS_STATE_NET_SERVICE_RUNNING) {
return; // No effect. Not allowed to set this flag manually
@@ -243,39 +274,19 @@ void _clrState(uint8_t newFlags)
}
}
-bool _getState(uint8_t testFlags)
+bool Events::getState(uint8_t testFlags)
{
- return testFlags & _serviceStateFlags;
+ return testFlags & service_state;
}
-#if defined(__WINDOWS__)
-DWORD WINAPI _runCallbacks(LPVOID thread_id)
-#else
-void* _runCallbacks(void* thread_id)
-#endif
+void Events::enable()
{
-#if defined(__APPLE__)
- pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME);
-#endif
- while (_getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) {
- struct zts_callback_msg* msg;
- size_t sz = _callbackMsgQueue.size_approx();
- for (size_t j = 0; j < sz; j++) {
- if (_callbackMsgQueue.try_dequeue(msg)) {
- _callbackLock.lock();
- _passDequeuedEventToUser(msg);
- _callbackLock.unlock();
- delete msg;
- }
- }
- zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL);
- }
-#if ZTS_ENABLE_JAVA
- JNIEnv* env;
- jint rs = jvm->DetachCurrentThread();
- pthread_exit(0);
-#endif
- return NULL;
+ _enabled = true;
+}
+
+void Events::disable()
+{
+ _enabled = false;
}
} // namespace ZeroTier
diff --git a/src/Events.hpp b/src/Events.hpp
index ac6f18f..6c272dc 100644
--- a/src/Events.hpp
+++ b/src/Events.hpp
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -14,98 +14,153 @@
/**
* @file
*
- * Header for callback event processing logic
+ * Callback event creation and distribution to user application
*/
-#ifndef ZT_EVENTS_HPP
-#define ZT_EVENTS_HPP
+#ifndef ZTS_USER_EVENTS_HPP
+#define ZTS_USER_EVENTS_HPP
-#include "Constants.hpp"
#include "ZeroTierSockets.h"
#include
#ifdef __WINDOWS__
- #include
+#include
#endif
-#ifdef ZTS_ENABLE_JAVA
- #include
-#endif
+/* Macro substitutions to standardize state checking of service, node, callbacks, and TCP/IP
+ * stack. These are used only for control functions that are called at a low frequency. All higher
+ * frequency socket calls use a unguarded state flags */
+
+// Lock service and check that it is running
+#define ACQUIRE_SERVICE(x) \
+ Mutex::Lock _ls(service_m); \
+ if (! zts_service || ! zts_service->isRunning()) { \
+ return x; \
+ }
+// Lock service and check that it is not currently running
+#define ACQUIRE_SERVICE_OFFLINE() \
+ Mutex::Lock _ls(service_m); \
+ if (zts_service && zts_service->isRunning()) { \
+ return ZTS_ERR_SERVICE; \
+ } \
+ if (! zts_service) { \
+ init_subsystems(); \
+ }
+// Unlock service
+#define RELEASE_SERVICE() service_m.unlock();
+// Lock service, ensure node is online
+#define ACQUIRE_ONLINE_NODE() \
+ ACQUIRE_SERVICE() if (! zts_service->nodeIsOnline()) \
+ { \
+ return ZTS_ERR_SERVICE; \
+ }
+// Lock event callback
+#define ACQUIRE_EVENTS() \
+ Mutex::Lock _lc(events_m); \
+ if (! zts_events) { \
+ return ZTS_ERR_SERVICE; \
+ }
+
namespace ZeroTier {
+#ifdef ZTS_ENABLE_JAVA
+#include
+// References to JNI objects and VM kept for future callbacks
+extern JavaVM* jvm;
+extern jobject objRef;
+extern jmethodID _userCallbackMethodRef;
+#endif
+
#define ZTS_STATE_NODE_RUNNING 0x01
#define ZTS_STATE_STACK_RUNNING 0x02
#define ZTS_STATE_NET_SERVICE_RUNNING 0x04
#define ZTS_STATE_CALLBACKS_RUNNING 0x08
#define ZTS_STATE_FREE_CALLED 0x10
-#ifdef ZTS_ENABLE_JAVA
-// References to JNI objects and VM kept for future callbacks
-extern JavaVM* jvm;
-extern jobject objRef;
-extern jmethodID _userCallbackMethodRef;
-#endif
+extern volatile uint8_t service_state;
+extern int last_state_check;
+
+inline int transport_ok()
+{
+ last_state_check = service_state & ZTS_STATE_NET_SERVICE_RUNNING;
+ return last_state_check;
+}
/**
* How often callback messages are assembled and/or sent
*/
#define ZTS_CALLBACK_PROCESSING_INTERVAL 25
-/**
- * Enqueue an event to be sent to the user application
- */
-void _enqueueEvent(int16_t eventCode, void* arg);
+class Events {
+ bool _enabled;
-/**
- * Send callback message to user application
- */
-void _passDequeuedEventToUser(struct ::zts_callback_msg* msg);
+ public:
+ Events() : _enabled(true)
+ {
+ }
-/**
- * Free memory occupied by callback structures
- */
-void _freeEvent(struct ::zts_callback_msg* msg);
+ /**
+ * Perform one iteration of callback processing
+ */
+ void run();
-/**
- * Return whether a callback method has been set
- */
-bool _isCallbackRegistered();
+ /**
+ * Enable callback event processing
+ */
+ void enable();
-/**
- * Clear pointer reference to user-provided callback function
- */
-void _clearRegisteredCallback();
+ /**
+ * Disable callback event processing
+ */
+ void disable();
-/**
- * Return whether service operation can be performed at this time
- */
-int _canPerformServiceOperation();
+ /**
+ * Enqueue an event to be sent to the user application
+ */
+ void enqueue(int16_t event_code, const void* arg, int len = 0);
-/**
- * Set internal state flags
- */
-void _setState(uint8_t newFlags);
+ /**
+ * Send callback message to user application
+ */
+ void sendToUser(zts_event_msg_t* msg);
-/**
- * Clear internal state flags
- */
-void _clrState(uint8_t newFlags);
+ /**
+ * Free memory occupied by callback structures
+ */
+ void destroy(zts_event_msg_t* msg);
-/**
- * Get internal state flags
- */
-bool _getState(uint8_t testFlags);
+ /**
+ * Return whether a callback method has been set
+ */
+ bool hasCallback();
-#ifdef __WINDOWS__
-DWORD WINAPI _runCallbacks(LPVOID thread_id);
-#else
-/**
- * Event callback thread
- */
-void* _runCallbacks(void* thread_id);
-#endif
+ /**
+ * Clear pointer reference to user-provided callback function
+ */
+ void clrCallback();
+
+ /**
+ * Return whether service operation can be performed at this time
+ */
+ int canPerformServiceOperation();
+
+ /**
+ * Set internal state flags
+ */
+ void setState(uint8_t newFlags);
+
+ /**
+ * Clear internal state flags
+ */
+ void clrState(uint8_t newFlags);
+
+ /**
+ * Get internal state flags
+ */
+ bool getState(uint8_t testFlags);
+};
} // namespace ZeroTier
-#endif // _H
\ No newline at end of file
+#endif // _H
diff --git a/src/NodeService.cpp b/src/NodeService.cpp
index debcd76..700d428 100644
--- a/src/NodeService.cpp
+++ b/src/NodeService.cpp
@@ -4,7 +4,7 @@
* 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: 2025-01-01
+ * 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.
@@ -14,1704 +14,1259 @@
/**
* @file
*
- * ZeroTier Node Service (a distant relative of OneService)
+ * ZeroTier Node Service
*/
#include "NodeService.hpp"
#include "../version.h"
-#include "Binder.hpp"
#include "BlockingQueue.hpp"
#include "Constants.hpp"
-#include "Debug.hpp"
-#include "Events.hpp"
#include "InetAddress.hpp"
#include "MAC.hpp"
-#include "ManagedRoute.hpp"
#include "Node.hpp"
#include "OSUtils.hpp"
#include "Phy.hpp"
-#include "PortMapper.hpp"
-#include "Thread.hpp"
+#include "Utilities.hpp"
#include "Utils.hpp"
-#include "VirtualTap.hpp"
#include "ZeroTierSockets.h"
#include
-#include
#if defined(__WINDOWS__)
- // WSADATA wsaData;
- #include
- #include
- #include
- #include
- #include
- #define stat _stat
+#include
+#include
+#include
+#include
+#include
+#define stat _stat
#endif
-#ifdef ZTS_ENABLE_JAVA
- #include
-#endif
-
-// Custom errno-like reporting variable
-int zts_errno;
-
namespace ZeroTier {
-uint8_t allowNetworkCaching;
-uint8_t allowPeerCaching;
-uint8_t allowLocalConf;
-uint8_t disableLocalStorage; // Off by default
+NodeService::NodeService()
+ : _phy(this, false, true)
+ , _node((Node*)0)
+ , _primaryPort()
+ , _udpPortPickerCounter(0)
+ , _lastDirectReceiveFromGlobal(0)
+ , _lastRestart(0)
+ , _nextBackgroundTaskDeadline(0)
+ , _run(false)
+ , _termReason(ONE_STILL_RUNNING)
+ , _portMappingEnabled(true)
+#ifdef ZT_USE_MINIUPNPC
+ , _portMapper((PortMapper*)0)
+#endif
+ , _allowNetworkCaching(true)
+ , _allowPeerCaching(true)
+ , _homePath("")
+{
+}
-typedef VirtualTap EthernetTap;
+NodeService::~NodeService()
+{
+ _binder.closeAll(_phy);
+#ifdef ZT_USE_MINIUPNPC
+ delete _portMapper;
+#endif
+}
-class NodeServiceImpl;
+NodeService::ReasonForTermination NodeService::run()
+{
+ _run = true;
+ try {
+ // Create home path (if necessary)
+ // By default, _homePath is empty and nothing is written to storage
+ if (_homePath.length() > 0) {
+ std::vector hpsp(
+ OSUtils::split(_homePath.c_str(), ZT_PATH_SEPARATOR_S, "", ""));
+ std::string ptmp;
+ if (_homePath[0] == ZT_PATH_SEPARATOR) {
+ ptmp.push_back(ZT_PATH_SEPARATOR);
+ }
+ for (std::vector::iterator pi(hpsp.begin()); pi != hpsp.end(); ++pi) {
+ if (ptmp.length() > 0) {
+ ptmp.push_back(ZT_PATH_SEPARATOR);
+ }
+ ptmp.append(*pi);
+ if ((*pi != ".") && (*pi != "..")) {
+ if (OSUtils::mkdir(ptmp) == false) {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = "home path could not be created";
+ return _termReason;
+ }
+ }
+ }
+ }
-static int SnodeVirtualNetworkConfigFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t nwid,
- void** nuptr,
- enum ZT_VirtualNetworkConfigOperation op,
- const ZT_VirtualNetworkConfig* nwconf);
-static void SnodeEventCallback(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- enum ZT_Event event,
- const void* metaData);
-static void SnodeStatePutFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- enum ZT_StateObjectType type,
- const uint64_t id[2],
- const void* data,
- int len);
-static int SnodeStateGetFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- enum ZT_StateObjectType type,
- const uint64_t id[2],
- void* data,
- unsigned int maxlen);
-static int SnodeWirePacketSendFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- int64_t localSocket,
- const struct sockaddr_storage* addr,
- const void* data,
- unsigned int len,
- unsigned int ttl);
-static void SnodeVirtualNetworkFrameFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t nwid,
- void** nuptr,
- uint64_t sourceMac,
- uint64_t destMac,
- unsigned int etherType,
- unsigned int vlanId,
- const void* data,
- unsigned int len);
-static int SnodePathCheckFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t ztaddr,
- int64_t localSocket,
- const struct sockaddr_storage* remoteAddr);
-static int SnodePathLookupFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t ztaddr,
- int family,
- struct sockaddr_storage* result);
-static void StapFrameHandler(
- 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);
-
-struct NodeServiceIncomingPacket {
- uint64_t now;
- int64_t sock;
- struct sockaddr_storage from;
- unsigned int size;
- uint8_t data[ZT_MAX_MTU];
-};
-
-class NodeServiceImpl : public NodeService {
- public:
- // begin member variables --------------------------------------------------
-
- const std::string _homePath;
- const std::string _networksPath;
- const std::string _moonsPath;
-
- Phy _phy;
- Node* _node;
- bool _updateAutoApply;
- unsigned int _multipathMode = 0;
- unsigned int _primaryPort = 0;
- unsigned int _secondaryPort = 0;
- unsigned int _tertiaryPort = 0;
- volatile unsigned int _udpPortPickerCounter;
-
- //
- std::map peerCache;
-
- //
- unsigned long _incomingPacketConcurrency;
- std::vector _incomingPacketMemoryPool;
- BlockingQueue _incomingPacketQueue;
- std::vector _incomingPacketThreads;
- Mutex _incomingPacketMemoryPoolLock, _incomingPacketThreadsLock;
-
- // Local configuration and memo-ized information from it
- Hashtable > _v4Hints;
- Hashtable > _v6Hints;
- Hashtable > _v4Blacklists;
- Hashtable > _v6Blacklists;
- std::vector _globalV4Blacklist;
- std::vector _globalV6Blacklist;
- std::vector _allowManagementFrom;
- std::vector _interfacePrefixBlacklist;
- Mutex _localConfig_m;
-
- std::vector explicitBind;
-
- /*
- * To attempt to handle NAT/gateway craziness we use three local UDP ports:
- *
- * [0] is the normal/default port, usually 9993
- * [1] is a port derived from our ZeroTier address
- * [2] is a port computed from the normal/default for use with uPnP/NAT-PMP mappings
- *
- * [2] exists because on some gateways trying to do regular NAT-t interferes
- * destructively with uPnP port mapping behavior in very weird buggy ways.
- * It's only used if uPnP/NAT-PMP is enabled in this build.
- */
- unsigned int _ports[3];
- Binder _binder;
-
- // Time we last received a packet from a global address
- uint64_t _lastDirectReceiveFromGlobal;
-
- // Last potential sleep/wake event
- uint64_t _lastRestart;
-
- // Deadline for the next background task service function
- volatile int64_t _nextBackgroundTaskDeadline;
-
- // Configured networks
- struct NetworkState {
- NetworkState() : tap((EthernetTap*)0)
+ // Set callbacks for ZT Node
{
- // Real defaults are in network 'up' code in network event handler
- settings.allowManaged = true;
- settings.allowGlobal = false;
- settings.allowDefault = false;
+ struct ZT_Node_Callbacks cb;
+ cb.version = 0;
+ cb.stateGetFunction = SnodeStateGetFunction;
+ cb.statePutFunction = SnodeStatePutFunction;
+ cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
+ cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
+ cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
+ cb.eventCallback = SnodeEventCallback;
+ cb.pathCheckFunction = SnodePathCheckFunction;
+ cb.pathLookupFunction = SnodePathLookupFunction;
+ _node = new Node(this, (void*)0, &cb, OSUtils::now());
}
- EthernetTap* tap;
- ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
- std::vector managedIps;
- std::list > managedRoutes;
- NetworkSettings settings;
- };
- std::map _nets;
- Mutex _nets_m;
-
- // Termination status information
- ReasonForTermination _termReason;
- std::string _fatalErrorMessage;
- Mutex _termReason_m;
-
- // uPnP/NAT-PMP port mapper if enabled
- bool _portMappingEnabled; // local.conf settings
-#ifdef ZT_USE_MINIUPNPC
- PortMapper* _portMapper;
-#endif
-
- // Set to false to force service to stop
- volatile bool _run;
- Mutex _run_m;
-
- // end member variables ----------------------------------------------------
-
- NodeServiceImpl(const char* hp, unsigned int port)
- : _homePath((hp) ? hp : ".")
- , _phy(this, false, true)
- , _node((Node*)0)
- , _updateAutoApply(false)
- , _primaryPort(port)
- , _udpPortPickerCounter(0)
- , _lastDirectReceiveFromGlobal(0)
- , _lastRestart(0)
- , _nextBackgroundTaskDeadline(0)
- , _termReason(ONE_STILL_RUNNING)
- , _portMappingEnabled(true)
-#ifdef ZT_USE_MINIUPNPC
- , _portMapper((PortMapper*)0)
-#endif
- , _run(true)
- {
- _ports[0] = 0;
- _ports[1] = 0;
- _ports[2] = 0;
- }
-
- virtual ~NodeServiceImpl()
- {
- _incomingPacketQueue.stop();
- _incomingPacketThreadsLock.lock();
- for (auto t = _incomingPacketThreads.begin(); t != _incomingPacketThreads.end(); ++t)
- t->join();
- _incomingPacketThreadsLock.unlock();
-
- _binder.closeAll(_phy);
-
- _incomingPacketMemoryPoolLock.lock();
- while (! _incomingPacketMemoryPool.empty()) {
- delete _incomingPacketMemoryPool.back();
- _incomingPacketMemoryPool.pop_back();
- }
- _incomingPacketMemoryPoolLock.unlock();
-
-#ifdef ZT_USE_MINIUPNPC
- delete _portMapper;
-#endif
- }
-
- virtual ReasonForTermination run() override
- {
- try {
- {
- struct ZT_Node_Callbacks cb;
- cb.version = 0;
- cb.stateGetFunction = SnodeStateGetFunction;
- cb.statePutFunction = SnodeStatePutFunction;
- cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
- cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
- cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
- cb.eventCallback = SnodeEventCallback;
- cb.pathCheckFunction = SnodePathCheckFunction;
- cb.pathLookupFunction = SnodePathLookupFunction;
- _node = new Node(this, (void*)0, &cb, OSUtils::now());
+ // Make sure we can use the primary port, and hunt for one if
+ // configured to do so
+ const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
+ for (int k = 0; k < portTrials; ++k) {
+ if (_primaryPort == 0) {
+ unsigned int randp = 0;
+ Utils::getSecureRandom(&randp, sizeof(randp));
+ _primaryPort = 20000 + (randp % 45500);
}
-
- // Make sure we can use the primary port, and hunt for one if configured to do so
- const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
- for (int k = 0; k < portTrials; ++k) {
- if (_primaryPort == 0) {
- unsigned int randp = 0;
- Utils::getSecureRandom(&randp, sizeof(randp));
- _primaryPort = 20000 + (randp % 45500);
- }
- if (_trialBind(_primaryPort)) {
- _ports[0] = _primaryPort;
- }
- else {
- _primaryPort = 0;
- }
- }
- if (_ports[0] == 0) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "cannot bind to local control interface port";
- return _termReason;
- }
-
- // Attempt to bind to a secondary port chosen from our ZeroTier address.
- // This exists because there are buggy NATs out there that fail if more
- // than one device behind the same NAT tries to use the same internal
- // private address port number. Buggy NATs are a running theme.
- _ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500)
- : _secondaryPort;
- for (int i = 0;; ++i) {
- if (i > 1000) {
- _ports[1] = 0;
- break;
- }
- else if (++_ports[1] >= 65536) {
- _ports[1] = 20000;
- }
- if (_trialBind(_ports[1]))
- break;
- }
-
-#ifdef ZT_USE_MINIUPNPC
- if (_portMappingEnabled) {
- // If we're running uPnP/NAT-PMP, bind a *third* port for that. We can't
- // use the other two ports for that because some NATs do really funky
- // stuff with ports that are explicitly mapped that breaks things.
- if (_ports[1]) {
- _ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort;
- for (int i = 0;; ++i) {
- if (i > 1000) {
- _ports[2] = 0;
- break;
- }
- else if (++_ports[2] >= 65536) {
- _ports[2] = 20000;
- }
- if (_trialBind(_ports[2]))
- break;
- }
- if (_ports[2]) {
- char uniqueName[64];
- OSUtils::ztsnprintf(
- uniqueName,
- sizeof(uniqueName),
- "ZeroTier/%.10llx@%u",
- _node->address(),
- _ports[2]);
- _portMapper = new PortMapper(_ports[2], uniqueName);
- }
- }
- }
-#endif
- // Join existing networks in networks.d
- if (allowNetworkCaching) {
- std::vector networksDotD(
- OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str()));
- for (std::vector::iterator f(networksDotD.begin());
- f != networksDotD.end();
- ++f) {
- std::size_t dot = f->find_last_of('.');
- if ((dot == 16) && (f->substr(16) == ".conf"))
- _node->join(
- Utils::hexStrToU64(f->substr(0, dot).c_str()),
- (void*)0,
- (void*)0);
- }
- }
- // Main I/O loop
- _nextBackgroundTaskDeadline = 0;
- int64_t clockShouldBe = OSUtils::now();
- _lastRestart = clockShouldBe;
- int64_t lastTapMulticastGroupCheck = 0;
- int64_t lastBindRefresh = 0;
- int64_t lastMultipathModeUpdate = 0;
- int64_t lastCleanedPeersDb = 0;
- int64_t lastLocalInterfaceAddressCheck =
- (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL)
- + 15000; // do this in 15s to give portmapper time to configure and other things
- // time to settle
- for (;;) {
- _run_m.lock();
- if (! _run) {
- _run_m.unlock();
- _termReason_m.lock();
- _termReason = ONE_NORMAL_TERMINATION;
- _termReason_m.unlock();
- break;
- }
- else {
- _run_m.unlock();
- }
-
- const int64_t now = OSUtils::now();
-
- // Attempt to detect sleep/wake events by detecting delay overruns
- bool restarted = false;
- if ((now > clockShouldBe) && ((now - clockShouldBe) > 10000)) {
- _lastRestart = now;
- restarted = true;
- }
-
- // Refresh bindings in case device's interfaces have changed, and also sync routes
- // to update any shadow routes (e.g. shadow default)
- if (((now - lastBindRefresh)
- >= (_multipathMode ? ZT_BINDER_REFRESH_PERIOD / 8 : ZT_BINDER_REFRESH_PERIOD))
- || (restarted)) {
- lastBindRefresh = now;
- unsigned int p[3];
- unsigned int pc = 0;
- for (int i = 0; i < 3; ++i) {
- if (_ports[i])
- p[pc++] = _ports[i];
- }
- _binder.refresh(_phy, p, pc, explicitBind, *this);
- }
- // Update multipath mode (if needed)
- if (((now - lastMultipathModeUpdate) >= ZT_BINDER_REFRESH_PERIOD / 8)
- || (restarted)) {
- lastMultipathModeUpdate = now;
- _node->setMultipathMode(_multipathMode);
- }
-
- //
- generateEventMsgs();
-
- // Run background task processor in core if it's time to do so
- int64_t dl = _nextBackgroundTaskDeadline;
- if (dl <= now) {
- _node->processBackgroundTasks((void*)0, now, &_nextBackgroundTaskDeadline);
- dl = _nextBackgroundTaskDeadline;
- }
-
- // Sync multicast group memberships
- if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
- lastTapMulticastGroupCheck = now;
- std::vector, std::vector > > >
- mgChanges;
- {
- Mutex::Lock _l(_nets_m);
- mgChanges.reserve(_nets.size() + 1);
- for (std::map::const_iterator n(_nets.begin());
- n != _nets.end();
- ++n) {
- if (n->second.tap) {
- mgChanges.push_back(std::pair<
- uint64_t,
- std::pair<
- std::vector,
- std::vector > >(
- n->first,
- std::pair<
- std::vector,
- std::vector >()));
- n->second.tap->scanMulticastGroups(
- mgChanges.back().second.first,
- mgChanges.back().second.second);
- }
- }
- }
- for (std::vector,
- std::vector > > >::iterator c(mgChanges.begin());
- c != mgChanges.end();
- ++c) {
- auto mgpair = c->second;
- for (std::vector::iterator m(mgpair.first.begin());
- m != mgpair.first.end();
- ++m)
- _node->multicastSubscribe(
- (void*)0,
- c->first,
- m->mac().toInt(),
- m->adi());
- for (std::vector::iterator m(mgpair.second.begin());
- m != mgpair.second.end();
- ++m)
- _node->multicastUnsubscribe(c->first, m->mac().toInt(), m->adi());
- }
- }
-
- // Sync information about physical network interfaces
- if ((now - lastLocalInterfaceAddressCheck)
- >= (_multipathMode ? ZT_LOCAL_INTERFACE_CHECK_INTERVAL / 8
- : ZT_LOCAL_INTERFACE_CHECK_INTERVAL)) {
- lastLocalInterfaceAddressCheck = now;
-
- _node->clearLocalInterfaceAddresses();
-
-#ifdef ZT_USE_MINIUPNPC
- if (_portMapper) {
- std::vector mappedAddresses(_portMapper->get());
- for (std::vector::const_iterator ext(mappedAddresses.begin());
- ext != mappedAddresses.end();
- ++ext)
- _node->addLocalInterfaceAddress(
- reinterpret_cast(&(*ext)));
- }
-#endif
-
- std::vector boundAddrs(_binder.allBoundLocalInterfaceAddresses());
- for (std::vector::const_iterator i(boundAddrs.begin());
- i != boundAddrs.end();
- ++i)
- _node->addLocalInterfaceAddress(
- reinterpret_cast(&(*i)));
- }
-
- // Clean peers.d periodically
- if ((now - lastCleanedPeersDb) >= 3600000) {
- lastCleanedPeersDb = now;
- OSUtils::cleanDirectory(
- (_homePath + ZT_PATH_SEPARATOR_S "peers.d").c_str(),
- now - 2592000000LL); // delete older than 30 days
- }
-
- const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
- clockShouldBe = now + (uint64_t)delay;
- _phy.poll(delay);
- }
- }
- catch (std::exception& e) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = std::string("unexpected exception in main thread: ") + e.what();
- }
- catch (...) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = "unexpected exception in main thread: unknown exception";
- }
-
- {
- Mutex::Lock _l(_nets_m);
- for (std::map::iterator n(_nets.begin()); n != _nets.end(); ++n)
- delete n->second.tap;
- _nets.clear();
- }
-
- delete _node;
- _node = (Node*)0;
-
- return _termReason;
- }
-
- virtual ReasonForTermination reasonForTermination() const override
- {
- Mutex::Lock _l(_termReason_m);
- return _termReason;
- }
-
- virtual std::string fatalErrorMessage() const override
- {
- Mutex::Lock _l(_termReason_m);
- return _fatalErrorMessage;
- }
-
- virtual std::string portDeviceName(uint64_t nwid) const override
- {
- Mutex::Lock _l(_nets_m);
- std::map::const_iterator n(_nets.find(nwid));
- if ((n != _nets.end()) && (n->second.tap))
- return n->second.tap->deviceName();
- else
- return std::string();
- }
-
- virtual std::string givenHomePath()
- {
- return _homePath;
- }
-
- void getRoutes(uint64_t nwid, void* routeArray, unsigned int* numRoutes) override
- {
- Mutex::Lock _l(_nets_m);
- NetworkState& n = _nets[nwid];
- *numRoutes = *numRoutes < n.config.routeCount ? *numRoutes : n.config.routeCount;
- for (unsigned int i = 0; i < *numRoutes; i++) {
- ZT_VirtualNetworkRoute* vnr = (ZT_VirtualNetworkRoute*)routeArray;
- memcpy(&vnr[i], &(n.config.routes[i]), sizeof(ZT_VirtualNetworkRoute));
- }
- }
-
- virtual Node* getNode() override
- {
- return _node;
- }
-
- virtual void terminate() override
- {
- _run_m.lock();
- _run = false;
- _run_m.unlock();
- _phy.whack();
- }
-
- virtual bool getNetworkSettings(const uint64_t nwid, NetworkSettings& settings) const override
- {
- Mutex::Lock _l(_nets_m);
- std::map::const_iterator n(_nets.find(nwid));
- if (n == _nets.end())
- return false;
- settings = n->second.settings;
- return true;
- }
-
- // =========================================================================
- // Internal implementation methods for control plane, route setup, etc.
- // =========================================================================
-
- // Checks if a managed IP or route target is allowed
- bool checkIfManagedIsAllowed(const NetworkState& n, const InetAddress& target)
- {
- if (! n.settings.allowManaged)
- return false;
-
- if (n.settings.allowManagedWhitelist.size() > 0) {
- bool allowed = false;
- for (InetAddress addr : n.settings.allowManagedWhitelist) {
- if (addr.containsAddress(target) && addr.netmaskBits() <= target.netmaskBits()) {
- allowed = true;
- break;
- }
- }
- if (! allowed)
- return false;
- }
-
- if (target.isDefaultRoute())
- return n.settings.allowDefault;
- switch (target.ipScope()) {
- case InetAddress::IP_SCOPE_NONE:
- case InetAddress::IP_SCOPE_MULTICAST:
- case InetAddress::IP_SCOPE_LOOPBACK:
- case InetAddress::IP_SCOPE_LINK_LOCAL: return false;
- case InetAddress::IP_SCOPE_GLOBAL: return n.settings.allowGlobal;
- default: return true;
- }
- }
-
- // Apply or update managed IPs for a configured network (be sure n.tap exists)
- void syncManagedStuff(NetworkState& n)
- {
- char ipbuf[64];
- // assumes _nets_m is locked
- std::vector newManagedIps;
- newManagedIps.reserve(n.config.assignedAddressCount);
- for (unsigned int i = 0; i < n.config.assignedAddressCount; ++i) {
- const InetAddress* ii =
- reinterpret_cast(&(n.config.assignedAddresses[i]));
- if (checkIfManagedIsAllowed(n, *ii))
- newManagedIps.push_back(*ii);
- }
- std::sort(newManagedIps.begin(), newManagedIps.end());
- newManagedIps.erase(
- std::unique(newManagedIps.begin(), newManagedIps.end()),
- newManagedIps.end());
- for (std::vector::iterator ip(n.managedIps.begin()); ip != n.managedIps.end();
- ++ip) {
- if (std::find(newManagedIps.begin(), newManagedIps.end(), *ip) == newManagedIps.end()) {
- if (! n.tap->removeIp(*ip)) {
- fprintf(
- stderr,
- "ERROR: unable to remove ip address %s" ZT_EOL_S,
- ip->toString(ipbuf));
- }
- else {
- struct zts_addr_details* ad = new zts_addr_details();
- ad->nwid = n.tap->_nwid;
- if ((*ip).isV4()) {
- struct sockaddr_in* in4 = (struct sockaddr_in*)&(ad->addr);
- memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4);
- _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad);
- }
- if ((*ip).isV6()) {
- struct sockaddr_in6* in6 = (struct sockaddr_in6*)&(ad->addr);
- memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16);
- _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad);
- }
- }
- }
- }
- for (std::vector::iterator ip(newManagedIps.begin());
- ip != newManagedIps.end();
- ++ip) {
- if (std::find(n.managedIps.begin(), n.managedIps.end(), *ip) == n.managedIps.end()) {
- if (! n.tap->addIp(*ip)) {
- fprintf(
- stderr,
- "ERROR: unable to add ip address %s" ZT_EOL_S,
- ip->toString(ipbuf));
- }
- else {
- struct zts_addr_details* ad = new zts_addr_details();
- ad->nwid = n.tap->_nwid;
- if ((*ip).isV4()) {
- struct sockaddr_in* in4 = (struct sockaddr_in*)&(ad->addr);
- memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4);
- _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad);
- }
- if ((*ip).isV6()) {
- struct sockaddr_in6* in6 = (struct sockaddr_in6*)&(ad->addr);
- memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16);
- _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad);
- }
- }
- }
- }
- n.managedIps.swap(newManagedIps);
- }
-
- // =========================================================================
- // Handlers for Node and Phy<> callbacks
- // =========================================================================
-
- inline void phyOnDatagram(
- PhySocket* sock,
- void** uptr,
- const struct sockaddr* localAddr,
- const struct sockaddr* from,
- void* data,
- unsigned long len)
- {
- if ((len >= 16)
- && (reinterpret_cast(from)->ipScope()
- == InetAddress::IP_SCOPE_GLOBAL))
- _lastDirectReceiveFromGlobal = OSUtils::now();
- const ZT_ResultCode rc = _node->processWirePacket(
- (void*)0,
- OSUtils::now(),
- reinterpret_cast(sock),
- reinterpret_cast(
- from), // Phy<> uses sockaddr_storage, so it'll always be that big
- data,
- len,
- &_nextBackgroundTaskDeadline);
- if (ZT_ResultCode_isFatal(rc)) {
- char tmp[256];
- OSUtils::ztsnprintf(
- tmp,
- sizeof(tmp),
- "fatal error code from processWirePacket: %d",
- (int)rc);
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = tmp;
- this->terminate();
- }
- }
-
- inline void phyOnTcpConnect(PhySocket* sock, void** uptr, bool success)
- {
- }
- inline void phyOnTcpAccept(
- PhySocket* sockL,
- PhySocket* sockN,
- void** uptrL,
- void** uptrN,
- const struct sockaddr* from)
- {
- }
- void phyOnTcpClose(PhySocket* sock, void** uptr)
- {
- }
- void phyOnTcpData(PhySocket* sock, void** uptr, void* data, unsigned long len)
- {
- }
- inline void phyOnTcpWritable(PhySocket* sock, void** uptr)
- {
- }
- inline void
- phyOnFileDescriptorActivity(PhySocket* sock, void** uptr, bool readable, bool writable)
- {
- }
- inline void phyOnUnixAccept(PhySocket* sockL, PhySocket* sockN, void** uptrL, void** uptrN)
- {
- }
- inline void phyOnUnixClose(PhySocket* sock, void** uptr)
- {
- }
- inline void phyOnUnixData(PhySocket* sock, void** uptr, void* data, unsigned long len)
- {
- }
- inline void phyOnUnixWritable(PhySocket* sock, void** uptr)
- {
- }
-
- inline int nodeVirtualNetworkConfigFunction(
- uint64_t nwid,
- void** nuptr,
- enum ZT_VirtualNetworkConfigOperation op,
- const ZT_VirtualNetworkConfig* nwc)
- {
- Mutex::Lock _l(_nets_m);
- NetworkState& n = _nets[nwid];
-
- switch (op) {
- case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
- if (! n.tap) {
- char friendlyName[128];
- OSUtils::ztsnprintf(
- friendlyName,
- sizeof(friendlyName),
- "ZeroTier One [%.16llx]",
- nwid);
-
- n.tap = new EthernetTap(
- _homePath.c_str(),
- MAC(nwc->mac),
- nwc->mtu,
- (unsigned int)ZT_IF_METRIC,
- nwid,
- friendlyName,
- StapFrameHandler,
- (void*)this);
- *nuptr = (void*)&n;
- }
- // After setting up tap, fall through to CONFIG_UPDATE since we also want to do
- // this...
-
- case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
- memcpy(&(n.config), nwc, sizeof(ZT_VirtualNetworkConfig));
- if (n.tap) { // sanity check
- syncManagedStuff(n);
- n.tap->setMtu(nwc->mtu);
- }
- else {
- _nets.erase(nwid);
- return -999; // tap init failed
- }
- if (op
- == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE) { // Prevent junk from
- // ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP
- _enqueueEvent(ZTS_EVENT_NETWORK_UPDATE, (void*)prepare_network_details_msg(n));
- }
- break;
-
- case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
- case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
- if (n.tap) { // sanity check
- *nuptr = (void*)0;
- delete n.tap;
- _nets.erase(nwid);
- if (allowNetworkCaching) {
- if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
- char nlcpath[256];
- OSUtils::ztsnprintf(
- nlcpath,
- sizeof(nlcpath),
- "%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S
- "%.16llx.local.conf",
- _homePath.c_str(),
- nwid);
- OSUtils::rm(nlcpath);
- }
- }
- }
- else {
- _nets.erase(nwid);
- }
- break;
- }
- return 0;
- }
-
- inline void nodeEventCallback(enum ZT_Event event, const void* metaData)
- {
- // Feed node events into lock-free queue for later dequeuing by the callback thread
- switch (event) {
- case ZT_EVENT_UP: {
- _enqueueEvent(ZTS_EVENT_NODE_UP, NULL);
- } break;
- case ZT_EVENT_ONLINE: {
- struct zts_node_details* nd = new zts_node_details;
- nd->address = _node->address();
- nd->versionMajor = ZEROTIER_ONE_VERSION_MAJOR;
- nd->versionMinor = ZEROTIER_ONE_VERSION_MINOR;
- nd->versionRev = ZEROTIER_ONE_VERSION_REVISION;
- nd->primaryPort = _primaryPort;
- nd->secondaryPort = _secondaryPort;
- nd->tertiaryPort = _tertiaryPort;
- _enqueueEvent(ZTS_EVENT_NODE_ONLINE, (void*)nd);
- } break;
- case ZT_EVENT_OFFLINE: {
- struct zts_node_details* nd = new zts_node_details;
- nd->address = _node->address();
- _enqueueEvent(ZTS_EVENT_NODE_OFFLINE, (void*)nd);
- } break;
- case ZT_EVENT_DOWN: {
- struct zts_node_details* nd = new zts_node_details;
- nd->address = _node->address();
- _enqueueEvent(ZTS_EVENT_NODE_DOWN, (void*)nd);
- } break;
- case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_IDENTITY_COLLISION;
- _fatalErrorMessage = "identity/address collision";
- this->terminate();
- } break;
-
- case ZT_EVENT_TRACE: {
- if (metaData) {
- ::fprintf(stderr, "%s" ZT_EOL_S, (const char*)metaData);
- ::fflush(stderr);
- }
- } break;
-
- default: break;
- }
- }
-
- void
- native_ss_to_zts_ss(struct zts_sockaddr_storage* ss_out, const struct sockaddr_storage* ss_in)
- {
- if (ss_in->ss_family == AF_INET) {
- struct sockaddr_in* s_in4 = (struct sockaddr_in*)ss_in;
- struct zts_sockaddr_in* d_in4 = (struct zts_sockaddr_in*)ss_out;
-#ifndef __WINDOWS__
- d_in4->sin_len = 0; // s_in4->sin_len;
-#endif
- d_in4->sin_family = ZTS_AF_INET;
- d_in4->sin_port = s_in4->sin_port;
- memcpy(&(d_in4->sin_addr), &(s_in4->sin_addr), sizeof(s_in4->sin_addr));
- }
- if (ss_in->ss_family == AF_INET6) {
- struct sockaddr_in6* s_in6 = (struct sockaddr_in6*)ss_in;
- struct zts_sockaddr_in6* d_in6 = (struct zts_sockaddr_in6*)ss_out;
-#ifndef __WINDOWS__
- d_in6->sin6_len = 0; // s_in6->sin6_len;
-#endif
- d_in6->sin6_family = ZTS_AF_INET6;
- d_in6->sin6_port = s_in6->sin6_port;
- d_in6->sin6_flowinfo = s_in6->sin6_flowinfo;
- memcpy(&(d_in6->sin6_addr), &(s_in6->sin6_addr), sizeof(s_in6->sin6_addr));
- d_in6->sin6_scope_id = s_in6->sin6_scope_id;
- }
- }
-
- struct zts_network_details* prepare_network_details_msg(const NetworkState& n)
- {
- struct zts_network_details* nd = new zts_network_details();
-
- nd->nwid = n.config.nwid;
- nd->mac = n.config.mac;
- memcpy(nd->name, n.config.name, sizeof(n.config.name));
- nd->status = (ZTS_VirtualNetworkStatus)n.config.status;
- nd->type = (ZTS_VirtualNetworkType)n.config.type;
- nd->mtu = n.config.mtu;
- nd->dhcp = n.config.dhcp;
- nd->bridge = n.config.bridge;
- nd->broadcastEnabled = n.config.broadcastEnabled;
- nd->portError = n.config.portError;
- nd->netconfRevision = n.config.netconfRevision;
-
- // Copy and convert address structures
- nd->assignedAddressCount = n.config.assignedAddressCount;
- for (unsigned int i = 0; i < n.config.assignedAddressCount; i++) {
- native_ss_to_zts_ss(&(nd->assignedAddresses[i]), &(n.config.assignedAddresses[i]));
- }
-
- nd->routeCount = n.config.routeCount;
- for (unsigned int i = 0; i < n.config.routeCount; i++) {
- native_ss_to_zts_ss(&(nd->routes[i].target), &(n.config.routes[i].target));
- native_ss_to_zts_ss(&(nd->routes[i].via), &(n.config.routes[i].via));
- nd->routes[i].flags = n.config.routes[i].flags;
- nd->routes[i].metric = n.config.routes[i].metric;
- }
-
- nd->multicastSubscriptionCount = n.config.multicastSubscriptionCount;
- memcpy(
- nd->multicastSubscriptions,
- &(n.config.multicastSubscriptions),
- sizeof(n.config.multicastSubscriptions));
-
- return nd;
- }
-
- inline void generateEventMsgs()
- {
- // Force the ordering of callback messages, these messages are
- // only useful if the node and stack are both up and running
- if (! _node->online() || ! _lwip_is_up()) {
- return;
- }
- // Generate messages to be dequeued by the callback message thread
- Mutex::Lock _l(_nets_m);
- for (std::map::iterator n(_nets.begin()); n != _nets.end(); ++n) {
- auto netState = n->second;
- int mostRecentStatus = netState.config.status;
- VirtualTap* tap = netState.tap;
- // uint64_t nwid = n->first;
- if (netState.tap->_networkStatus == mostRecentStatus) {
- continue; // No state change
- }
- switch (mostRecentStatus) {
- case ZT_NETWORK_STATUS_NOT_FOUND:
- _enqueueEvent(
- ZTS_EVENT_NETWORK_NOT_FOUND,
- (void*)prepare_network_details_msg(netState));
- break;
- case ZT_NETWORK_STATUS_CLIENT_TOO_OLD:
- _enqueueEvent(
- ZTS_EVENT_NETWORK_CLIENT_TOO_OLD,
- (void*)prepare_network_details_msg(netState));
- break;
- case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION:
- _enqueueEvent(
- ZTS_EVENT_NETWORK_REQ_CONFIG,
- (void*)prepare_network_details_msg(netState));
- break;
- case ZT_NETWORK_STATUS_OK:
- if (tap->hasIpv4Addr() && _lwip_is_netif_up(tap->netif4)) {
- _enqueueEvent(
- ZTS_EVENT_NETWORK_READY_IP4,
- (void*)prepare_network_details_msg(netState));
- }
- if (tap->hasIpv6Addr() && _lwip_is_netif_up(tap->netif6)) {
- _enqueueEvent(
- ZTS_EVENT_NETWORK_READY_IP6,
- (void*)prepare_network_details_msg(netState));
- }
- // In addition to the READY messages, send one OK message
- _enqueueEvent(
- ZTS_EVENT_NETWORK_OK,
- (void*)prepare_network_details_msg(netState));
- break;
- case ZT_NETWORK_STATUS_ACCESS_DENIED:
- _enqueueEvent(
- ZTS_EVENT_NETWORK_ACCESS_DENIED,
- (void*)prepare_network_details_msg(netState));
- break;
- default: break;
- }
- netState.tap->_networkStatus = mostRecentStatus;
- }
- bool bShouldCopyPeerInfo = false;
- int eventCode = 0;
- ZT_PeerList* pl = _node->peers();
- struct zts_peer_details* pd;
- if (pl) {
- for (unsigned long i = 0; i < pl->peerCount; ++i) {
- if (! peerCache.count(pl->peers[i].address)) {
- // New peer, add status
- if (pl->peers[i].pathCount > 0) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_DIRECT;
- }
- if (pl->peers[i].pathCount == 0) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_RELAY;
- }
- }
- // Previously known peer, update status
- else {
- if (peerCache[pl->peers[i].address] < pl->peers[i].pathCount) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_PATH_DISCOVERED;
- }
- if (peerCache[pl->peers[i].address] > pl->peers[i].pathCount) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_PATH_DEAD;
- }
- if (peerCache[pl->peers[i].address] == 0 && pl->peers[i].pathCount > 0) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_DIRECT;
- }
- if (peerCache[pl->peers[i].address] > 0 && pl->peers[i].pathCount == 0) {
- bShouldCopyPeerInfo = true;
- eventCode = ZTS_EVENT_PEER_RELAY;
- }
- }
- if (bShouldCopyPeerInfo) {
- pd = new zts_peer_details();
- memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details));
- for (unsigned int j = 0; j < pl->peers[i].pathCount; j++) {
- native_ss_to_zts_ss(
- &(pd->paths[j].address),
- &(pl->peers[i].paths[j].address));
- }
- _enqueueEvent(eventCode, (void*)pd);
- bShouldCopyPeerInfo = false;
- }
- // Update our cache with most recently observed path count
- peerCache[pl->peers[i].address] = pl->peers[i].pathCount;
- }
- }
- _node->freeQueryResult((void*)pl);
- }
-
- inline void join(uint64_t nwid) override
- {
- _node->join(nwid, NULL, NULL);
- }
-
- inline void leave(uint64_t nwid) override
- {
- _node->leave(nwid, NULL, NULL);
- }
-
- inline void getIdentity(char* key_pair_str, uint16_t* key_buf_len) override
- {
- if (key_pair_str == NULL || *key_buf_len < ZT_IDENTITY_STRING_BUFFER_LENGTH) {
- return;
- }
- uint16_t keylen = strlen(_userProvidedSecretIdentity);
- if (*key_buf_len < keylen) {
- *key_buf_len = 0;
- return;
- }
- memcpy(key_pair_str, _userProvidedSecretIdentity, keylen);
- *key_buf_len = keylen;
- }
-
- // TODO: This logic should be further generalized in the next API redesign
- inline void nodeStatePutFunction(
- enum ZT_StateObjectType type,
- const uint64_t id[2],
- const void* data,
- int len)
- {
- char p[1024];
- FILE* f;
- bool secure = false;
- char dirname[1024];
- dirname[0] = 0;
-
- switch (type) {
- case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
- memcpy(_userProvidedPublicIdentity, data, len);
- if (disableLocalStorage) {
- return;
- }
- else {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "identity.public",
- _homePath.c_str());
- }
- break;
- case ZT_STATE_OBJECT_IDENTITY_SECRET:
- memcpy(_userProvidedSecretIdentity, data, len);
- if (disableLocalStorage) {
- return;
- }
- else {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "identity.secret",
- _homePath.c_str());
- }
- secure = true;
- break;
- case ZT_STATE_OBJECT_PLANET:
- if (disableLocalStorage) {
- return;
- }
- else {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "planet",
- _homePath.c_str());
- }
- break;
- case ZT_STATE_OBJECT_NETWORK_CONFIG:
- if (disableLocalStorage) {
- return;
- }
- else {
- if (allowNetworkCaching) {
- OSUtils::ztsnprintf(
- dirname,
- sizeof(dirname),
- "%s" ZT_PATH_SEPARATOR_S "networks.d",
- _homePath.c_str());
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",
- dirname,
- (unsigned long long)id[0]);
- secure = true;
- }
- else {
- return;
- }
- }
- break;
- case ZT_STATE_OBJECT_PEER:
- if (disableLocalStorage) {
- return;
- }
- else {
- if (allowPeerCaching) {
- OSUtils::ztsnprintf(
- dirname,
- sizeof(dirname),
- "%s" ZT_PATH_SEPARATOR_S "peers.d",
- _homePath.c_str());
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",
- dirname,
- (unsigned long long)id[0]);
- }
- else {
- return; // Do nothing
- }
- }
- break;
- default: return;
- }
-
- if (len >= 0) {
- // Check to see if we've already written this first. This reduces
- // redundant writes and I/O overhead on most platforms and has
- // little effect on others.
- f = fopen(p, "rb");
- if (f) {
- char buf[65535];
- long l = (long)fread(buf, 1, sizeof(buf), f);
- fclose(f);
- if ((l == (long)len) && (memcmp(data, buf, l) == 0))
- return;
- }
-
- f = fopen(p, "wb");
- if ((! f) && (dirname[0])) { // create subdirectory if it does not exist
- OSUtils::mkdir(dirname);
- f = fopen(p, "wb");
- }
- if (f) {
- if (fwrite(data, len, 1, f) != 1)
- fprintf(stderr, "WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S, p);
- fclose(f);
- if (secure)
- OSUtils::lockDownFile(p, false);
+ if (_trialBind(_primaryPort)) {
+ _ports[0] = _primaryPort;
}
else {
- fprintf(
- stderr,
- "WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,
- p);
+ _primaryPort = 0;
}
}
- else {
- OSUtils::rm(p);
+ if (_ports[0] == 0) {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = "cannot bind to local control interface port";
+ return _termReason;
}
- }
- // TODO: This logic should be further generalized in the next API redesign
- inline int nodeStateGetFunction(
- enum ZT_StateObjectType type,
- const uint64_t id[2],
- void* data,
- unsigned int maxlen)
- {
- char p[4096];
- unsigned int keylen = 0;
- switch (type) {
- case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
- if (disableLocalStorage) {
- keylen = strlen(_userProvidedPublicIdentity);
- if (keylen > maxlen) {
- return -1;
+ // Attempt to bind to a secondary port chosen from our ZeroTier
+ // address. This exists because there are buggy NATs out there that
+ // fail if more than one device behind the same NAT tries to use the
+ // same internal private address port number. Buggy NATs are a
+ // running theme.
+ _ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500)
+ : _secondaryPort;
+ for (int i = 0;; ++i) {
+ if (i > 1000) {
+ _ports[1] = 0;
+ break;
+ }
+ else if (++_ports[1] >= 65536) {
+ _ports[1] = 20000;
+ }
+ if (_trialBind(_ports[1])) {
+ break;
+ }
+ }
+#ifdef ZT_USE_MINIUPNPC
+ if (_portMappingEnabled) {
+ // If we're running uPnP/NAT-PMP, bind a *third* port for that.
+ // We can't use the other two ports for that because some NATs
+ // do really funky stuff with ports that are explicitly mapped
+ // that breaks things.
+ if (_ports[1]) {
+ _ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort;
+ for (int i = 0;; ++i) {
+ if (i > 1000) {
+ _ports[2] = 0;
+ break;
}
- if (keylen > 0) {
- memcpy(data, _userProvidedPublicIdentity, keylen);
- return keylen;
+ else if (++_ports[2] >= 65536) {
+ _ports[2] = 20000;
+ }
+ if (_trialBind(_ports[2])) {
+ break;
}
}
- else {
+ if (_ports[2]) {
+ char uniqueName[64] = { 0 };
OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "identity.public",
- _homePath.c_str());
+ uniqueName,
+ sizeof(uniqueName),
+ "ZeroTier/%.10llx@%u",
+ _node->address(),
+ _ports[2]);
+ _portMapper = new PortMapper(_ports[2], uniqueName);
}
+ }
+ }
+#endif
+ // Join existing networks in networks.d
+ if (_allowNetworkCaching) {
+ std::vector networksDotD(
+ OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str()));
+ for (std::vector::iterator f(networksDotD.begin());
+ f != networksDotD.end();
+ ++f) {
+ std::size_t dot = f->find_last_of('.');
+ if ((dot == 16) && (f->substr(16) == ".conf")) {
+ _node->join(Utils::hexStrToU64(f->substr(0, dot).c_str()), (void*)0, (void*)0);
+ }
+ }
+ }
+ // Main I/O loop
+ _nextBackgroundTaskDeadline = 0;
+ int64_t clockShouldBe = OSUtils::now();
+ _lastRestart = clockShouldBe;
+ int64_t lastTapMulticastGroupCheck = 0;
+ int64_t lastBindRefresh = 0;
+ int64_t lastCleanedPeersDb = 0;
+ int64_t lastLocalInterfaceAddressCheck =
+ (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL)
+ + 15000; // do this in 15s to give portmapper time to
+ // configure and other things time to settle
+ for (;;) {
+ _run_m.lock();
+ if (! _run) {
+ _run_m.unlock();
+ _termReason_m.lock();
+ _termReason = ONE_NORMAL_TERMINATION;
+ _termReason_m.unlock();
break;
- case ZT_STATE_OBJECT_IDENTITY_SECRET:
- if (disableLocalStorage) {
- keylen = strlen(_userProvidedSecretIdentity);
- if (keylen > maxlen) {
- return -1;
- }
- if (keylen > 0) {
- memcpy(data, _userProvidedSecretIdentity, keylen);
- return keylen;
+ }
+ else {
+ _run_m.unlock();
+ }
+
+ const int64_t now = OSUtils::now();
+
+ // Attempt to detect sleep/wake events by detecting delay
+ // overruns
+ bool restarted = false;
+ if ((now > clockShouldBe) && ((now - clockShouldBe) > 10000)) {
+ _lastRestart = now;
+ restarted = true;
+ }
+
+ // Refresh bindings in case device's interfaces have changed,
+ // and also sync routes to update any shadow routes (e.g. shadow
+ // default)
+ if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD) || (restarted)) {
+ lastBindRefresh = now;
+ unsigned int p[3] = { 0 };
+ unsigned int pc = 0;
+ for (int i = 0; i < 3; ++i) {
+ if (_ports[i]) {
+ p[pc++] = _ports[i];
}
}
- else {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "identity.secret",
- _homePath.c_str());
- }
- break;
- case ZT_STATE_OBJECT_PLANET:
- if (disableLocalStorage) {
- return -1;
- }
- else {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "planet",
- _homePath.c_str());
- }
- break;
- case ZT_STATE_OBJECT_NETWORK_CONFIG:
- if (disableLocalStorage) {
- return -1;
- }
- else {
- if (allowNetworkCaching) {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S
- "%.16llx.conf",
- _homePath.c_str(),
- (unsigned long long)id[0]);
- }
- else {
- return -1;
- }
- }
- break;
- case ZT_STATE_OBJECT_PEER:
- if (allowPeerCaching) {
- OSUtils::ztsnprintf(
- p,
- sizeof(p),
- "%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",
- _homePath.c_str(),
- (unsigned long long)id[0]);
- }
- break;
- default: return -1;
- }
- FILE* f = fopen(p, "rb");
- if (f) {
- int n = (int)fread(data, 1, maxlen, f);
- fclose(f);
- if (n >= 0)
- return n;
- }
- return -1;
- }
+ _binder.refresh(_phy, p, pc, explicitBind, *this);
+ }
- inline int nodeWirePacketSendFunction(
- const int64_t localSocket,
- const struct sockaddr_storage* addr,
- const void* data,
- unsigned int len,
- unsigned int ttl)
- {
- // Even when relaying we still send via UDP. This way if UDP starts
- // working we can instantly "fail forward" to it and stop using TCP
- // proxy fallback, which is slow.
+ // Generate callback messages for user application
+ generateEventMsgs();
- if ((localSocket != -1) && (localSocket != 0)
- && (_binder.isUdpSocketValid((PhySocket*)((uintptr_t)localSocket)))) {
- if ((ttl) && (addr->ss_family == AF_INET))
- _phy.setIp4UdpTtl((PhySocket*)((uintptr_t)localSocket), ttl);
- const bool r = _phy.udpSend(
- (PhySocket*)((uintptr_t)localSocket),
- (const struct sockaddr*)addr,
- data,
- len);
- if ((ttl) && (addr->ss_family == AF_INET))
- _phy.setIp4UdpTtl((PhySocket*)((uintptr_t)localSocket), 255);
- return ((r) ? 0 : -1);
- }
- else {
- return ((_binder.udpSendAll(_phy, addr, data, len, ttl)) ? 0 : -1);
- }
- }
+ // Run background task processor in core if it's time to do so
+ int64_t dl = _nextBackgroundTaskDeadline;
+ if (dl <= now) {
+ _node->processBackgroundTasks((void*)0, now, &_nextBackgroundTaskDeadline);
+ dl = _nextBackgroundTaskDeadline;
+ }
- inline void nodeVirtualNetworkFrameFunction(
- uint64_t nwid,
- void** nuptr,
- uint64_t sourceMac,
- uint64_t destMac,
- unsigned int etherType,
- unsigned int vlanId,
- const void* data,
- unsigned int len)
- {
- NetworkState* n = reinterpret_cast(*nuptr);
- if ((! n) || (! n->tap))
- return;
- n->tap->put(MAC(sourceMac), MAC(destMac), etherType, data, len);
- }
-
- inline int nodePathCheckFunction(
- uint64_t ztaddr,
- const int64_t localSocket,
- const struct sockaddr_storage* remoteAddr)
- {
- // Make sure we're not trying to do ZeroTier-over-ZeroTier
- {
- Mutex::Lock _l(_nets_m);
- for (std::map::const_iterator n(_nets.begin());
- n != _nets.end();
- ++n) {
- if (n->second.tap) {
- std::vector ips(n->second.tap->ips());
- for (std::vector::const_iterator i(ips.begin()); i != ips.end();
- ++i) {
- if (i->containsAddress(
- *(reinterpret_cast(remoteAddr)))) {
- return 0;
+ // Sync multicast group memberships
+ if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
+ lastTapMulticastGroupCheck = now;
+ std::vector, std::vector > > >
+ mgChanges;
+ {
+ Mutex::Lock _l(_nets_m);
+ mgChanges.reserve(_nets.size() + 1);
+ for (std::map::const_iterator n(_nets.begin());
+ n != _nets.end();
+ ++n) {
+ if (n->second.tap) {
+ mgChanges.push_back(std::pair<
+ uint64_t,
+ std::pair<
+ std::vector,
+ std::vector > >(
+ n->first,
+ std::pair<
+ std::vector,
+ std::vector >()));
+ n->second.tap->scanMulticastGroups(
+ mgChanges.back().second.first,
+ mgChanges.back().second.second);
}
}
}
+ for (std::vector, std::vector > > >::
+ iterator c(mgChanges.begin());
+ c != mgChanges.end();
+ ++c) {
+ auto mgpair = c->second;
+ for (std::vector::iterator m(mgpair.first.begin());
+ m != mgpair.first.end();
+ ++m)
+ _node->multicastSubscribe((void*)0, c->first, m->mac().toInt(), m->adi());
+ for (std::vector::iterator m(mgpair.second.begin());
+ m != mgpair.second.end();
+ ++m)
+ _node->multicastUnsubscribe(c->first, m->mac().toInt(), m->adi());
+ }
+ }
+
+ // Sync information about physical network interfaces
+ if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
+ lastLocalInterfaceAddressCheck = now;
+
+ _node->clearLocalInterfaceAddresses();
+
+#ifdef ZT_USE_MINIUPNPC
+ if (_portMapper) {
+ std::vector mappedAddresses(_portMapper->get());
+ for (std::vector::const_iterator ext(mappedAddresses.begin());
+ ext != mappedAddresses.end();
+ ++ext)
+ _node->addLocalInterfaceAddress(
+ reinterpret_cast(&(*ext)));
+ }
+#endif
+
+ std::vector boundAddrs(_binder.allBoundLocalInterfaceAddresses());
+ for (std::vector::const_iterator i(boundAddrs.begin());
+ i != boundAddrs.end();
+ ++i)
+ _node->addLocalInterfaceAddress(
+ reinterpret_cast(&(*i)));
+ }
+
+ // Clean peers.d periodically
+ if ((now - lastCleanedPeersDb) >= 3600000) {
+ lastCleanedPeersDb = now;
+ OSUtils::cleanDirectory(
+ (_homePath + ZT_PATH_SEPARATOR_S "peers.d").c_str(),
+ now - 2592000000LL); // delete older than 30 days
+ }
+
+ const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
+ clockShouldBe = now + (uint64_t)delay;
+ _phy.poll(delay);
+ }
+ }
+ catch (std::exception& e) {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = std::string("unexpected exception in main thread: ") + e.what();
+ }
+ catch (...) {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = "unexpected exception in main thread: unknown exception";
+ }
+
+ {
+ Mutex::Lock _l(_nets_m);
+ for (std::map::iterator n(_nets.begin()); n != _nets.end(); ++n)
+ delete n->second.tap;
+ _nets.clear();
+ }
+
+ switch (_termReason) {
+ case ONE_NORMAL_TERMINATION:
+ nodeEventCallback(ZT_EVENT_DOWN, NULL);
+ break;
+ case ONE_UNRECOVERABLE_ERROR:
+ nodeEventCallback(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION, NULL);
+ break;
+ default:
+ break;
+ }
+ delete _node;
+ _node = (Node*)0;
+ return _termReason;
+}
+
+NodeService::ReasonForTermination NodeService::reasonForTermination() const
+{
+ Mutex::Lock _l(_termReason_m);
+ return _termReason;
+}
+
+std::string NodeService::fatalErrorMessage() const
+{
+ Mutex::Lock _l(_termReason_m);
+ return _fatalErrorMessage;
+}
+
+void NodeService::getRoutes(uint64_t net_id, void* routeArray, unsigned int* numRoutes)
+{
+ Mutex::Lock _l(_nets_m);
+ NetworkState& n = _nets[net_id];
+ *numRoutes = *numRoutes < n.config.routeCount ? *numRoutes : n.config.routeCount;
+ for (unsigned int i = 0; i < *numRoutes; i++) {
+ ZT_VirtualNetworkRoute* vnr = (ZT_VirtualNetworkRoute*)routeArray;
+ memcpy(&vnr[i], &(n.config.routes[i]), sizeof(ZT_VirtualNetworkRoute));
+ }
+}
+
+void NodeService::terminate()
+{
+ _run_m.lock();
+ _run = false;
+ _run_m.unlock();
+ _phy.whack();
+}
+
+void NodeService::uninitialize()
+{
+ // Reset port
+ _primaryPort = 0;
+ // Reset storage location
+ _homePath.clear();
+ // Reset cache settings
+ _allowNetworkCaching = true;
+ _allowPeerCaching = true;
+ // Reset identities
+ memset(_publicIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH);
+ memset(_secretIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH);
+ // Reset blacklist
+ _interfacePrefixBlacklist.clear();
+}
+
+int NodeService::getNetworkSettings(const uint64_t net_id, NetworkSettings& settings) const
+{
+ Mutex::Lock _l(_nets_m);
+ std::map::const_iterator n(_nets.find(net_id));
+ if (n == _nets.end()) {
+ return false;
+ }
+ settings = n->second.settings;
+ return true;
+}
+
+// Checks if a managed IP or route target is allowed
+int NodeService::checkIfManagedIsAllowed(const NetworkState& n, const InetAddress& target)
+{
+ if (! n.settings.allowManaged) {
+ return false;
+ }
+ if (n.settings.allowManagedWhitelist.size() > 0) {
+ bool allowed = false;
+ for (InetAddress addr : n.settings.allowManagedWhitelist) {
+ if (addr.containsAddress(target) && addr.netmaskBits() <= target.netmaskBits()) {
+ allowed = true;
+ break;
}
}
+ if (! allowed)
+ return false;
+ }
+ if (target.isDefaultRoute())
+ return n.settings.allowDefault;
+ switch (target.ipScope()) {
+ case InetAddress::IP_SCOPE_NONE:
+ case InetAddress::IP_SCOPE_MULTICAST:
+ case InetAddress::IP_SCOPE_LOOPBACK:
+ case InetAddress::IP_SCOPE_LINK_LOCAL:
+ return false;
+ case InetAddress::IP_SCOPE_GLOBAL:
+ return n.settings.allowGlobal;
+ default:
+ return true;
+ }
+}
- /* Note: I do not think we need to scan for overlap with managed routes
- * because of the "route forking" and interface binding that we do. This
- * ensures (we hope) that ZeroTier traffic will still take the physical
- * path even if its managed routes override this for other traffic. Will
- * revisit if we see recursion problems. */
-
- // Check blacklists
- const Hashtable >* blh =
- (const Hashtable >*)0;
- const std::vector* gbl = (const std::vector*)0;
- if (remoteAddr->ss_family == AF_INET) {
- blh = &_v4Blacklists;
- gbl = &_globalV4Blacklist;
+// Apply or update managed IPs for a configured network (be sure n.tap
+// exists)
+void NodeService::syncManagedStuff(NetworkState& n)
+{
+ char ipbuf[64] = { 0 };
+ // assumes _nets_m is locked
+ std::vector newManagedIps;
+ newManagedIps.reserve(n.config.assignedAddressCount);
+ for (unsigned int i = 0; i < n.config.assignedAddressCount; ++i) {
+ const InetAddress* ii =
+ reinterpret_cast(&(n.config.assignedAddresses[i]));
+ if (checkIfManagedIsAllowed(n, *ii)) {
+ newManagedIps.push_back(*ii);
}
- else if (remoteAddr->ss_family == AF_INET6) {
- blh = &_v6Blacklists;
- gbl = &_globalV6Blacklist;
- }
- if (blh) {
- Mutex::Lock _l(_localConfig_m);
- const std::vector* l = blh->get(ztaddr);
- if (l) {
- for (std::vector::const_iterator a(l->begin()); a != l->end(); ++a) {
- if (a->containsAddress(*reinterpret_cast(remoteAddr)))
- return 0;
+ }
+ std::sort(newManagedIps.begin(), newManagedIps.end());
+ newManagedIps.erase(
+ std::unique(newManagedIps.begin(), newManagedIps.end()),
+ newManagedIps.end());
+ for (std::vector::iterator ip(n.managedIps.begin()); ip != n.managedIps.end();
+ ++ip) {
+ if (std::find(newManagedIps.begin(), newManagedIps.end(), *ip) == newManagedIps.end()) {
+ if (! n.tap->removeIp(*ip)) {
+ fprintf(
+ stderr,
+ "ERROR: unable to remove ip address %s" ZT_EOL_S,
+ ip->toString(ipbuf));
+ }
+ else {
+ zts_addr_info_t* ad = new zts_addr_info_t();
+ ad->net_id = n.tap->_net_id;
+ if ((*ip).isV4()) {
+ struct sockaddr_in* in4 = (struct sockaddr_in*)&(ad->addr);
+ memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4);
+ in4->sin_family = ZTS_AF_INET;
+ _events->enqueue(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad);
+ }
+ if ((*ip).isV6()) {
+ struct sockaddr_in6* in6 = (struct sockaddr_in6*)&(ad->addr);
+ memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16);
+ in6->sin6_family = ZTS_AF_INET6;
+ _events->enqueue(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad);
}
}
}
- if (gbl) {
- for (std::vector::const_iterator a(gbl->begin()); a != gbl->end(); ++a) {
- if (a->containsAddress(*reinterpret_cast(remoteAddr)))
- return 0;
- }
- }
- return 1;
}
-
- inline int nodePathLookupFunction(uint64_t ztaddr, int family, struct sockaddr_storage* result)
- {
- const Hashtable >* lh =
- (const Hashtable >*)0;
- if (family < 0)
- lh = (_node->prng() & 1) ? &_v4Hints : &_v6Hints;
- else if (family == AF_INET)
- lh = &_v4Hints;
- else if (family == AF_INET6)
- lh = &_v6Hints;
- else
- return 0;
- const std::vector* l = lh->get(ztaddr);
- if ((l) && (l->size() > 0)) {
- memcpy(
- result,
- &((*l)[(unsigned long)_node->prng() % l->size()]),
- sizeof(struct sockaddr_storage));
- return 1;
- }
- else
- return 0;
- }
-
- inline void tapFrameHandler(
- uint64_t nwid,
- const MAC& from,
- const MAC& to,
- unsigned int etherType,
- unsigned int vlanId,
- const void* data,
- unsigned int len)
- {
- _node->processVirtualNetworkFrame(
- (void*)0,
- OSUtils::now(),
- nwid,
- from.toInt(),
- to.toInt(),
- etherType,
- vlanId,
- data,
- len,
- &_nextBackgroundTaskDeadline);
- }
-
- bool shouldBindInterface(const char* ifname, const InetAddress& ifaddr)
- {
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
- if ((ifname[0] == 'l') && (ifname[1] == 'o'))
- return false; // loopback
- if ((ifname[0] == 'z') && (ifname[1] == 't'))
- return false; // sanity check: zt#
- if ((ifname[0] == 't') && (ifname[1] == 'u') && (ifname[2] == 'n'))
- return false; // tun# is probably an OpenVPN tunnel or similar
- if ((ifname[0] == 't') && (ifname[1] == 'a') && (ifname[2] == 'p'))
- return false; // tap# is probably an OpenVPN tunnel or similar
-#endif
-
-#ifdef __APPLE__
- if ((ifname[0] == 'f') && (ifname[1] == 'e') && (ifname[2] == 't') && (ifname[3] == 'h'))
- return false; // ... as is feth#
- if ((ifname[0] == 'l') && (ifname[1] == 'o'))
- return false; // loopback
- if ((ifname[0] == 'z') && (ifname[1] == 't'))
- return false; // sanity check: zt#
- if ((ifname[0] == 't') && (ifname[1] == 'u') && (ifname[2] == 'n'))
- return false; // tun# is probably an OpenVPN tunnel or similar
- if ((ifname[0] == 't') && (ifname[1] == 'a') && (ifname[2] == 'p'))
- return false; // tap# is probably an OpenVPN tunnel or similar
- if ((ifname[0] == 'u') && (ifname[1] == 't') && (ifname[2] == 'u') && (ifname[3] == 'n'))
- return false; // ... as is utun#
-#endif
-
- {
- Mutex::Lock _l(_localConfig_m);
- for (std::vector::const_iterator p(_interfacePrefixBlacklist.begin());
- p != _interfacePrefixBlacklist.end();
- ++p) {
- if (! strncmp(p->c_str(), ifname, p->length()))
- return false;
+ for (std::vector::iterator ip(newManagedIps.begin()); ip != newManagedIps.end();
+ ++ip) {
+ if (std::find(n.managedIps.begin(), n.managedIps.end(), *ip) == n.managedIps.end()) {
+ if (! n.tap->addIp(*ip)) {
+ fprintf(stderr, "ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf));
}
- }
- {
- // Check global blacklists
- const std::vector* gbl = (const std::vector*)0;
- if (ifaddr.ss_family == AF_INET) {
- gbl = &_globalV4Blacklist;
- }
- else if (ifaddr.ss_family == AF_INET6) {
- gbl = &_globalV6Blacklist;
- }
- if (gbl) {
- Mutex::Lock _l(_localConfig_m);
- for (std::vector::const_iterator a(gbl->begin()); a != gbl->end();
- ++a) {
- if (a->containsAddress(ifaddr))
- return false;
+ else {
+ zts_addr_info_t* ad = new zts_addr_info_t();
+ ad->net_id = n.tap->_net_id;
+ if ((*ip).isV4()) {
+ struct sockaddr_in* in4 = (struct sockaddr_in*)&(ad->addr);
+ memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4);
+ in4->sin_family = ZTS_AF_INET;
+ _events->enqueue(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad);
+ }
+ if ((*ip).isV6()) {
+ struct sockaddr_in6* in6 = (struct sockaddr_in6*)&(ad->addr);
+ memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16);
+ in6->sin6_family = ZTS_AF_INET6;
+ _events->enqueue(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad);
}
}
}
- {
- Mutex::Lock _l(_nets_m);
- for (std::map::const_iterator n(_nets.begin());
- n != _nets.end();
- ++n) {
- if (n->second.tap) {
- std::vector ips(n->second.tap->ips());
- for (std::vector::const_iterator i(ips.begin()); i != ips.end();
- ++i) {
- if (i->ipsEqual(ifaddr))
- return false;
+ }
+ n.managedIps.swap(newManagedIps);
+}
+
+void NodeService::phyOnDatagram(
+ PhySocket* sock,
+ void** uptr,
+ const struct sockaddr* localAddr,
+ const struct sockaddr* from,
+ void* data,
+ unsigned long len)
+{
+ if ((len >= 16)
+ && (reinterpret_cast(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
+ _lastDirectReceiveFromGlobal = OSUtils::now();
+ const ZT_ResultCode rc = _node->processWirePacket(
+ (void*)0,
+ OSUtils::now(),
+ reinterpret_cast(sock),
+ reinterpret_cast(from), // Phy<> uses sockaddr_storage, so
+ // it'll always be that big
+ data,
+ len,
+ &_nextBackgroundTaskDeadline);
+ if (ZT_ResultCode_isFatal(rc)) {
+ char tmp[256] = { 0 };
+ OSUtils::ztsnprintf(
+ tmp,
+ sizeof(tmp),
+ "fatal error code from processWirePacket: %d",
+ (int)rc);
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = tmp;
+ this->terminate();
+ }
+}
+
+int NodeService::nodeVirtualNetworkConfigFunction(
+ uint64_t net_id,
+ void** nuptr,
+ enum ZT_VirtualNetworkConfigOperation op,
+ const ZT_VirtualNetworkConfig* nwc)
+{
+ Mutex::Lock _l(_nets_m);
+ NetworkState& n = _nets[net_id];
+
+ switch (op) {
+ case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP:
+ if (! n.tap) {
+ char friendlyName[128] = { 0 };
+ OSUtils::ztsnprintf(
+ friendlyName,
+ sizeof(friendlyName),
+ "ZeroTier One [%.16llx]",
+ net_id);
+
+ n.tap = new VirtualTap(
+ _homePath.c_str(),
+ MAC(nwc->mac),
+ nwc->mtu,
+ (unsigned int)ZT_IF_METRIC,
+ net_id,
+ friendlyName,
+ StapFrameHandler,
+ (void*)this);
+ *nuptr = (void*)&n;
+ n.tap->setUserEventSystem(_events);
+ }
+ // After setting up tap, fall through to CONFIG_UPDATE since we
+ // also want to do this...
+
+ case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE:
+ memcpy(&(n.config), nwc, sizeof(ZT_VirtualNetworkConfig));
+ if (n.tap) { // sanity check
+ syncManagedStuff(n);
+ n.tap->setMtu(nwc->mtu);
+ }
+ else {
+ _nets.erase(net_id);
+ return -999; // tap init failed
+ }
+ if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE) {
+ _events->enqueue(ZTS_EVENT_NETWORK_UPDATE, (void*)prepare_network_details_msg(n));
+ }
+ break;
+
+ case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN:
+ case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY:
+ if (n.tap) { // sanity check
+ *nuptr = (void*)0;
+ delete n.tap;
+ _nets.erase(net_id);
+ if (_allowNetworkCaching) {
+ if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) {
+ char nlcpath[256] = { 0 };
+ OSUtils::ztsnprintf(
+ nlcpath,
+ sizeof(nlcpath),
+ "%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S
+ "%.16llx.local.conf",
+ _homePath.c_str(),
+ net_id);
+ OSUtils::rm(nlcpath);
}
}
}
- }
-
- return true;
- }
-
- bool _trialBind(unsigned int port)
- {
- struct sockaddr_in in4;
- struct sockaddr_in6 in6;
- PhySocket* tb;
-
- memset(&in4, 0, sizeof(in4));
- in4.sin_family = AF_INET;
- in4.sin_port = Utils::hton((uint16_t)port);
- tb = _phy.udpBind(reinterpret_cast(&in4), (void*)0, 0);
- if (tb) {
- _phy.close(tb, false);
- tb = _phy.tcpListen(reinterpret_cast(&in4), (void*)0);
- if (tb) {
- _phy.close(tb, false);
- return true;
+ else {
+ _nets.erase(net_id);
}
- }
-
- memset(&in6, 0, sizeof(in6));
- in6.sin6_family = AF_INET6;
- in6.sin6_port = Utils::hton((uint16_t)port);
- tb = _phy.udpBind(reinterpret_cast(&in6), (void*)0, 0);
- if (tb) {
- _phy.close(tb, false);
- tb = _phy.tcpListen(reinterpret_cast(&in6), (void*)0);
- if (tb) {
- _phy.close(tb, false);
- return true;
- }
- }
-
- return false;
+ break;
}
-};
-
-static int SnodeVirtualNetworkConfigFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t nwid,
- void** nuptr,
- enum ZT_VirtualNetworkConfigOperation op,
- const ZT_VirtualNetworkConfig* nwconf)
-{
- return reinterpret_cast(uptr)
- ->nodeVirtualNetworkConfigFunction(nwid, nuptr, op, nwconf);
+ return 0;
}
-static void
-SnodeEventCallback(ZT_Node* node, void* uptr, void* tptr, enum ZT_Event event, const void* metaData)
+void NodeService::nodeEventCallback(enum ZT_Event event, const void* metaData)
{
- reinterpret_cast(uptr)->nodeEventCallback(event, metaData);
+ zts_node_info_t* nd = new zts_node_info_t;
+ nd->node_id = _node ? _node->address() : 0x0;
+ nd->ver_major = ZEROTIER_ONE_VERSION_MAJOR;
+ nd->ver_minor = ZEROTIER_ONE_VERSION_MINOR;
+ nd->ver_rev = ZEROTIER_ONE_VERSION_REVISION;
+ nd->port_primary = _primaryPort;
+ nd->port_secondary = _secondaryPort;
+ nd->port_tertiary = _tertiaryPort;
+
+ int event_code = 0;
+ _nodeIsOnline = (event == ZT_EVENT_ONLINE) ? true : false;
+
+ switch (event) {
+ case ZT_EVENT_UP:
+ event_code = ZTS_EVENT_NODE_UP;
+ break;
+ case ZT_EVENT_ONLINE:
+ event_code = ZTS_EVENT_NODE_ONLINE;
+ break;
+ case ZT_EVENT_OFFLINE:
+ event_code = ZTS_EVENT_NODE_OFFLINE;
+ break;
+ case ZT_EVENT_DOWN:
+ event_code = ZTS_EVENT_NODE_DOWN;
+ break;
+ case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: {
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_IDENTITY_COLLISION;
+ event_code = ZTS_EVENT_NODE_FATAL_ERROR;
+ this->terminate();
+ } break;
+ default:
+ break;
+ }
+ if (event_code) {
+ _events->enqueue(event_code, (void*)nd);
+ }
}
-static void SnodeStatePutFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
+zts_net_info_t* NodeService::prepare_network_details_msg(const NetworkState& n)
+{
+ zts_net_info_t* nd = new zts_net_info_t();
+
+ nd->net_id = n.config.nwid;
+ nd->mac = n.config.mac;
+ memcpy(nd->name, n.config.name, sizeof(n.config.name));
+ nd->status = (zts_network_status_t)n.config.status;
+ nd->type = (zts_net_info_type_t)n.config.type;
+ nd->mtu = n.config.mtu;
+ nd->dhcp = n.config.dhcp;
+ nd->bridge = n.config.bridge;
+ nd->broadcast_enabled = n.config.broadcastEnabled;
+ nd->port_error = n.config.portError;
+ nd->netconf_rev = n.config.netconfRevision;
+
+ // Copy and convert address structures
+ nd->assigned_addr_count = n.config.assignedAddressCount;
+ for (unsigned int i = 0; i < n.config.assignedAddressCount; i++) {
+ native_ss_to_zts_ss(&(nd->assigned_addrs[i]), &(n.config.assignedAddresses[i]));
+ }
+
+ nd->route_count = n.config.routeCount;
+ for (unsigned int i = 0; i < n.config.routeCount; i++) {
+ native_ss_to_zts_ss(&(nd->routes[i].target), &(n.config.routes[i].target));
+ native_ss_to_zts_ss(&(nd->routes[i].via), &(n.config.routes[i].via));
+ nd->routes[i].flags = n.config.routes[i].flags;
+ nd->routes[i].metric = n.config.routes[i].metric;
+ }
+
+ nd->multicast_sub_count = n.config.multicastSubscriptionCount;
+ memcpy(
+ nd->multicast_subs,
+ &(n.config.multicastSubscriptions),
+ sizeof(n.config.multicastSubscriptions));
+
+ return nd;
+}
+
+void NodeService::generateEventMsgs()
+{
+ // Force the ordering of callback messages, these messages are
+ // only useful if the node and stack are both up and running
+ if (! _node->online() || ! _lwip_is_up()) {
+ return;
+ }
+ // Generate messages to be dequeued by the callback message thread
+ Mutex::Lock _l(_nets_m);
+ for (std::map::iterator n(_nets.begin()); n != _nets.end(); ++n) {
+ auto netState = n->second;
+ int mostRecentStatus = netState.config.status;
+ VirtualTap* tap = netState.tap;
+ // uint64_t net_id = n->first;
+ if (netState.tap->_networkStatus == mostRecentStatus) {
+ continue; // No state change
+ }
+ switch (mostRecentStatus) {
+ case ZT_NETWORK_STATUS_NOT_FOUND:
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_NOT_FOUND,
+ (void*)prepare_network_details_msg(netState));
+ break;
+ case ZT_NETWORK_STATUS_CLIENT_TOO_OLD:
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_CLIENT_TOO_OLD,
+ (void*)prepare_network_details_msg(netState));
+ break;
+ case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION:
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_REQ_CONFIG,
+ (void*)prepare_network_details_msg(netState));
+ break;
+ case ZT_NETWORK_STATUS_OK:
+ if (tap->hasIpv4Addr() && _lwip_is_netif_up(tap->netif4)) {
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_READY_IP4,
+ (void*)prepare_network_details_msg(netState));
+ }
+ if (tap->hasIpv6Addr() && _lwip_is_netif_up(tap->netif6)) {
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_READY_IP6,
+ (void*)prepare_network_details_msg(netState));
+ }
+ // In addition to the READY messages, send one OK message
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_OK,
+ (void*)prepare_network_details_msg(netState));
+ break;
+ case ZT_NETWORK_STATUS_ACCESS_DENIED:
+ _events->enqueue(
+ ZTS_EVENT_NETWORK_ACCESS_DENIED,
+ (void*)prepare_network_details_msg(netState));
+ break;
+ default:
+ break;
+ }
+ netState.tap->_networkStatus = mostRecentStatus;
+ }
+ bool bShouldCopyPeerInfo = false;
+ int event_code = 0;
+ ZT_PeerList* pl = _node->peers();
+ zts_peer_info_t* pd;
+ if (pl) {
+ for (unsigned long i = 0; i < pl->peerCount; ++i) {
+ if (! peerCache.count(pl->peers[i].address)) {
+ // New peer, add status
+ if (pl->peers[i].pathCount > 0) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_DIRECT;
+ }
+ if (pl->peers[i].pathCount == 0) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_RELAY;
+ }
+ }
+ // Previously known peer, update status
+ else {
+ if (peerCache[pl->peers[i].address] < pl->peers[i].pathCount) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_PATH_DISCOVERED;
+ }
+ if (peerCache[pl->peers[i].address] > pl->peers[i].pathCount) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_PATH_DEAD;
+ }
+ if (peerCache[pl->peers[i].address] == 0 && pl->peers[i].pathCount > 0) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_DIRECT;
+ }
+ if (peerCache[pl->peers[i].address] > 0 && pl->peers[i].pathCount == 0) {
+ bShouldCopyPeerInfo = true;
+ event_code = ZTS_EVENT_PEER_RELAY;
+ }
+ }
+ if (bShouldCopyPeerInfo) {
+ pd = new zts_peer_info_t();
+ memcpy(pd, &(pl->peers[i]), sizeof(zts_peer_info_t));
+ for (unsigned int j = 0; j < pl->peers[i].pathCount; j++) {
+ native_ss_to_zts_ss(&(pd->paths[j].address), &(pl->peers[i].paths[j].address));
+ }
+ _events->enqueue(event_code, (void*)pd);
+ bShouldCopyPeerInfo = false;
+ }
+ // Update our cache with most recently observed path count
+ peerCache[pl->peers[i].address] = pl->peers[i].pathCount;
+ }
+ }
+ _node->freeQueryResult((void*)pl);
+}
+
+int NodeService::join(uint64_t net_id)
+{
+ if (! net_id) {
+ return ZTS_ERR_ARG;
+ }
+ _node->join(net_id, NULL, NULL);
+ return ZTS_ERR_OK;
+}
+
+int NodeService::leave(uint64_t net_id)
+{
+ if (! net_id) {
+ return ZTS_ERR_ARG;
+ }
+ _node->leave(net_id, NULL, NULL);
+ return ZTS_ERR_OK;
+}
+
+int NodeService::networkCount()
+{
+ Mutex::Lock _l(_nets_m);
+ return _nets.size();
+}
+
+int NodeService::getFirstAssignedAddr(
+ uint64_t net_id,
+ int family,
+ struct zts_sockaddr_storage* addr)
+{
+ if (net_id == 0 || family < 0 || ! addr) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _l(_nets_m);
+ std::map::const_iterator n(_nets.find(net_id));
+ if (n == _nets.end()) {
+ return ZTS_ERR_NO_RESULT;
+ }
+ auto netState = n->second;
+ if (netState.config.assignedAddressCount == 0) {
+ return ZTS_ERR_NO_RESULT;
+ }
+ for (unsigned int i = 0; i < netState.config.assignedAddressCount; i++) {
+ struct sockaddr* sa = (struct sockaddr*)&(netState.config.assignedAddresses[i]);
+ // Family values may vary across platforms, thus the following
+ if (sa->sa_family == AF_INET && family == ZTS_AF_INET) {
+ native_ss_to_zts_ss(addr, &(netState.config.assignedAddresses[i]));
+ return ZTS_ERR_OK;
+ }
+ if (sa->sa_family == AF_INET6 && family == ZTS_AF_INET6) {
+ native_ss_to_zts_ss(addr, &(netState.config.assignedAddresses[i]));
+ return ZTS_ERR_OK;
+ }
+ }
+ return ZTS_ERR_NO_RESULT;
+}
+
+int NodeService::getAllAssignedAddr(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count)
+{
+ if (net_id == 0 || ! addr || ! count || *count != ZTS_MAX_ASSIGNED_ADDRESSES) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _l(_nets_m);
+ std::map::const_iterator n(_nets.find(net_id));
+ if (n == _nets.end()) {
+ return ZTS_ERR_NO_RESULT;
+ }
+ memset(addr, 0, sizeof(struct zts_sockaddr_storage) * ZTS_MAX_ASSIGNED_ADDRESSES);
+ auto netState = n->second;
+ if (netState.config.assignedAddressCount == 0) {
+ return ZTS_ERR_NO_RESULT;
+ }
+ for (unsigned int i = 0; i < netState.config.assignedAddressCount; i++) {
+ native_ss_to_zts_ss(&addr[i], &(netState.config.assignedAddresses[i]));
+ }
+ *count = netState.config.assignedAddressCount;
+ return ZTS_ERR_OK;
+}
+
+int NodeService::addrIsAssigned(uint64_t net_id, int family)
+{
+ if (net_id == 0) {
+ return ZTS_ERR_ARG;
+ }
+ struct zts_sockaddr_storage addr; // unused
+ return getFirstAssignedAddr(net_id, family, &addr) != ZTS_ERR_NO_RESULT;
+}
+
+int NodeService::networkHasRoute(uint64_t net_id, int family)
+{
+ Mutex::Lock _l(_nets_m);
+ std::map::const_iterator n(_nets.find(net_id));
+ if (n == _nets.end()) {
+ return ZTS_ERR_NO_RESULT;
+ }
+ auto netState = n->second;
+ for (unsigned int i = 0; i < netState.config.routeCount; i++) {
+ struct sockaddr* sa = (struct sockaddr*)&(netState.config.routes[i].target);
+ if (sa->sa_family == AF_INET && family == ZTS_AF_INET) {
+ return true;
+ }
+ if (sa->sa_family == AF_INET6 && family == ZTS_AF_INET6) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int NodeService::orbit(void* tptr, uint64_t moon_world_id, uint64_t moon_seed)
+{
+ if (! moon_world_id || ! moon_seed) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (! _run) {
+ return ZTS_ERR_SERVICE;
+ }
+ return _node->orbit(NULL, moon_world_id, moon_seed);
+}
+
+int NodeService::deorbit(void* tptr, uint64_t moon_world_id)
+{
+ if (! moon_world_id) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (! _run) {
+ return ZTS_ERR_SERVICE;
+ }
+ return _node->deorbit(NULL, moon_world_id);
+}
+
+uint64_t NodeService::getNodeId()
+{
+ Mutex::Lock _lr(_run_m);
+ if (! _run) {
+ return 0x0;
+ }
+ return _node ? _node->address() : 0x0;
+}
+
+int NodeService::setIdentity(const char* keypair, uint16_t len)
+{
+ if (keypair == NULL || len < ZT_IDENTITY_STRING_BUFFER_LENGTH) {
+ return ZTS_ERR_ARG;
+ }
+ // Double check user-provided keypair
+ Identity id;
+ if ((strlen(keypair) > 32) && (keypair[10] == ':')) {
+ if (! id.fromString(keypair)) {
+ return id.locallyValidate();
+ }
+ }
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ Mutex::Lock _ls(_store_m);
+ memcpy(_secretIdStr, keypair, len);
+ return ZTS_ERR_OK;
+}
+
+int NodeService::getIdentity(char* keypair, uint16_t* len)
+{
+ if (keypair == NULL || *len < ZT_IDENTITY_STRING_BUFFER_LENGTH) {
+ return ZTS_ERR_ARG;
+ }
+ if (_node) {
+ _node->identity().toString(true, keypair);
+ }
+ return ZTS_ERR_OK;
+}
+
+void NodeService::nodeStatePutFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
const void* data,
int len)
{
- reinterpret_cast(uptr)->nodeStatePutFunction(type, id, data, len);
+ char p[1024] = { 0 };
+ FILE* f;
+ bool secure = false;
+ char dirname[1024] = { 0 };
+ dirname[0] = 0;
+
+ Mutex::Lock _ls(_store_m);
+
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ _events->enqueue(ZTS_EVENT_STORE_IDENTITY_PUBLIC, data, len);
+ memcpy(_publicIdStr, data, len);
+ if (_homePath.length() > 0) {
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "identity.public",
+ _homePath.c_str());
+ }
+ else {
+ return;
+ }
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ _events->enqueue(ZTS_EVENT_STORE_IDENTITY_SECRET, data, len);
+ memcpy(_secretIdStr, data, len);
+ if (_homePath.length() > 0) {
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "identity.secret",
+ _homePath.c_str());
+ secure = true;
+ }
+ else {
+ return;
+ }
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ _events->enqueue(ZTS_EVENT_STORE_PLANET, data, len);
+ memcpy(_planetData, data, len);
+ if (_homePath.length() > 0) {
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "planet",
+ _homePath.c_str());
+ }
+ else {
+ return;
+ }
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ if (_homePath.length() > 0 && _allowNetworkCaching) {
+ OSUtils::ztsnprintf(
+ dirname,
+ sizeof(dirname),
+ "%s" ZT_PATH_SEPARATOR_S "networks.d",
+ _homePath.c_str());
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",
+ dirname,
+ (unsigned long long)id[0]);
+ secure = true;
+ }
+ else {
+ return;
+ }
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ if (_homePath.length() > 0 && _allowPeerCaching) {
+ OSUtils::ztsnprintf(
+ dirname,
+ sizeof(dirname),
+ "%s" ZT_PATH_SEPARATOR_S "peers.d",
+ _homePath.c_str());
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",
+ dirname,
+ (unsigned long long)id[0]);
+ }
+ else {
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+
+ if (len >= 0) {
+ // Check to see if we've already written this first. This reduces
+ // redundant writes and I/O overhead on most platforms and has
+ // little effect on others.
+ f = fopen(p, "rb");
+ if (f) {
+ char buf[65535] = { 0 };
+ long l = (long)fread(buf, 1, sizeof(buf), f);
+ fclose(f);
+ if ((l == (long)len) && (memcmp(data, buf, l) == 0)) {
+ return;
+ }
+ }
+
+ f = fopen(p, "wb");
+ if ((! f) && (dirname[0])) { // create subdirectory if it does not exist
+ OSUtils::mkdir(dirname);
+ f = fopen(p, "wb");
+ }
+ if (f) {
+ if (fwrite(data, len, 1, f) != 1)
+ fprintf(
+ stderr,
+ "WARNING: unable to write to file: %s (I/O "
+ "error)" ZT_EOL_S,
+ p);
+ fclose(f);
+ if (secure) {
+ OSUtils::lockDownFile(p, false);
+ }
+ }
+ else {
+ fprintf(
+ stderr,
+ "WARNING: unable to write to file: %s (unable to "
+ "open)" ZT_EOL_S,
+ p);
+ }
+ }
+ else {
+ OSUtils::rm(p);
+ }
}
-static int SnodeStateGetFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
+int NodeService::nodeStateGetFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
void* data,
unsigned int maxlen)
{
- return reinterpret_cast(uptr)->nodeStateGetFunction(type, id, data, maxlen);
+ char p[4096] = { 0 };
+ unsigned int keylen = 0;
+ switch (type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ keylen = strlen(_publicIdStr);
+ if (keylen > 0 && keylen <= maxlen) {
+ memcpy(data, _publicIdStr, keylen);
+ return keylen;
+ }
+ if (_homePath.length() > 0) {
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "identity.public",
+ _homePath.c_str());
+ }
+ /*
+ if (_shouldGenerateNewId) {
+
+ }
+ */
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ keylen = strlen(_secretIdStr);
+ if (keylen > 0 && keylen <= maxlen) {
+ memcpy(data, _secretIdStr, keylen);
+ return keylen;
+ }
+ if (_homePath.length() > 0) {
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "identity.secret",
+ _homePath.c_str());
+ }
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "planet", _homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",
+ _homePath.c_str(),
+ (unsigned long long)id[0]);
+ break;
+ case ZT_STATE_OBJECT_PEER:
+ OSUtils::ztsnprintf(
+ p,
+ sizeof(p),
+ "%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",
+ _homePath.c_str(),
+ (unsigned long long)id[0]);
+ break;
+ default:
+ return -1;
+ }
+ FILE* f = fopen(p, "rb");
+ if (f) {
+ int n = (int)fread(data, 1, maxlen, f);
+ fclose(f);
+ if (n >= 0) {
+ return n;
+ }
+ }
+ return -1;
}
-static int SnodeWirePacketSendFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- int64_t localSocket,
+int NodeService::nodeWirePacketSendFunction(
+ const int64_t localSocket,
const struct sockaddr_storage* addr,
const void* data,
unsigned int len,
unsigned int ttl)
{
- return reinterpret_cast(uptr)
- ->nodeWirePacketSendFunction(localSocket, addr, data, len, ttl);
+ // Even when relaying we still send via UDP. This way if UDP starts
+ // working we can instantly "fail forward" to it and stop using TCP
+ // proxy fallback, which is slow.
+
+ if ((localSocket != -1) && (localSocket != 0)
+ && (_binder.isUdpSocketValid((PhySocket*)((uintptr_t)localSocket)))) {
+ if ((ttl) && (addr->ss_family == AF_INET))
+ _phy.setIp4UdpTtl((PhySocket*)((uintptr_t)localSocket), ttl);
+ const bool r = _phy.udpSend(
+ (PhySocket*)((uintptr_t)localSocket),
+ (const struct sockaddr*)addr,
+ data,
+ len);
+ if ((ttl) && (addr->ss_family == AF_INET))
+ _phy.setIp4UdpTtl((PhySocket*)((uintptr_t)localSocket), 255);
+ return ((r) ? 0 : -1);
+ }
+ else {
+ return ((_binder.udpSendAll(_phy, addr, data, len, ttl)) ? 0 : -1);
+ }
}
-static void SnodeVirtualNetworkFrameFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
- uint64_t nwid,
+void NodeService::nodeVirtualNetworkFrameFunction(
+ uint64_t net_id,
void** nuptr,
uint64_t sourceMac,
uint64_t destMac,
@@ -1720,46 +1275,106 @@ static void SnodeVirtualNetworkFrameFunction(
const void* data,
unsigned int len)
{
- reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(
- nwid,
- nuptr,
- sourceMac,
- destMac,
- etherType,
- vlanId,
- data,
- len);
+ NetworkState* n = reinterpret_cast(*nuptr);
+ if ((! n) || (! n->tap)) {
+ return;
+ }
+ n->tap->put(MAC(sourceMac), MAC(destMac), etherType, data, len);
}
-static int SnodePathCheckFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
+int NodeService::nodePathCheckFunction(
uint64_t ztaddr,
- int64_t localSocket,
+ const int64_t localSocket,
const struct sockaddr_storage* remoteAddr)
{
- return reinterpret_cast(uptr)->nodePathCheckFunction(
- ztaddr,
- localSocket,
- remoteAddr);
+ // Make sure we're not trying to do ZeroTier-over-ZeroTier
+ {
+ Mutex::Lock _l(_nets_m);
+ for (std::map::const_iterator n(_nets.begin()); n != _nets.end();
+ ++n) {
+ if (n->second.tap) {
+ std::vector ips(n->second.tap->ips());
+ for (std::vector::const_iterator i(ips.begin()); i != ips.end(); ++i) {
+ if (i->containsAddress(*(reinterpret_cast(remoteAddr)))) {
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ /* Note: I do not think we need to scan for overlap with managed routes
+ * because of the "route forking" and interface binding that we do. This
+ * ensures (we hope) that ZeroTier traffic will still take the physical
+ * path even if its managed routes this for other traffic. Will
+ * revisit if we see recursion problems. */
+
+ // Check blacklists
+ const Hashtable >* blh =
+ (const Hashtable >*)0;
+ const std::vector* gbl = (const std::vector*)0;
+ if (remoteAddr->ss_family == AF_INET) {
+ blh = &_v4Blacklists;
+ gbl = &_globalV4Blacklist;
+ }
+ else if (remoteAddr->ss_family == AF_INET6) {
+ blh = &_v6Blacklists;
+ gbl = &_globalV6Blacklist;
+ }
+ if (blh) {
+ Mutex::Lock _l(_localConfig_m);
+ const std::vector* l = blh->get(ztaddr);
+ if (l) {
+ for (std::vector::const_iterator a(l->begin()); a != l->end(); ++a) {
+ if (a->containsAddress(*reinterpret_cast(remoteAddr))) {
+ return 0;
+ }
+ }
+ }
+ }
+ if (gbl) {
+ for (std::vector::const_iterator a(gbl->begin()); a != gbl->end(); ++a) {
+ if (a->containsAddress(*reinterpret_cast(remoteAddr)))
+ return 0;
+ }
+ }
+ return 1;
}
-static int SnodePathLookupFunction(
- ZT_Node* node,
- void* uptr,
- void* tptr,
+int NodeService::nodePathLookupFunction(
uint64_t ztaddr,
int family,
struct sockaddr_storage* result)
{
- return reinterpret_cast(uptr)->nodePathLookupFunction(ztaddr, family, result);
+ const Hashtable >* lh =
+ (const Hashtable >*)0;
+ if (family < 0) {
+ lh = (_node->prng() & 1) ? &_v4Hints : &_v6Hints;
+ }
+ else if (family == AF_INET) {
+ lh = &_v4Hints;
+ }
+ else if (family == AF_INET6) {
+ lh = &_v6Hints;
+ }
+ else {
+ return 0;
+ }
+ const std::vector* l = lh->get(ztaddr);
+ if ((l) && (l->size() > 0)) {
+ memcpy(
+ result,
+ &((*l)[(unsigned long)_node->prng() % l->size()]),
+ sizeof(struct sockaddr_storage));
+ return 1;
+ }
+ else {
+ return 0;
+ }
}
-static void StapFrameHandler(
- void* uptr,
- void* tptr,
- uint64_t nwid,
+void NodeService::tapFrameHandler(
+ uint64_t net_id,
const MAC& from,
const MAC& to,
unsigned int etherType,
@@ -1767,133 +1382,320 @@ static void StapFrameHandler(
const void* data,
unsigned int len)
{
- reinterpret_cast(uptr)
- ->tapFrameHandler(nwid, from, to, etherType, vlanId, data, len);
+ _node->processVirtualNetworkFrame(
+ (void*)0,
+ OSUtils::now(),
+ net_id,
+ from.toInt(),
+ to.toInt(),
+ etherType,
+ vlanId,
+ data,
+ len,
+ &_nextBackgroundTaskDeadline);
}
-std::string NodeService::platformDefaultHomePath()
+int NodeService::shouldBindInterface(const char* ifname, const InetAddress& ifaddr)
{
- return OSUtils::platformDefaultHomePath();
-}
-
-NodeService* NodeService::newInstance(const char* hp, unsigned int port)
-{
- return new NodeServiceImpl(hp, port);
-}
-NodeService::~NodeService()
-{
-}
-
-//////////////////////////////////////////////////////////////////////////////
-// Service //
-//////////////////////////////////////////////////////////////////////////////
-
-NodeService* service;
-
-// Lock to guard access to ZeroTier core service
-Mutex serviceLock;
-
-// Starts a ZeroTier NodeService background thread
-#if defined(__WINDOWS__)
-DWORD WINAPI _runNodeService(LPVOID arg)
-#else
-void* _runNodeService(void* arg)
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ if ((ifname[0] == 'l') && (ifname[1] == 'o')) {
+ return false; // loopback
+ }
+ if ((ifname[0] == 'z') && (ifname[1] == 't')) {
+ return false; // sanity check: zt#
+ }
+ if ((ifname[0] == 't') && (ifname[1] == 'u') && (ifname[2] == 'n')) {
+ return false; // tun# is probably an OpenVPN tunnel or similar
+ }
+ if ((ifname[0] == 't') && (ifname[1] == 'a') && (ifname[2] == 'p')) {
+ return false; // tap# is probably an OpenVPN tunnel or similar
+ }
#endif
-{
-#if defined(__APPLE__)
- pthread_setname_np(ZTS_SERVICE_THREAD_NAME);
+
+#ifdef __APPLE__
+ if ((ifname[0] == 'f') && (ifname[1] == 'e') && (ifname[2] == 't') && (ifname[3] == 'h')) {
+ return false; // ... as is feth#
+ }
+ if ((ifname[0] == 'l') && (ifname[1] == 'o')) {
+ return false; // loopback
+ }
+ if ((ifname[0] == 'z') && (ifname[1] == 't')) {
+ return false; // sanity check: zt#
+ }
+ if ((ifname[0] == 't') && (ifname[1] == 'u') && (ifname[2] == 'n')) {
+ return false; // tun# is probably an OpenVPN tunnel or similar
+ }
+ if ((ifname[0] == 't') && (ifname[1] == 'a') && (ifname[2] == 'p')) {
+ return false; // tap# is probably an OpenVPN tunnel or similar
+ }
+ if ((ifname[0] == 'u') && (ifname[1] == 't') && (ifname[2] == 'u') && (ifname[3] == 'n')) {
+ return false; // ... as is utun#
+ }
#endif
- struct serviceParameters* params = (struct serviceParameters*)arg;
- int err;
- try {
- std::vector hpsp(
- OSUtils::split(params->path.c_str(), ZT_PATH_SEPARATOR_S, "", ""));
- std::string ptmp;
- if (params->path[0] == ZT_PATH_SEPARATOR) {
- ptmp.push_back(ZT_PATH_SEPARATOR);
- }
- for (std::vector::iterator pi(hpsp.begin()); pi != hpsp.end(); ++pi) {
- if (ptmp.length() > 0) {
- ptmp.push_back(ZT_PATH_SEPARATOR);
+
+ {
+ Mutex::Lock _l(_localConfig_m);
+ for (std::vector::const_iterator p(_interfacePrefixBlacklist.begin());
+ p != _interfacePrefixBlacklist.end();
+ ++p) {
+ if (! strncmp(p->c_str(), ifname, p->length())) {
+ return false;
}
- ptmp.append(*pi);
- if ((*pi != ".") && (*pi != "..")) {
- if (OSUtils::mkdir(ptmp) == false) {
- DEBUG_ERROR("home path does not exist, and could not create");
- err = true;
- perror("error\n");
+ }
+ }
+ {
+ // Check global blacklists
+ const std::vector* gbl = (const std::vector*)0;
+ if (ifaddr.ss_family == AF_INET) {
+ gbl = &_globalV4Blacklist;
+ }
+ else if (ifaddr.ss_family == AF_INET6) {
+ gbl = &_globalV6Blacklist;
+ }
+ if (gbl) {
+ Mutex::Lock _l(_localConfig_m);
+ for (std::vector::const_iterator a(gbl->begin()); a != gbl->end(); ++a) {
+ if (a->containsAddress(ifaddr)) {
+ return false;
}
}
}
- for (;;) {
- serviceLock.lock();
- service = NodeService::newInstance(params->path.c_str(), params->port);
- service->_userProvidedPort = params->port;
- service->_userProvidedPath = params->path;
- if (params->publicIdentityStr[0] != '\0' && params->secretIdentityStr[0] != '\0'
- && params->path.length() == 0) {
- memcpy(
- service->_userProvidedPublicIdentity,
- params->publicIdentityStr,
- strlen(params->publicIdentityStr));
- memcpy(
- service->_userProvidedSecretIdentity,
- params->secretIdentityStr,
- strlen(params->secretIdentityStr));
- }
-
- serviceLock.unlock();
- switch (service->run()) {
- case NodeService::ONE_STILL_RUNNING:
- case NodeService::ONE_NORMAL_TERMINATION:
- _enqueueEvent(ZTS_EVENT_NODE_NORMAL_TERMINATION, NULL);
- break;
- case NodeService::ONE_UNRECOVERABLE_ERROR:
- DEBUG_ERROR("fatal error: %s", service->fatalErrorMessage().c_str());
- err = true;
- _enqueueEvent(ZTS_EVENT_NODE_UNRECOVERABLE_ERROR, NULL);
- break;
- case NodeService::ONE_IDENTITY_COLLISION: {
- err = true;
- delete service;
- service = (NodeService*)0;
- std::string oldid;
- OSUtils::readFile(
- (params->path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),
- oldid);
- if (oldid.length()) {
- OSUtils::writeFile(
- (params->path + ZT_PATH_SEPARATOR_S
- + "identity.secret.saved_after_collision")
- .c_str(),
- oldid);
- OSUtils::rm(
- (params->path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str());
- OSUtils::rm(
- (params->path + ZT_PATH_SEPARATOR_S + "identity.public").c_str());
+ }
+ {
+ Mutex::Lock _l(_nets_m);
+ for (std::map::const_iterator n(_nets.begin()); n != _nets.end();
+ ++n) {
+ if (n->second.tap) {
+ std::vector ips(n->second.tap->ips());
+ for (std::vector::const_iterator i(ips.begin()); i != ips.end(); ++i) {
+ if (i->ipsEqual(ifaddr)) {
+ return false;
}
- _enqueueEvent(ZTS_EVENT_NODE_IDENTITY_COLLISION, NULL);
}
- continue; // restart!
}
- break; // terminate loop -- normally we don't keep restarting
}
- serviceLock.lock();
- _clrState(ZTS_STATE_NODE_RUNNING);
- delete service;
- service = (NodeService*)0;
- serviceLock.unlock();
- _enqueueEvent(ZTS_EVENT_NODE_DOWN, NULL);
}
- catch (...) {
- DEBUG_ERROR("unexpected exception starting ZeroTier instance");
+ return true;
+}
+
+int NodeService::_trialBind(unsigned int port)
+{
+ struct sockaddr_in in4;
+ struct sockaddr_in6 in6;
+ PhySocket* tb;
+
+ memset(&in4, 0, sizeof(in4));
+ in4.sin_family = AF_INET;
+ in4.sin_port = Utils::hton((uint16_t)port);
+ tb = _phy.udpBind(reinterpret_cast(&in4), (void*)0, 0);
+ if (tb) {
+ _phy.close(tb, false);
+ return true;
}
- delete params;
- zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL * 2);
-#ifndef __WINDOWS__
- pthread_exit(0);
-#endif
- return NULL;
+
+ memset(&in6, 0, sizeof(in6));
+ in6.sin6_family = AF_INET6;
+ in6.sin6_port = Utils::hton((uint16_t)port);
+ tb = _phy.udpBind(reinterpret_cast(&in6), (void*)0, 0);
+ if (tb) {
+ _phy.close(tb, false);
+ return true;
+ }
+ return false;
+}
+
+int NodeService::isRunning()
+{
+ return _run;
+}
+
+int NodeService::nodeIsOnline()
+{
+ return _nodeIsOnline;
+}
+
+int NodeService::setHomePath(const char* homePath)
+{
+ if (! homePath) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ _homePath = std::string(homePath);
+ return ZTS_ERR_OK;
+}
+
+int NodeService::setPrimaryPort(unsigned short primaryPort)
+{
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ _primaryPort = primaryPort;
+ return ZTS_ERR_OK;
+}
+
+unsigned short NodeService::getPrimaryPort()
+{
+ return _primaryPort;
+}
+
+int NodeService::setUserEventSystem(Events* events)
+{
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ _events = events;
+ return ZTS_ERR_OK;
+}
+
+int NodeService::setPlanet(const char* planetData, int len)
+{
+ if (! planetData || len <= 0 || len > ZTS_STORE_DATA_LEN) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ Mutex::Lock _ls(_store_m);
+ memcpy(_planetData, planetData, len);
+ return ZTS_ERR_OK;
+}
+
+int NodeService::addInterfacePrefixToBlacklist(const char* prefix, int len)
+{
+ if (! prefix || len == 0 || len > 15) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (_run) {
+ return ZTS_ERR_SERVICE;
+ }
+ Mutex::Lock _l(_localConfig_m);
+ _interfacePrefixBlacklist.push_back(std::string(prefix));
+ return ZTS_ERR_OK;
+}
+
+uint64_t NodeService::getMACAddress(uint64_t net_id)
+{
+ if (net_id == 0) {
+ return ZTS_ERR_ARG;
+ }
+ Mutex::Lock _lr(_run_m);
+ if (! _run) {
+ return ZTS_ERR_SERVICE;
+ }
+ Mutex::Lock _ln(_nets_m);
+ std::map