Expand C API and simplify NodeService

This commit is contained in:
Joseph Henry
2021-04-22 11:20:04 -07:00
parent 565b56d290
commit 43350691b5
30 changed files with 7697 additions and 5467 deletions

View File

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

View File

@@ -1,7 +1,7 @@
<div align="center"> <div align="center">
<h1>ZeroTier SDK</h1> <h1>ZeroTier SDK</h1>
<img alt="zts_socket()" src="https://i.imgur.com/BwSHwE3.png"> </img> <img alt="zts_socket()" src="https://i.imgur.com/BwSHwE3.png" class="doxyhidden"> </img>
Peer-to-peer and cross-platform encrypted connections built right into your app or service. No drivers, no root, and no host configuration. Peer-to-peer and cross-platform encrypted connections built right into your app or service. No drivers, no root, and no host configuration.

97
examples/c/adhoc.c Normal file
View File

@@ -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 <stdio.h>
#include <stdlib.h>
/*
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 <adhocStartPort> <adhocEndPort>\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();
}

113
examples/c/callbackapi.c Normal file
View File

@@ -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 <stdio.h>
#include <stdlib.h>
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 <net_id>\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();
}

109
examples/c/centralapi.cpp Normal file
View File

@@ -0,0 +1,109 @@
#include "ZeroTierSockets.h"
#include <iomanip>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
// 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 <central_url> <api_token>\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;
}

105
examples/c/client.c Normal file
View File

@@ -0,0 +1,105 @@
/**
* libzt C API example
*
* Simple socket-based client application
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc != 5) {
printf("\nlibzt example client\n");
printf("client <id_storage_path> <net_id> <remote_addr> <remote_port>\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();
}

View File

@@ -0,0 +1,96 @@
/**
* libzt C API example
*
* Simple socket-based client application
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc != 5) {
printf("\nlibzt example client\n");
printf("client <id_storage_path> <net_id> <remote_addr> <remote_port>\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();
}

View File

@@ -0,0 +1,169 @@
/**
* libzt C API example
*
* Simple socket-based server application
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc != 5) {
printf("\nlibzt example server\n");
printf("server <id_storage_path> <net_id> <local_addr> <local_port>\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();
}

109
examples/c/nostorage.c Normal file
View File

@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
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();
}

View File

@@ -0,0 +1,67 @@
/**
* libzt C API example
*
* Pingable node
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (argc != 2) {
printf("\nUsage:\n");
printf("pingable-node <net_id>\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();
}

111
examples/c/server.c Normal file
View File

@@ -0,0 +1,111 @@
/**
* libzt C API example
*
* Simple socket-based server application
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
if (argc != 5) {
printf("\nlibzt example server\n");
printf("server <id_storage_path> <net_id> <local_addr> <local_port>\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();
}

130
examples/c/statistics.c Normal file
View File

@@ -0,0 +1,130 @@
/**
* libzt C API example
*
* Pingable node that also displays protocol statistics that are
* useful for debugging.
*/
#include "ZeroTierSockets.h"
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
if (argc != 2) {
printf("\nUsage:\n");
printf("pingable-node <net_id>\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();
}

View File

@@ -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/`
<div style="page-break-after: always;"></div>
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.
<div style="page-break-after: always;"></div>
# 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)`.
<div style="page-break-after: always;"></div>
# 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.
<div style="page-break-after: always;"></div>
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
```
<div style="page-break-after: always;"></div>
## 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
```
<div style="page-break-after: always;"></div>
# 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`*
<div style="page-break-after: always;"></div>
# 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.
<div style="page-break-after: always;"></div>
# 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);
```
<div style="page-break-after: always;"></div>
# 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.*
<div style="page-break-after: always;"></div>
# 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_*`.
<div style="page-break-after: always;"></div>

File diff suppressed because it is too large Load Diff

View File

@@ -4,27 +4,27 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
*/ */
/****/ /****/
#ifndef ZT_CENTRAL_H #ifndef ZTS_DISABLE_CENTRAL_API
#define ZT_CENTRAL_H
#ifdef ZTS_ENABLE_CENTRAL_API #include "Debug.hpp"
#include "Mutex.hpp"
#include "OSUtils.hpp"
#include "ZeroTierSockets.h"
#include "Debug.hpp" #include <curl/curl.h>
#include "Mutex.hpp" #include <iomanip>
#include "ZeroTierSockets.h" #include <iostream>
#include <stdio.h>
#include <string.h>
#include <curl/curl.h> #define REQ_LEN 64
#include <iomanip>
#include <iostream>
#include <stdio.h>
#include <string.h>
char api_url[ZTS_CENRTAL_MAX_URL_LEN]; char api_url[ZTS_CENRTAL_MAX_URL_LEN];
char api_token[ZTS_CENTRAL_TOKEN_LEN + 1]; char api_token[ZTS_CENTRAL_TOKEN_LEN + 1];
@@ -41,31 +41,40 @@ using namespace ZeroTier;
Mutex _responseBuffer_m; Mutex _responseBuffer_m;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
size_t on_data(void* buffer, size_t size, size_t nmemb, void* userp) 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); DEBUG_INFO("buf=%p,size=%zu,nmemb=%zu,userp=%p", buffer, size, nmemb, userp);
int byte_count = (size * nmemb); int byte_count = (size * nmemb);
if (_resp_buf_offset + byte_count >= _resp_buf_len) { if (_resp_buf_offset + byte_count >= _resp_buf_len) {
DEBUG_ERROR("Out of buffer space. Cannot store response from server"); 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.) return 0; // Signal to libcurl that our buffer is full (triggers a
// write error.)
} }
memcpy(_resp_buf + _resp_buf_offset, buffer, byte_count); memcpy(_resp_buf + _resp_buf_offset, buffer, byte_count);
_resp_buf_offset += byte_count; _resp_buf_offset += byte_count;
return 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; _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; _bIsVerbose = is_verbose;
return ZTS_ERR_OK;
} }
void zts_central_clear_resp_buf() void zts_central_clear_resp_buf()
@@ -81,7 +90,7 @@ int zts_central_init(
char* resp_buf, char* resp_buf,
uint32_t resp_buf_len) 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 _bIsVerbose = 0; // Default disable libcurl verbose output
Mutex::Lock _l(_responseBuffer_m); Mutex::Lock _l(_responseBuffer_m);
if (resp_buf_len == 0) { if (resp_buf_len == 0) {
@@ -99,7 +108,7 @@ int zts_central_init(
} }
else { else {
memset(api_url, 0, ZTS_CENRTAL_MAX_URL_LEN); 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); int token_len = strlen(token_str);
if (token_len != ZTS_CENTRAL_TOKEN_LEN) { if (token_len != ZTS_CENTRAL_TOKEN_LEN) {
@@ -107,7 +116,7 @@ int zts_central_init(
} }
else { else {
memset(api_token, 0, ZTS_CENTRAL_TOKEN_LEN); memset(api_token, 0, ZTS_CENTRAL_TOKEN_LEN);
memcpy(api_token, token_str, token_len); strncpy(api_token, token_str, token_len);
} }
_bInit = true; _bInit = true;
return ZTS_ERR_OK; return ZTS_ERR_OK;
@@ -118,7 +127,7 @@ void zts_central_cleanup()
curl_global_cleanup(); curl_global_cleanup();
} }
int _central_req( int central_req(
int request_type, int request_type,
char* central_str, char* central_str,
char* api_route_str, char* api_route_str,
@@ -128,15 +137,17 @@ int _central_req(
{ {
int err = ZTS_ERR_OK; int err = ZTS_ERR_OK;
if (! _bInit) { 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; return ZTS_ERR_SERVICE;
} }
if (request_type == ZTS_HTTP_GET && ! (_access_modes & ZTS_CENTRAL_READ)) { 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; return ZTS_ERR_SERVICE;
} }
if (request_type == ZTS_HTTP_POST && ! (_access_modes & ZTS_CENTRAL_WRITE)) { 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; return ZTS_ERR_SERVICE;
} }
zts_central_clear_resp_buf(); zts_central_clear_resp_buf();
@@ -150,9 +161,9 @@ int _central_req(
if (url_len > ZTS_CENRTAL_MAX_URL_LEN) { if (url_len > ZTS_CENRTAL_MAX_URL_LEN) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
char req_url[ZTS_CENRTAL_MAX_URL_LEN]; char req_url[ZTS_CENRTAL_MAX_URL_LEN] = { 0 };
strcpy(req_url, central_str); strncpy(req_url, central_str, ZTS_CENRTAL_MAX_URL_LEN);
strcat(req_url, api_route_str); strncat(req_url, api_route_str, ZTS_CENRTAL_MAX_URL_LEN);
CURL* curl; CURL* curl;
CURLcode res; CURLcode res;
@@ -162,10 +173,13 @@ int _central_req(
} }
struct curl_slist* hs = NULL; 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) { if (token_strlen == ZTS_CENTRAL_TOKEN_LEN) {
memset(auth_str, 0, ZTS_CENTRAL_TOKEN_LEN + 32); OSUtils::ztsnprintf(
sprintf(auth_str, "Authorization: Bearer %s", token_str); auth_str,
ZTS_CENTRAL_TOKEN_LEN + 32,
"Authorization: Bearer %s",
token_str);
} }
hs = curl_slist_append(hs, auth_str); hs = curl_slist_append(hs, auth_str);
@@ -194,7 +208,8 @@ int _central_req(
if (request_type == ZTS_HTTP_DELETE) { if (request_type == ZTS_HTTP_DELETE) {
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "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 // Perform request
res = curl_easy_perform(curl); res = curl_easy_perform(curl);
if (res == CURLE_OK) { if (res == CURLE_OK) {
@@ -208,7 +223,7 @@ int _central_req(
// curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); // curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
} }
else { else {
DEBUG_ERROR("%s", curl_easy_strerror(res)); DEBUG_INFO("%s", curl_easy_strerror(res));
err = ZTS_ERR_SERVICE; err = ZTS_ERR_SERVICE;
} }
curl_easy_cleanup(curl); 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; 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]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx", nwid); OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
return _central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL); 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]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx", nwid); OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
return _central_req(ZTS_HTTP_POST, api_url, req, api_token, resp_code, NULL); 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]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx", nwid); OSUtils::ztsnprintf(req, REQ_LEN, "/api/network/%llx", net_id);
return _central_req(ZTS_HTTP_DELETE, api_url, req, api_token, resp_code, NULL); 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; return ZTS_ERR_ARG;
} }
char req[64]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx/member/%llx", nwid, nodeid); 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); 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; return ZTS_ERR_ARG;
} }
char req[64]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx/member/%llx", nwid, nodeid); 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); 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) { if (is_authed != 0 && is_authed != 1) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
char config_data[64]; char config_data[REQ_LEN] = { 0 };
if (is_authed == ZTS_CENTRAL_NODE_AUTH_TRUE) { 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) { 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]; char req[REQ_LEN] = { 0 };
sprintf(req, "/api/network/%llx/member", nwid); 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); return central_req(ZTS_HTTP_GET, api_url, req, api_token, resp_code, NULL);
} }
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif
#endif // ZTS_ENABLE_CENTRAL_API #endif // ZTS_DISABLE_CENTRAL_API
#endif // _H

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -17,215 +17,85 @@
* Debug macros * Debug macros
*/ */
#ifndef LIBZT_DEBUG_HPP #ifndef ZTS_DEBUG_HPP
#define LIBZT_DEBUG_HPP #define ZTS_DEBUG_HPP
//////////////////////////////////////////////////////////////////////////////
// Debugging Macros //
//////////////////////////////////////////////////////////////////////////////
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
#include <pthread.h> #include <unistd.h>
#include <sys/syscall.h>
#include <unistd.h>
#endif #endif
#include <string.h> #include <string.h>
#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 #define ZT_COLOR true
// Debug output colors // Debug output colors
#if defined(__APPLE__) #if defined(__APPLE__)
#include "TargetConditionals.h" #include "TargetConditionals.h"
#endif #endif
#if defined(ZT_COLOR) && ! defined(_WIN32) && ! defined(__ANDROID__) \ #if defined(ZT_COLOR) && ! defined(_WIN32) && ! defined(__ANDROID__) \
&& ! defined(TARGET_OS_IPHONE) && ! defined(TARGET_IPHONE_SIMULATOR) \ && ! defined(TARGET_OS_IPHONE) && ! defined(TARGET_IPHONE_SIMULATOR) \
&& ! defined(__APP_FRAMEWORK__) && ! defined(__APP_FRAMEWORK__)
#define ZT_RED "\x1B[31m" #define ZT_RED "\x1B[31m"
#define ZT_GRN "\x1B[32m" #define ZT_GRN "\x1B[32m"
#define ZT_YEL "\x1B[33m" #define ZT_YEL "\x1B[33m"
#define ZT_BLU "\x1B[34m" #define ZT_BLU "\x1B[34m"
#define ZT_MAG "\x1B[35m" #define ZT_MAG "\x1B[35m"
#define ZT_CYN "\x1B[36m" #define ZT_CYN "\x1B[36m"
#define ZT_WHT "\x1B[37m" #define ZT_WHT "\x1B[37m"
#define ZT_RESET "\x1B[0m" #define ZT_RESET "\x1B[0m"
#else #else
#define ZT_RED #define ZT_RED
#define ZT_GRN #define ZT_GRN
#define ZT_YEL #define ZT_YEL
#define ZT_BLU #define ZT_BLU
#define ZT_MAG #define ZT_MAG
#define ZT_CYN #define ZT_CYN
#define ZT_WHT #define ZT_WHT
#define ZT_RESET #define ZT_RESET
#endif #endif
#define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short #define ZT_FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) // short
#if defined(__JNI_LIB__)
#include <jni.h>
#endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <android/log.h> #include <android/log.h>
#define ZT_LOG_TAG "ZTSDK" #define ZT_LOG_TAG "ZTSDK"
#endif #endif
#if defined(LIBZT_DEBUG) || defined(LIBZT_TRACE) || defined(__NATIVETEST__) #if defined(LIBZT_DEBUG)
// #if defined(__ANDROID__)
#if ZT_MSG_ERROR == true #define DEBUG_INFO(fmt, args...) \
#if defined(__ANDROID__) ((void)__android_log_print( \
#define DEBUG_ERROR(fmt, args...) \ ANDROID_LOG_VERBOSE, \
((void)__android_log_print( \ ZT_LOG_TAG, \
ANDROID_LOG_VERBOSE, \ "%17s:%5d:%20s: " fmt "\n", \
ZT_LOG_TAG, \ ZT_FILENAME, \
"%17s:%5d:%20s: " fmt "\n", \ __LINE__, \
ZT_FILENAME, \ __FUNCTION__, \
__LINE__, \ ##args))
__FUNCTION__, \ #elif defined(_WIN32)
##args)) #define DEBUG_INFO(fmt, ...) \
#elif defined(_WIN32) fprintf( \
#define DEBUG_ERROR(fmt, ...) \ stderr, \
fprintf( \ ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
stderr, \ ZT_FILENAME, \
ZT_RED "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \ __LINE__, \
ZT_FILENAME, \ __FUNCTION__, \
__LINE__, \ __VA_ARGS__)
__FUNCTION__, \ #else
__VA_ARGS__) #define DEBUG_INFO(fmt, args...) \
#else fprintf( \
#define DEBUG_ERROR(fmt, args...) \ stderr, \
fprintf( \ ZT_WHT "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \
stderr, \ ZT_FILENAME, \
ZT_RED "%17s:%5d:%25s: " fmt "\n" ZT_RESET, \ __LINE__, \
ZT_FILENAME, \ __FUNCTION__, \
__LINE__, \ ##args)
__FUNCTION__, \ #endif
##args) #else // !LIBZT_DEBUG
#endif #if defined(_WIN32)
#else #define DEBUG_INFO(...)
#define DEBUG_ERROR(fmt, args...) #else
#endif #define DEBUG_INFO(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
#endif #endif
#endif // _H #endif // _H

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -14,49 +14,57 @@
/** /**
* @file * @file
* *
* Callback event processing logic * Callback event creation and distribution to user application
*/ */
#include "concurrentqueue.h" #include "Events.hpp"
#ifdef ZTS_ENABLE_JAVA
#include <jni.h>
#endif
#include "Constants.hpp" #include "Constants.hpp"
#include "Debug.hpp"
#include "Events.hpp"
#include "Node.hpp" #include "Node.hpp"
#include "NodeService.hpp" #include "NodeService.hpp"
#include "OSUtils.hpp" #include "OSUtils.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#include "concurrentqueue.h"
#define NODE_EVENT_TYPE(code) code >= ZTS_EVENT_NODE_UP&& code <= ZTS_EVENT_NODE_NORMAL_TERMINATION #ifdef ZTS_ENABLE_JAVA
#define NETWORK_EVENT_TYPE(code) \ #include <jni.h>
code >= ZTS_EVENT_NETWORK_NOT_FOUND&& code <= ZTS_EVENT_NETWORK_UPDATE #endif
#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_PYTHON #ifdef ZTS_ENABLE_PYTHON
#include "Python.h" #include "Python.h"
PythonDirectorCallbackClass* _userEventCallback = NULL; PythonDirectorCallbackClass* _userEventCallback = NULL;
void PythonDirectorCallbackClass::on_zerotier_event(struct zts_callback_msg* msg) void PythonDirectorCallbackClass::on_zerotier_event(zts_event_msg_t* msg)
{ {
} }
#endif #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 { namespace ZeroTier {
extern NodeService* service; extern NodeService* zts_service;
// Global state variable shared between Socket, Control, Event and NodeService logic. // Global state variable shared between Socket, Control, Event and
uint8_t _serviceStateFlags; // 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. // Lock to guard access to callback function pointers.
Mutex _callbackLock; Mutex events_m;
#ifdef ZTS_ENABLE_PINVOKE #ifdef ZTS_ENABLE_PINVOKE
void (*_userEventCallback)(void*); void (*_userEventCallback)(void*);
@@ -65,45 +73,74 @@ void (*_userEventCallback)(void*);
void (*_userEventCallback)(void*); void (*_userEventCallback)(void*);
#endif #endif
moodycamel::ConcurrentQueue<struct zts_callback_msg*> _callbackMsgQueue; moodycamel::ConcurrentQueue<zts_event_msg_t*> _callbackMsgQueue;
void _enqueueEvent(int16_t eventCode, void* arg) void Events::run()
{ {
struct zts_callback_msg* msg = new zts_callback_msg(); while (getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) {
msg->eventCode = eventCode; 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)) { void Events::enqueue(int16_t event_code, const void* arg, int len)
msg->node = (struct zts_node_details*)arg; {
if (! _enabled) {
return;
} }
if (NETWORK_EVENT_TYPE(eventCode)) { zts_event_msg_t* msg = new zts_event_msg_t();
msg->network = (struct zts_network_details*)arg; 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 */ /* nothing to convey to user */
} }
if (NETIF_EVENT_TYPE(eventCode)) { if (ZTS_NETIF_EVENT(event_code)) {
msg->netif = (struct zts_netif_details*)arg; msg->netif = (zts_netif_info_t*)arg;
msg->len = sizeof(zts_netif_info_t);
} }
if (ROUTE_EVENT_TYPE(eventCode)) { if (ZTS_ROUTE_EVENT(event_code)) {
msg->route = (struct zts_virtual_network_route*)arg; msg->route = (zts_route_info_t*)arg;
msg->len = sizeof(zts_route_info_t);
} }
if (PEER_EVENT_TYPE(eventCode)) { if (ZTS_PEER_EVENT(event_code)) {
msg->peer = (struct zts_peer_details*)arg; msg->peer = (zts_peer_info_t*)arg;
msg->len = sizeof(zts_peer_info_t);
} }
if (ADDR_EVENT_TYPE(eventCode)) { if (ZTS_ADDR_EVENT(event_code)) {
msg->addr = (struct zts_addr_details*)arg; 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) { if (msg && _callbackMsgQueue.size_approx() > 1024) {
// Rate-limit number of events // Rate-limit number of events
_freeEvent(msg); destroy(msg);
} }
else { else {
_callbackMsgQueue.enqueue(msg); _callbackMsgQueue.enqueue(msg);
} }
} }
void _freeEvent(struct zts_callback_msg* msg) void Events::destroy(zts_event_msg_t* msg)
{ {
if (! msg) { if (! msg) {
return; 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 #ifdef ZTS_ENABLE_PYTHON
PyGILState_STATE state = PyGILState_Ensure(); PyGILState_STATE state = PyGILState_Ensure();
_userEventCallback->on_zerotier_event(msg); _userEventCallback->on_zerotier_event(msg);
@@ -139,24 +176,24 @@ void _passDequeuedEventToUser(struct zts_callback_msg* msg)
#ifdef ZTS_ENABLE_JAVA #ifdef ZTS_ENABLE_JAVA
if (_userCallbackMethodRef) { if (_userCallbackMethodRef) {
JNIEnv* env; JNIEnv* env;
#if defined(__ANDROID__) #if defined(__ANDROID__)
jint rs = jvm->AttachCurrentThread(&env, NULL); jint rs = jvm->AttachCurrentThread(&env, NULL);
#else #else
jint rs = jvm->AttachCurrentThread((void**)&env, NULL); jint rs = jvm->AttachCurrentThread((void**)&env, NULL);
#endif #endif
assert(rs == JNI_OK); assert(rs == JNI_OK);
uint64_t arg = 0; uint64_t arg = 0;
uint64_t id = 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; 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; 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; 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 #endif // ZTS_ENABLE_JAVA
#ifdef ZTS_ENABLE_PINVOKE #ifdef ZTS_ENABLE_PINVOKE
@@ -169,53 +206,47 @@ void _passDequeuedEventToUser(struct zts_callback_msg* msg)
_userEventCallback(msg); _userEventCallback(msg);
} }
#endif #endif
_freeEvent(msg); destroy(msg);
if (bShouldStopCallbackThread) { if (bShouldStopCallbackThread) {
/* Ensure last possible callback ZTS_EVENT_STACK_DOWN is /* Ensure last possible callback ZTS_EVENT_STACK_DOWN is
delivered before callback thread is finally stopped. */ 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; bool retval = false;
#ifdef ZTS_ENABLE_JAVA #ifdef ZTS_ENABLE_JAVA
retval = (jvm && objRef && _userCallbackMethodRef); retval = (jvm && objRef && _userCallbackMethodRef);
#else #else
retval = _userEventCallback; retval = _userEventCallback;
#endif #endif
_callbackLock.unlock(); events_m.unlock();
return retval; return retval;
} }
void _clearRegisteredCallback() void Events::clrCallback()
{ {
_callbackLock.lock(); events_m.lock();
#ifdef ZTS_ENABLE_JAVA #ifdef ZTS_ENABLE_JAVA
objRef = NULL; objRef = NULL;
_userCallbackMethodRef = NULL; _userCallbackMethodRef = NULL;
#else #else
_userEventCallback = NULL; _userEventCallback = NULL;
#endif #endif
_callbackLock.unlock(); events_m.unlock();
} }
int _canPerformServiceOperation() int Events::canPerformServiceOperation()
{ {
return service && service->isRunning() && service->getNode() && service->getNode()->online() return zts_service && zts_service->isRunning() && ! getState(ZTS_STATE_FREE_CALLED);
&& ! _getState(ZTS_STATE_FREE_CALLED);
} }
#define RESET_FLAGS() _serviceStateFlags = 0; void Events::setState(uint8_t newFlags)
#define SET_FLAGS(f) _serviceStateFlags |= f;
#define CLR_FLAGS(f) _serviceStateFlags &= ~f;
#define GET_FLAGS(f) ((_serviceStateFlags & f) > 0)
void _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 return; // No effect. Not allowed to set this flag manually
} }
SET_FLAGS(newFlags); 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) { if (newFlags & ZTS_STATE_NET_SERVICE_RUNNING) {
return; // No effect. Not allowed to set this flag manually 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__) void Events::enable()
DWORD WINAPI _runCallbacks(LPVOID thread_id)
#else
void* _runCallbacks(void* thread_id)
#endif
{ {
#if defined(__APPLE__) _enabled = true;
pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME); }
#endif
while (_getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) { void Events::disable()
struct zts_callback_msg* msg; {
size_t sz = _callbackMsgQueue.size_approx(); _enabled = false;
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;
} }
} // namespace ZeroTier } // namespace ZeroTier

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -14,98 +14,153 @@
/** /**
* @file * @file
* *
* Header for callback event processing logic * Callback event creation and distribution to user application
*/ */
#ifndef ZT_EVENTS_HPP #ifndef ZTS_USER_EVENTS_HPP
#define ZT_EVENTS_HPP #define ZTS_USER_EVENTS_HPP
#include "Constants.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#include <string> #include <string>
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <BaseTsd.h> #include <BaseTsd.h>
#endif #endif
#ifdef ZTS_ENABLE_JAVA /* Macro substitutions to standardize state checking of service, node, callbacks, and TCP/IP
#include <jni.h> * stack. These are used only for control functions that are called at a low frequency. All higher
#endif * 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 { namespace ZeroTier {
#ifdef ZTS_ENABLE_JAVA
#include <jni.h>
// 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_NODE_RUNNING 0x01
#define ZTS_STATE_STACK_RUNNING 0x02 #define ZTS_STATE_STACK_RUNNING 0x02
#define ZTS_STATE_NET_SERVICE_RUNNING 0x04 #define ZTS_STATE_NET_SERVICE_RUNNING 0x04
#define ZTS_STATE_CALLBACKS_RUNNING 0x08 #define ZTS_STATE_CALLBACKS_RUNNING 0x08
#define ZTS_STATE_FREE_CALLED 0x10 #define ZTS_STATE_FREE_CALLED 0x10
#ifdef ZTS_ENABLE_JAVA extern volatile uint8_t service_state;
// References to JNI objects and VM kept for future callbacks extern int last_state_check;
extern JavaVM* jvm;
extern jobject objRef; inline int transport_ok()
extern jmethodID _userCallbackMethodRef; {
#endif last_state_check = service_state & ZTS_STATE_NET_SERVICE_RUNNING;
return last_state_check;
}
/** /**
* How often callback messages are assembled and/or sent * How often callback messages are assembled and/or sent
*/ */
#define ZTS_CALLBACK_PROCESSING_INTERVAL 25 #define ZTS_CALLBACK_PROCESSING_INTERVAL 25
/** class Events {
* Enqueue an event to be sent to the user application bool _enabled;
*/
void _enqueueEvent(int16_t eventCode, void* arg);
/** public:
* Send callback message to user application Events() : _enabled(true)
*/ {
void _passDequeuedEventToUser(struct ::zts_callback_msg* msg); }
/** /**
* Free memory occupied by callback structures * Perform one iteration of callback processing
*/ */
void _freeEvent(struct ::zts_callback_msg* msg); void run();
/** /**
* Return whether a callback method has been set * Enable callback event processing
*/ */
bool _isCallbackRegistered(); void enable();
/** /**
* Clear pointer reference to user-provided callback function * Disable callback event processing
*/ */
void _clearRegisteredCallback(); void disable();
/** /**
* Return whether service operation can be performed at this time * Enqueue an event to be sent to the user application
*/ */
int _canPerformServiceOperation(); void enqueue(int16_t event_code, const void* arg, int len = 0);
/** /**
* Set internal state flags * Send callback message to user application
*/ */
void _setState(uint8_t newFlags); void sendToUser(zts_event_msg_t* msg);
/** /**
* Clear internal state flags * Free memory occupied by callback structures
*/ */
void _clrState(uint8_t newFlags); void destroy(zts_event_msg_t* msg);
/** /**
* Get internal state flags * Return whether a callback method has been set
*/ */
bool _getState(uint8_t testFlags); bool hasCallback();
#ifdef __WINDOWS__ /**
DWORD WINAPI _runCallbacks(LPVOID thread_id); * Clear pointer reference to user-provided callback function
#else */
/** void clrCallback();
* Event callback thread
*/ /**
void* _runCallbacks(void* thread_id); * Return whether service operation can be performed at this time
#endif */
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 } // namespace ZeroTier
#endif // _H #endif // _H

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -14,16 +14,20 @@
/** /**
* @file * @file
* *
* Header for ZeroTier Node Service (a distant relative of OneService) * ZeroTier Node Service
*/ */
#ifndef ZT_NODE_SERVICE_HPP #ifndef ZTS_NODE_SERVICE_HPP
#define ZT_NODE_SERVICE_HPP #define ZTS_NODE_SERVICE_HPP
#include "Binder.hpp"
#include "Constants.hpp" #include "Constants.hpp"
#include "Events.hpp"
#include "InetAddress.hpp" #include "InetAddress.hpp"
#include "Mutex.hpp" #include "Mutex.hpp"
#include "Node.hpp" #include "Node.hpp"
#include "PortMapper.hpp"
#include "VirtualTap.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#include <string> #include <string>
@@ -31,8 +35,9 @@
#define ZTS_SERVICE_THREAD_NAME "ZTServiceThread" #define ZTS_SERVICE_THREAD_NAME "ZTServiceThread"
#define ZTS_EVENT_CALLBACK_THREAD_NAME "ZTEventCallbackThread" #define ZTS_EVENT_CALLBACK_THREAD_NAME "ZTEventCallbackThread"
// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also // Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and
// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi. // also bridged via ZeroTier to the same LAN traffic will (if the OS is sane)
// prefer WiFi.
#define ZT_IF_METRIC 5000 #define ZT_IF_METRIC 5000
// How often to check for new multicast subscriptions on a tap device // How often to check for new multicast subscriptions on a tap device
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 #define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
@@ -40,21 +45,16 @@
#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000 #define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <Windows.h> #include <Windows.h>
#endif #endif
namespace ZeroTier { namespace ZeroTier {
/** /**
* Local service for ZeroTier One as system VPN/NFV provider * ZeroTier node service
*/ */
class NodeService { class NodeService {
public: public:
uint16_t _userProvidedPort;
std::string _userProvidedPath;
char _userProvidedPublicIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
char _userProvidedSecretIdentity[ZT_IDENTITY_STRING_BUFFER_LENGTH];
/** /**
* Returned by node main if/when it terminates * Returned by node main if/when it terminates
*/ */
@@ -91,140 +91,455 @@ class NodeService {
/** /**
* Whitelist of addresses that can be configured by this network. * Whitelist of addresses that can be configured by this network.
* If empty and allowManaged is true, allow all private/pseudoprivate addresses. * If empty and allowManaged is true, allow all
* private/pseudoprivate addresses.
*/ */
std::vector<InetAddress> allowManagedWhitelist; std::vector<InetAddress> allowManagedWhitelist;
/** /**
* Allow configuration of IPs and routes within global (Internet) IP space? * Allow configuration of IPs and routes within global (Internet) IP
* space?
*/ */
bool allowGlobal; bool allowGlobal;
/** /**
* Allow overriding of system default routes for "full tunnel" operation? * Allow overriding of system default routes for "full tunnel"
* operation?
*/ */
bool allowDefault; bool allowDefault;
}; };
/** Phy<NodeService*> _phy;
* @return Platform default home path or empty string if this platform doesn't have one Node* _node;
*/ unsigned int _primaryPort = 0;
static std::string platformDefaultHomePath(); unsigned int _secondaryPort = 0;
unsigned int _tertiaryPort = 0;
volatile unsigned int _udpPortPickerCounter;
/** std::map<uint64_t, unsigned int> peerCache;
* Create a new instance of the service
// Local configuration and memo-ized information from it
Hashtable<uint64_t, std::vector<InetAddress> > _v4Hints;
Hashtable<uint64_t, std::vector<InetAddress> > _v6Hints;
Hashtable<uint64_t, std::vector<InetAddress> > _v4Blacklists;
Hashtable<uint64_t, std::vector<InetAddress> > _v6Blacklists;
std::vector<InetAddress> _globalV4Blacklist;
std::vector<InetAddress> _globalV6Blacklist;
std::vector<InetAddress> _allowManagementFrom;
std::vector<std::string> _interfacePrefixBlacklist;
Mutex _localConfig_m;
std::vector<InetAddress> explicitBind;
/*
* To attempt to handle NAT/gateway craziness we use three local UDP
* ports:
* *
* Once created, you must call the run() method to actually start * [0] is the normal/default port, usually 9993
* processing. * [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
* *
* The port is saved to a file in the home path called zerotier-one.port, * [2] exists because on some gateways trying to do regular NAT-t
* which is used by the CLI and can be used to see which port was chosen if * interferes destructively with uPnP port mapping behavior in very
* 0 (random port) is picked. * weird buggy ways. It's only used if uPnP/NAT-PMP is enabled in this
* * build.
* @param hp Home path
* @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
*/ */
static NodeService* newInstance(const char* hp, unsigned int port); unsigned int _ports[3] = { 0 };
Binder _binder;
virtual ~NodeService(); // Time we last received a packet from a global address
uint64_t _lastDirectReceiveFromGlobal;
/** // Last potential sleep/wake event
* Execute the service main I/O loop until terminated uint64_t _lastRestart;
*
* The terminate() method may be called from a signal handler or another
* thread to terminate execution. Otherwise this will not return unless
* another condition terminates execution such as a fatal error.
*/
virtual ReasonForTermination run() = 0;
/** // Deadline for the next background task service function
* @return Reason for terminating or ONE_STILL_RUNNING if running volatile int64_t _nextBackgroundTaskDeadline;
*/
virtual ReasonForTermination reasonForTermination() const = 0;
/** // Configured networks
* @return Fatal error message or empty string if none struct NetworkState {
*/ NetworkState() : tap((VirtualTap*)0)
virtual std::string fatalErrorMessage() const = 0; {
// Real defaults are in network 'up' code in network event
// handler
settings.allowManaged = true;
settings.allowGlobal = false;
settings.allowDefault = false;
}
/** VirtualTap* tap;
* @return System device name corresponding with a given ZeroTier network ID or empty string if ZT_VirtualNetworkConfig config; // memcpy() of raw config from core
* not opened yet or network ID not found std::vector<InetAddress> managedIps;
*/ NetworkSettings settings;
virtual std::string portDeviceName(uint64_t nwid) const = 0; };
std::map<uint64_t, NetworkState> _nets;
/** /** Lock to control access to network configuration data */
* Whether we allow access to the service via local HTTP requests (disabled by default in libzt) Mutex _nets_m;
*/ /** Lock to control access to storage data */
bool allowHttpBackplaneManagement = false; Mutex _store_m;
/** Lock to control access to service run state */
Mutex _run_m;
// Set to false to force service to stop
volatile bool _run;
/** Lock to control access to termination reason */
Mutex _termReason_m;
// Termination status information
ReasonForTermination _termReason;
/** std::string _fatalErrorMessage;
* @return Reference to the Node
*/
virtual Node* getNode() = 0;
/** // uPnP/NAT-PMP port mapper if enabled
* Fills out a structure with network-specific route information bool _portMappingEnabled; // local.conf settings
*/ #ifdef ZT_USE_MINIUPNPC
virtual void getRoutes(uint64_t nwid, void* routeArray, unsigned int* numRoutes) = 0; PortMapper* _portMapper;
virtual void join(uint64_t nwid) = 0;
virtual void leave(uint64_t nwid) = 0;
virtual void getIdentity(char* key_pair_str, uint16_t* key_buf_len) = 0;
/**
* Terminate background service (can be called from other threads)
*/
virtual void terminate() = 0;
/**
* Get local settings for a network
*
* @param nwid Network ID
* @param settings Buffer to fill with local network settings
* @return True if network was found and settings is filled
*/
virtual bool getNetworkSettings(const uint64_t nwid, NetworkSettings& settings) const = 0;
/**
* @return True if service is still running
*/
inline bool isRunning() const
{
return (this->reasonForTermination() == ONE_STILL_RUNNING);
}
protected:
NodeService()
{
}
private:
NodeService(const NodeService& one)
{
}
inline NodeService& operator=(const NodeService& one)
{
return *this;
}
};
struct serviceParameters {
int port;
std::string path;
char publicIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
char secretIdentityStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
};
#ifdef __WINDOWS__
DWORD WINAPI _runNodeService(LPVOID arg);
#else
/**
* NodeService thread
*/
void* _runNodeService(void* arg);
#endif #endif
/** Whether we allow caching network configs to storage */
uint8_t _allowNetworkCaching;
/** Whether we allow caching peer hints to storage */
uint8_t _allowPeerCaching;
char _publicIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
char _secretIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 };
char _planetData[ZTS_STORE_DATA_LEN] = { 0 };
/** Whether the node has successfully come online */
bool _nodeIsOnline;
/** Whether we allow the NodeService to generate events for the user */
bool _eventsEnabled;
/** Primary port defined by the user */
uint16_t _userProvidedPort;
/** Storage path defined by the user */
std::string _homePath;
/** System to ingest events from this class and emit them to the user */
Events* _events;
/**
* Constructor
*/
NodeService();
/**
* Destructor
*/
~NodeService();
ReasonForTermination run();
ReasonForTermination reasonForTermination() const;
std::string fatalErrorMessage() const;
void getRoutes(uint64_t net_id, void* routeArray, unsigned int* numRoutes);
void terminate();
void uninitialize();
int getNetworkSettings(const uint64_t net_id, NetworkSettings& settings) const;
int checkIfManagedIsAllowed(const NetworkState& n, const InetAddress& target);
void syncManagedStuff(NetworkState& n);
void phyOnDatagram(
PhySocket* sock,
void** uptr,
const struct sockaddr* localAddr,
const struct sockaddr* from,
void* data,
unsigned long len);
int nodeVirtualNetworkConfigFunction(
uint64_t net_id,
void** nuptr,
enum ZT_VirtualNetworkConfigOperation op,
const ZT_VirtualNetworkConfig* nwc);
void nodeEventCallback(enum ZT_Event event, const void* metaData);
zts_net_info_t* prepare_network_details_msg(const NetworkState& n);
void generateEventMsgs();
/** Join a network */
int join(uint64_t net_id);
/** Leave a network */
int leave(uint64_t net_id);
/** Return number of networks joined */
int networkCount();
/** Orbit a moon */
int orbit(void* tptr, uint64_t moonWorldId, uint64_t moonSeed);
/** De-orbit a moon */
int deorbit(void* tptr, uint64_t moonWorldId);
uint64_t getNodeId();
int getIdentity(char* keypair, uint16_t* len);
int setIdentity(const char* keypair, uint16_t len);
void nodeStatePutFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
const void* data,
int len);
int nodeStateGetFunction(
enum ZT_StateObjectType type,
const uint64_t id[2],
void* data,
unsigned int maxlen);
int nodeWirePacketSendFunction(
const int64_t localSocket,
const struct sockaddr_storage* addr,
const void* data,
unsigned int len,
unsigned int ttl);
void nodeVirtualNetworkFrameFunction(
uint64_t net_id,
void** nuptr,
uint64_t sourceMac,
uint64_t destMac,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len);
int nodePathCheckFunction(
uint64_t ztaddr,
const int64_t localSocket,
const struct sockaddr_storage* remoteAddr);
int nodePathLookupFunction(uint64_t ztaddr, int family, struct sockaddr_storage* result);
void tapFrameHandler(
uint64_t net_id,
const MAC& from,
const MAC& to,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len);
int shouldBindInterface(const char* ifname, const InetAddress& ifaddr);
int _trialBind(unsigned int port);
/** Return whether the NodeService is running */
int isRunning();
/** Return whether the node is online */
int nodeIsOnline();
/** Instruct the NodeService on where to look for identity files and caches */
int setHomePath(const char* homePath);
/** Set the NodeService's primary port */
int setPrimaryPort(unsigned short primaryPort);
/** Get the NodeService's primary port */
unsigned short getPrimaryPort();
/** Set the event system instance used to convey messages to the user */
int setUserEventSystem(Events* events);
/** Set the planet definition */
int setPlanet(const char* data, int len);
/** Add Interface prefix to blacklist (prevents ZeroTier from using that interface) */
int addInterfacePrefixToBlacklist(const char* prefix, int len);
/** Return the MAC Address of the node in the given network */
uint64_t getMACAddress(uint64_t net_id);
int getNetworkName(uint64_t net_id, char* dst, int len);
int allowPeerCaching(int allowed);
int allowNetworkCaching(int allowed);
int getNetworkBroadcast(uint64_t net_id);
int getNetworkMTU(uint64_t net_id);
int getNetworkType(uint64_t net_id);
int getNetworkStatus(uint64_t net_id);
int getFirstAssignedAddr(uint64_t net_id, int family, struct zts_sockaddr_storage* addr);
int getAllAssignedAddr(uint64_t net_id, struct zts_sockaddr_storage* addr, int* count);
int networkHasRoute(uint64_t net_id, int family);
int addrIsAssigned(uint64_t net_id, int family);
void phyOnTcpConnect(PhySocket* sock, void** uptr, bool success)
{
}
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)
{
}
void phyOnTcpWritable(PhySocket* sock, void** uptr)
{
}
void phyOnFileDescriptorActivity(PhySocket* sock, void** uptr, bool readable, bool writable)
{
}
void phyOnUnixAccept(PhySocket* sockL, PhySocket* sockN, void** uptrL, void** uptrN)
{
}
void phyOnUnixClose(PhySocket* sock, void** uptr)
{
}
void phyOnUnixData(PhySocket* sock, void** uptr, void* data, unsigned long len)
{
}
void phyOnUnixWritable(PhySocket* sock, void** uptr)
{
}
};
static int SnodeVirtualNetworkConfigFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t net_id,
void** nuptr,
enum ZT_VirtualNetworkConfigOperation op,
const ZT_VirtualNetworkConfig* nwconf)
{
return reinterpret_cast<NodeService*>(uptr)
->nodeVirtualNetworkConfigFunction(net_id, nuptr, op, nwconf);
}
static void
SnodeEventCallback(ZT_Node* node, void* uptr, void* tptr, enum ZT_Event event, const void* metaData)
{
reinterpret_cast<NodeService*>(uptr)->nodeEventCallback(event, 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)
{
reinterpret_cast<NodeService*>(uptr)->nodeStatePutFunction(type, id, data, 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)
{
return reinterpret_cast<NodeService*>(uptr)->nodeStateGetFunction(type, id, data, 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)
{
return reinterpret_cast<NodeService*>(uptr)
->nodeWirePacketSendFunction(localSocket, addr, data, len, ttl);
}
static void SnodeVirtualNetworkFrameFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t net_id,
void** nuptr,
uint64_t sourceMac,
uint64_t destMac,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len)
{
reinterpret_cast<NodeService*>(uptr)->nodeVirtualNetworkFrameFunction(
net_id,
nuptr,
sourceMac,
destMac,
etherType,
vlanId,
data,
len);
}
static int SnodePathCheckFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t ztaddr,
int64_t localSocket,
const struct sockaddr_storage* remoteAddr)
{
return reinterpret_cast<NodeService*>(uptr)->nodePathCheckFunction(
ztaddr,
localSocket,
remoteAddr);
}
static int SnodePathLookupFunction(
ZT_Node* node,
void* uptr,
void* tptr,
uint64_t ztaddr,
int family,
struct sockaddr_storage* result)
{
return reinterpret_cast<NodeService*>(uptr)->nodePathLookupFunction(ztaddr, family, result);
}
static void StapFrameHandler(
void* uptr,
void* tptr,
uint64_t net_id,
const MAC& from,
const MAC& to,
unsigned int etherType,
unsigned int vlanId,
const void* data,
unsigned int len)
{
reinterpret_cast<NodeService*>(uptr)
->tapFrameHandler(net_id, from, to, etherType, vlanId, data, len);
}
} // namespace ZeroTier } // namespace ZeroTier
#endif #endif

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -18,23 +18,23 @@
*/ */
#ifdef ZTS_ENABLE_PYTHON #ifdef ZTS_ENABLE_PYTHON
/** /**
* In some situations (Python comes to mind) a signal may not make its * In some situations (Python comes to mind) a signal may not make its
* way to libzt, for this reason we make sure to define a custom signal * way to libzt, for this reason we make sure to define a custom signal
* handler that can at least process SIGTERMs * handler that can at least process SIGTERMs
*/ */
#define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1 #define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1
#endif #endif
#ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS #ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS
#include "Signals.hpp" #include "Signals.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#include <cstdlib> #include <cstdlib>
#include <execinfo.h> #include <execinfo.h>
#include <signal.h> #include <signal.h>
void _signal_handler(int signal) void _signal_handler(int signal)
{ {

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -17,11 +17,11 @@
* Custom signal handler * Custom signal handler
*/ */
#ifndef SIGNALS_HPP #ifndef ZTS_SIGNALS_HPP
#define SIGNALS_HPP #define ZTS_SIGNALS_HPP
#ifdef ZTS_ENABLE_PYTHON #ifdef ZTS_ENABLE_PYTHON
#define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1 #define ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS 1
#endif #endif
#ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS #ifdef ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -19,33 +19,28 @@
#include "lwip/sockets.h" #include "lwip/sockets.h"
#include "Events.hpp"
#include "Utilities.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#include "lwip/def.h" #include "lwip/def.h"
#include "lwip/dns.h" #include "lwip/dns.h"
#include "lwip/inet.h" #include "lwip/inet.h"
#include "lwip/ip_addr.h" #include "lwip/ip_addr.h"
#include "lwip/netdb.h" #include "lwip/netdb.h"
#include "lwip/stats.h"
#define ZTS_STATE_NODE_RUNNING 0x01 // errno-like reporting variable
#define ZTS_STATE_STACK_RUNNING 0x02 int zts_errno;
#define ZTS_STATE_NET_SERVICE_RUNNING 0x04 extern int last_state_check;
#define ZTS_STATE_CALLBACKS_RUNNING 0x08
#define ZTS_STATE_FREE_CALLED 0x10
extern int zts_errno;
namespace ZeroTier { namespace ZeroTier {
extern uint8_t _serviceStateFlags;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
int zts_socket(const int socket_family, const int socket_type, const int protocol) int zts_socket(const int socket_family, const int socket_type, const int protocol)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_socket(socket_family, socket_type, protocol); return lwip_socket(socket_family, socket_type, protocol);
@@ -53,21 +48,24 @@ int zts_socket(const int socket_family, const int socket_type, const int protoco
int zts_connect(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen) int zts_connect(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! addr) { if (! addr) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
if (addrlen > (int)sizeof(struct zts_sockaddr_storage) if (addrlen > (zts_socklen_t)sizeof(struct zts_sockaddr_storage)
|| addrlen < (int)sizeof(struct zts_sockaddr_in)) { || addrlen < (zts_socklen_t)sizeof(struct zts_sockaddr_in)) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
return lwip_connect(fd, (sockaddr*)addr, addrlen); return lwip_connect(fd, (sockaddr*)addr, addrlen);
} }
int zts_connect_easy(int fd, int family, char* ipstr, int port, int timeout_ms) int zts_simple_connect(int fd, const char* ipstr, int port, int timeout_ms)
{ {
if (! transport_ok()) {
return ZTS_ERR_SERVICE;
}
if (timeout_ms < 0) { if (timeout_ms < 0) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
@@ -83,21 +81,17 @@ int zts_connect_easy(int fd, int family, char* ipstr, int port, int timeout_ms)
struct zts_sockaddr_storage ss; struct zts_sockaddr_storage ss;
struct zts_sockaddr* sa = NULL; struct zts_sockaddr* sa = NULL;
if (family == ZTS_AF_INET) { // Convert to standard address structure
addrlen = sizeof(ss);
ipstr2sockaddr(family, ipstr, port, (struct zts_sockaddr*)&ss, &addrlen); addrlen = sizeof(ss);
sa = (struct zts_sockaddr*)&ss; zts_util_ipstr_to_saddr(ipstr, port, (struct zts_sockaddr*)&ss, &addrlen);
} sa = (struct zts_sockaddr*)&ss;
if (family == ZTS_AF_INET6) {
addrlen = sizeof(ss);
ipstr2sockaddr(family, ipstr, port, (struct zts_sockaddr*)&ss, &addrlen);
sa = (struct zts_sockaddr*)&ss;
}
if (addrlen > 0 && sa != NULL) { if (addrlen > 0 && sa != NULL) {
if (zts_get_blocking(fd)) { if (zts_simple_get_blocking(fd)) {
do { do {
err = zts_connect(fd, sa, addrlen); err = zts_connect(fd, sa, addrlen);
zts_delay_ms(connect_delay); zts_util_delay(connect_delay);
n_tries--; n_tries--;
} while ((err < 0) && (zts_errno != 0) && (n_tries > 0)); } while ((err < 0) && (zts_errno != 0) && (n_tries > 0));
} }
@@ -108,7 +102,7 @@ int zts_connect_easy(int fd, int family, char* ipstr, int port, int timeout_ms)
int zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen) int zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! addr) { if (! addr) {
@@ -121,28 +115,26 @@ int zts_bind(int fd, const struct zts_sockaddr* addr, zts_socklen_t addrlen)
return lwip_bind(fd, (sockaddr*)addr, addrlen); return lwip_bind(fd, (sockaddr*)addr, addrlen);
} }
int zts_bind_easy(int fd, int family, char* ipstr, int port) int zts_simple_bind(int fd, const char* ipstr, int port)
{ {
if (family == ZTS_AF_INET) { if (! transport_ok()) {
struct zts_sockaddr_in in4; return ZTS_ERR_SERVICE;
zts_socklen_t addrlen = sizeof(in4);
ipstr2sockaddr(family, ipstr, port, (struct zts_sockaddr*)&in4, &addrlen);
struct zts_sockaddr* sa = (struct zts_sockaddr*)&in4;
return zts_bind(fd, sa, addrlen);
} }
if (family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 in6; zts_socklen_t addrlen = 0;
zts_socklen_t addrlen = sizeof(in6); struct zts_sockaddr_storage ss;
ipstr2sockaddr(family, ipstr, port, (struct zts_sockaddr*)&in6, &addrlen); struct zts_sockaddr* sa = NULL;
struct zts_sockaddr* sa = (struct zts_sockaddr*)&in6;
return zts_bind(fd, sa, addrlen); addrlen = sizeof(ss);
} zts_util_ipstr_to_saddr(ipstr, port, (struct zts_sockaddr*)&ss, &addrlen);
return ZTS_ERR_ARG; sa = (struct zts_sockaddr*)&ss;
return zts_bind(fd, sa, addrlen);
} }
int zts_listen(int fd, int backlog) int zts_listen(int fd, int backlog)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_listen(fd, backlog); return lwip_listen(fd, backlog);
@@ -150,20 +142,20 @@ int zts_listen(int fd, int backlog)
int zts_accept(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen) int zts_accept(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_accept(fd, (sockaddr*)addr, (socklen_t*)addrlen); return lwip_accept(fd, (sockaddr*)addr, (socklen_t*)addrlen);
} }
int zts_accept_easy(int fd, char* remoteIpStr, int len, int* port) int zts_simple_accept(int fd, char* remote_addr, int len, int* port)
{ {
if (! transport_ok()) {
return ZTS_ERR_SERVICE;
}
if (len != ZTS_INET6_ADDRSTRLEN) { if (len != ZTS_INET6_ADDRSTRLEN) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
char ipstr[ZTS_INET6_ADDRSTRLEN];
memset(ipstr, 0, ZTS_INET6_ADDRSTRLEN);
zts_sockaddr_storage ss; zts_sockaddr_storage ss;
zts_socklen_t addrlen = sizeof(ss); zts_socklen_t addrlen = sizeof(ss);
@@ -171,20 +163,82 @@ int zts_accept_easy(int fd, char* remoteIpStr, int len, int* port)
struct zts_sockaddr* sa = (struct zts_sockaddr*)&ss; struct zts_sockaddr* sa = (struct zts_sockaddr*)&ss;
if (sa->sa_family == ZTS_AF_INET) { if (sa->sa_family == ZTS_AF_INET) {
struct zts_sockaddr_in* in4 = (struct zts_sockaddr_in*)sa; struct zts_sockaddr_in* in4 = (struct zts_sockaddr_in*)sa;
zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), remoteIpStr, ZTS_INET_ADDRSTRLEN); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), remote_addr, ZTS_INET_ADDRSTRLEN);
*port = ntohs(in4->sin_port); *port = ntohs(in4->sin_port);
} }
if (sa->sa_family == ZTS_AF_INET6) { if (sa->sa_family == ZTS_AF_INET6) {
struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)sa; struct zts_sockaddr_in6* in6 = (struct zts_sockaddr_in6*)sa;
zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), remoteIpStr, ZTS_INET6_ADDRSTRLEN); zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), remote_addr, ZTS_INET6_ADDRSTRLEN);
*port = ntohs(in6->sin6_port); *port = ntohs(in6->sin6_port);
} }
return acc_fd; return acc_fd;
} }
int zts_simple_tcp_client(const char* remote_ipstr, int remote_port)
{
int fd, family = zts_util_get_ip_family(remote_ipstr);
if ((fd = zts_socket(family, ZTS_SOCK_STREAM, 0)) < 0) {
return fd; // Failed to create socket
}
int timeout = 0;
if ((fd = zts_simple_connect(fd, remote_ipstr, remote_port, timeout)) < 0) {
zts_close(fd);
return fd; // Failed to connect
}
return fd;
}
int zts_simple_tcp_server(
const char* local_ipstr,
int local_port,
char* remote_ipstr,
int len,
int* remote_port)
{
int listen_fd, family = zts_util_get_ip_family(local_ipstr);
if ((listen_fd = zts_socket(family, ZTS_SOCK_STREAM, 0)) < 0) {
return listen_fd; // Failed to create socket
}
if ((listen_fd = zts_simple_bind(listen_fd, local_ipstr, local_port)) < 0) {
return listen_fd; // Failed to bind
}
int backlog = 0;
if ((listen_fd = zts_listen(listen_fd, backlog)) < 0) {
return listen_fd; // Failed to listen
}
int acc_fd = 0;
if ((acc_fd = zts_simple_accept(listen_fd, remote_ipstr, len, remote_port)) < 0) {
return acc_fd; // Failed to accept
}
zts_close(listen_fd);
return acc_fd;
}
int zts_simple_udp_server(const char* local_ipstr, int local_port)
{
int fd, family = zts_util_get_ip_family(local_ipstr);
if ((fd = zts_socket(family, ZTS_SOCK_DGRAM, 0)) < 0) {
return fd; // Failed to create socket
}
if ((fd = zts_simple_bind(fd, local_ipstr, local_port)) < 0) {
zts_close(fd);
return fd; // Failed to connect
}
return fd;
}
int zts_simple_udp_client(const char* remote_ipstr)
{
int fd, family = zts_util_get_ip_family(remote_ipstr);
if ((fd = zts_socket(family, ZTS_SOCK_DGRAM, 0)) < 0) {
return fd; // Failed to create socket
}
return fd;
}
int zts_setsockopt(int fd, int level, int optname, const void* optval, zts_socklen_t optlen) int zts_setsockopt(int fd, int level, int optname, const void* optval, zts_socklen_t optlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_setsockopt(fd, level, optname, optval, optlen); return lwip_setsockopt(fd, level, optname, optval, optlen);
@@ -192,7 +246,7 @@ int zts_setsockopt(int fd, int level, int optname, const void* optval, zts_sockl
int zts_getsockopt(int fd, int level, int optname, void* optval, zts_socklen_t* optlen) int zts_getsockopt(int fd, int level, int optname, void* optval, zts_socklen_t* optlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_getsockopt(fd, level, optname, optval, (socklen_t*)optlen); return lwip_getsockopt(fd, level, optname, optval, (socklen_t*)optlen);
@@ -200,7 +254,7 @@ int zts_getsockopt(int fd, int level, int optname, void* optval, zts_socklen_t*
int zts_getsockname(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen) int zts_getsockname(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! addr) { if (! addr) {
@@ -215,7 +269,7 @@ int zts_getsockname(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
int zts_getpeername(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen) int zts_getpeername(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! addr) { if (! addr) {
@@ -230,7 +284,7 @@ int zts_getpeername(int fd, struct zts_sockaddr* addr, zts_socklen_t* addrlen)
int zts_close(int fd) int zts_close(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_close(fd); return lwip_close(fd);
@@ -243,7 +297,7 @@ int zts_select(
zts_fd_set* exceptfds, zts_fd_set* exceptfds,
struct zts_timeval* timeout) struct zts_timeval* timeout)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_select( return lwip_select(
@@ -256,7 +310,7 @@ int zts_select(
int zts_fcntl(int fd, int cmd, int flags) int zts_fcntl(int fd, int cmd, int flags)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_fcntl(fd, cmd, flags); return lwip_fcntl(fd, cmd, flags);
@@ -264,7 +318,7 @@ int zts_fcntl(int fd, int cmd, int flags)
int zts_poll(struct zts_pollfd* fds, nfds_t nfds, int timeout) int zts_poll(struct zts_pollfd* fds, nfds_t nfds, int timeout)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_poll((pollfd*)fds, nfds, timeout); return lwip_poll((pollfd*)fds, nfds, timeout);
@@ -272,7 +326,7 @@ int zts_poll(struct zts_pollfd* fds, nfds_t nfds, int timeout)
int zts_ioctl(int fd, unsigned long request, void* argp) int zts_ioctl(int fd, unsigned long request, void* argp)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! argp) { if (! argp) {
@@ -283,7 +337,7 @@ int zts_ioctl(int fd, unsigned long request, void* argp)
ssize_t zts_send(int fd, const void* buf, size_t len, int flags) ssize_t zts_send(int fd, const void* buf, size_t len, int flags)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! buf) { if (! buf) {
@@ -300,7 +354,7 @@ ssize_t zts_sendto(
const struct zts_sockaddr* addr, const struct zts_sockaddr* addr,
zts_socklen_t addrlen) zts_socklen_t addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! addr || ! buf) { if (! addr || ! buf) {
@@ -315,16 +369,15 @@ ssize_t zts_sendto(
ssize_t zts_sendmsg(int fd, const struct zts_msghdr* msg, int flags) ssize_t zts_sendmsg(int fd, const struct zts_msghdr* msg, int flags)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_sendmsg(fd, (const struct msghdr*)msg, flags); return lwip_sendmsg(fd, (const struct msghdr*)msg, flags);
} }
ssize_t zts_recv(int fd, void* buf, size_t len, int flags) ssize_t zts_recv(int fd, void* buf, size_t len, int flags)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! buf) { if (! buf) {
@@ -341,7 +394,7 @@ ssize_t zts_recvfrom(
struct zts_sockaddr* addr, struct zts_sockaddr* addr,
zts_socklen_t* addrlen) zts_socklen_t* addrlen)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! buf) { if (! buf) {
@@ -352,7 +405,7 @@ ssize_t zts_recvfrom(
ssize_t zts_recvmsg(int fd, struct zts_msghdr* msg, int flags) ssize_t zts_recvmsg(int fd, struct zts_msghdr* msg, int flags)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! msg) { if (! msg) {
@@ -363,7 +416,7 @@ ssize_t zts_recvmsg(int fd, struct zts_msghdr* msg, int flags)
ssize_t zts_read(int fd, void* buf, size_t len) ssize_t zts_read(int fd, void* buf, size_t len)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! buf) { if (! buf) {
@@ -374,19 +427,17 @@ ssize_t zts_read(int fd, void* buf, size_t len)
ssize_t zts_readv(int fd, const struct zts_iovec* iov, int iovcnt) ssize_t zts_readv(int fd, const struct zts_iovec* iov, int iovcnt)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_readv(fd, (iovec*)iov, iovcnt); return lwip_readv(fd, (iovec*)iov, iovcnt);
} }
ssize_t zts_write(int fd, const void* buf, size_t len) ssize_t zts_write(int fd, const void* buf, size_t len)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (! buf) { if (! buf) {
return ZTS_ERR_ARG; return ZTS_ERR_ARG;
} }
@@ -395,16 +446,15 @@ ssize_t zts_write(int fd, const void* buf, size_t len)
ssize_t zts_writev(int fd, const struct zts_iovec* iov, int iovcnt) ssize_t zts_writev(int fd, const struct zts_iovec* iov, int iovcnt)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_writev(fd, (iovec*)iov, iovcnt); return lwip_writev(fd, (iovec*)iov, iovcnt);
} }
int zts_shutdown(int fd, int how) int zts_shutdown(int fd, int how)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
return lwip_shutdown(fd, how); return lwip_shutdown(fd, how);
@@ -412,7 +462,7 @@ int zts_shutdown(int fd, int how)
struct zts_hostent* zts_gethostbyname(const char* name) struct zts_hostent* zts_gethostbyname(const char* name)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return NULL; return NULL;
} }
if (! name) { if (! name) {
@@ -423,7 +473,7 @@ struct zts_hostent* zts_gethostbyname(const char* name)
int zts_dns_set_server(uint8_t index, const zts_ip_addr* addr) int zts_dns_set_server(uint8_t index, const zts_ip_addr* addr)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (index >= DNS_MAX_SERVERS) { if (index >= DNS_MAX_SERVERS) {
@@ -438,7 +488,7 @@ int zts_dns_set_server(uint8_t index, const zts_ip_addr* addr)
const zts_ip_addr* zts_dns_get_server(uint8_t index) const zts_ip_addr* zts_dns_get_server(uint8_t index)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return NULL; return NULL;
} }
if (index >= DNS_MAX_SERVERS) { if (index >= DNS_MAX_SERVERS) {
@@ -467,13 +517,14 @@ int zts_inet_pton(int family, const char* src, void* dst)
return lwip_inet_pton(family, src, dst); return lwip_inet_pton(family, src, dst);
} }
int ipstr2sockaddr( int zts_util_ipstr_to_saddr(
int family, const char* src_ipstr,
char* src_ipstr,
int port, int port,
struct zts_sockaddr* dest_addr, struct zts_sockaddr* dest_addr,
zts_socklen_t* addrlen) zts_socklen_t* addrlen)
{ {
int family = zts_util_get_ip_family(src_ipstr);
if (family == ZTS_AF_INET) { if (family == ZTS_AF_INET) {
struct zts_sockaddr_in in4; struct zts_sockaddr_in in4;
in4.sin_port = htons(port); in4.sin_port = htons(port);
@@ -509,14 +560,15 @@ int ipstr2sockaddr(
/** /**
* Helper functions that simplify API wrapper generation and usage in other * Helper functions that simplify API wrapper generation and usage in other
* non-C-like languages. Use simple integer types instead of bit flags, limit * non-C-like languages. Use simple integer types instead of bit flags,
* the number of operations each function performs, prevent the user from * limit the number of operations each function performs, prevent the user
* needing to manipulate the content of structures in a non-native language. * from needing to manipulate the content of structures in a non-native
* language.
*/ */
int zts_set_no_delay(int fd, int enabled) int zts_simple_set_no_delay(int fd, int enabled)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (enabled != 0 && enabled != 1) { if (enabled != 0 && enabled != 1) {
@@ -525,9 +577,9 @@ int zts_set_no_delay(int fd, int enabled)
return zts_setsockopt(fd, ZTS_IPPROTO_TCP, ZTS_TCP_NODELAY, (void*)&enabled, sizeof(int)); return zts_setsockopt(fd, ZTS_IPPROTO_TCP, ZTS_TCP_NODELAY, (void*)&enabled, sizeof(int));
} }
int zts_get_no_delay(int fd) int zts_simple_get_no_delay(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err, optval = 0; int err, optval = 0;
@@ -538,9 +590,9 @@ int zts_get_no_delay(int fd)
return optval != 0; return optval != 0;
} }
int zts_set_linger(int fd, int enabled, int value) int zts_simple_set_linger(int fd, int enabled, int value)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (enabled != 0 && enabled != 1) { if (enabled != 0 && enabled != 1) {
@@ -555,37 +607,37 @@ int zts_set_linger(int fd, int enabled, int value)
return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, sizeof(linger)); return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, sizeof(linger));
} }
int zts_get_linger_enabled(int fd) int zts_simple_get_linger_enabled(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err;
struct zts_linger linger; struct zts_linger linger;
zts_socklen_t len = sizeof(linger); zts_socklen_t len = sizeof(linger);
int err;
if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, &len)) < 0) { if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, &len)) < 0) {
return err; return err;
} }
return linger.l_onoff; return linger.l_onoff;
} }
int zts_get_linger_value(int fd) int zts_simple_get_linger_value(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err;
struct zts_linger linger; struct zts_linger linger;
zts_socklen_t len = sizeof(linger); zts_socklen_t len = sizeof(linger);
int err;
if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, &len)) < 0) { if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_LINGER, (void*)&linger, &len)) < 0) {
return err; return err;
} }
return linger.l_linger; return linger.l_linger;
} }
int zts_set_reuse_addr(int fd, int enabled) int zts_simple_set_reuse_addr(int fd, int enabled)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (enabled != 0 && enabled != 1) { if (enabled != 0 && enabled != 1) {
@@ -594,13 +646,12 @@ int zts_set_reuse_addr(int fd, int enabled)
return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void*)&enabled, sizeof(enabled)); return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void*)&enabled, sizeof(enabled));
} }
int zts_get_reuse_addr(int fd) int zts_simple_get_reuse_addr(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err; int err, optval = 0;
int optval = 0;
zts_socklen_t optlen = sizeof(optval); zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void*)&optval, &optlen)) < 0) { if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_REUSEADDR, (void*)&optval, &optlen)) < 0) {
return err; return err;
@@ -608,9 +659,9 @@ int zts_get_reuse_addr(int fd)
return optval != 0; return optval != 0;
} }
int zts_set_recv_timeout(int fd, int seconds, int microseconds) int zts_simple_set_recv_timeout(int fd, int seconds, int microseconds)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (seconds < 0 || microseconds < 0) { if (seconds < 0 || microseconds < 0) {
@@ -622,9 +673,9 @@ int zts_set_recv_timeout(int fd, int seconds, int microseconds)
return zts_setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, sizeof(tv)); return zts_setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*)&tv, sizeof(tv));
} }
int zts_get_recv_timeout(int fd) int zts_simple_get_recv_timeout(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
struct timeval tv; struct timeval tv;
@@ -636,9 +687,9 @@ int zts_get_recv_timeout(int fd)
return tv.tv_sec; // TODO microseconds return tv.tv_sec; // TODO microseconds
} }
int zts_set_send_timeout(int fd, int seconds, int microseconds) int zts_simple_set_send_timeout(int fd, int seconds, int microseconds)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (seconds < 0 || microseconds < 0) { if (seconds < 0 || microseconds < 0) {
@@ -650,9 +701,9 @@ int zts_set_send_timeout(int fd, int seconds, int microseconds)
return zts_setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, sizeof(tv)); return zts_setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*)&tv, sizeof(tv));
} }
int zts_get_send_timeout(int fd) int zts_simple_get_send_timeout(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
struct zts_timeval tv; struct zts_timeval tv;
@@ -664,9 +715,9 @@ int zts_get_send_timeout(int fd)
return tv.tv_sec; // TODO microseconds return tv.tv_sec; // TODO microseconds
} }
int zts_set_send_buf_size(int fd, int size) int zts_simple_set_send_buf_size(int fd, int size)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (size < 0) { if (size < 0) {
@@ -675,9 +726,9 @@ int zts_set_send_buf_size(int fd, int size)
return zts_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&size, sizeof(int)); return zts_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void*)&size, sizeof(int));
} }
int zts_get_send_buf_size(int fd) int zts_simple_get_send_buf_size(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err, optval = 0; int err, optval = 0;
@@ -688,9 +739,9 @@ int zts_get_send_buf_size(int fd)
return optval; return optval;
} }
int zts_set_recv_buf_size(int fd, int size) int zts_simple_set_recv_buf_size(int fd, int size)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (size < 0) { if (size < 0) {
@@ -699,9 +750,9 @@ int zts_set_recv_buf_size(int fd, int size)
return zts_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&size, sizeof(int)); return zts_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void*)&size, sizeof(int));
} }
int zts_get_recv_buf_size(int fd) int zts_simple_get_recv_buf_size(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err, optval = 0; int err, optval = 0;
@@ -712,9 +763,9 @@ int zts_get_recv_buf_size(int fd)
return optval; return optval;
} }
int zts_set_ttl(int fd, int ttl) int zts_simple_set_ttl(int fd, int ttl)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (ttl < 0 || ttl > 255) { if (ttl < 0 || ttl > 255) {
@@ -723,9 +774,9 @@ int zts_set_ttl(int fd, int ttl)
return zts_setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); return zts_setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
} }
int zts_get_ttl(int fd) int zts_simple_get_ttl(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err, ttl = 0; int err, ttl = 0;
@@ -736,9 +787,9 @@ int zts_get_ttl(int fd)
return ttl; return ttl;
} }
int zts_set_blocking(int fd, int enabled) int zts_simple_set_blocking(int fd, int enabled)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (enabled != 0 && enabled != 1) { if (enabled != 0 && enabled != 1) {
@@ -754,9 +805,9 @@ int zts_set_blocking(int fd, int enabled)
} }
} }
int zts_get_blocking(int fd) int zts_simple_get_blocking(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int flags = zts_fcntl(fd, ZTS_F_GETFL, 0); int flags = zts_fcntl(fd, ZTS_F_GETFL, 0);
@@ -766,10 +817,9 @@ int zts_get_blocking(int fd)
return ! (flags & ZTS_O_NONBLOCK); return ! (flags & ZTS_O_NONBLOCK);
} }
int zts_set_keepalive(int fd, int enabled) int zts_simple_set_keepalive(int fd, int enabled)
{ {
// if (! transport_ok()) {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
if (enabled != 0 && enabled != 1) { if (enabled != 0 && enabled != 1) {
@@ -779,13 +829,12 @@ int zts_set_keepalive(int fd, int enabled)
return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, &keepalive, sizeof(keepalive)); return zts_setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, &keepalive, sizeof(keepalive));
} }
int zts_get_keepalive(int fd) int zts_simple_get_keepalive(int fd)
{ {
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { if (! transport_ok()) {
return ZTS_ERR_SERVICE; return ZTS_ERR_SERVICE;
} }
int err; int err, optval = 0;
int optval = 0;
zts_socklen_t optlen = sizeof(optval); zts_socklen_t optlen = sizeof(optval);
if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, (void*)&optval, &optlen)) < 0) { if ((err = zts_getsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, (void*)&optval, &optlen)) < 0) {
return err; return err;
@@ -793,102 +842,6 @@ int zts_get_keepalive(int fd)
return optval != 0; return optval != 0;
} }
//----------------------------------------------------------------------------//
// Statistics //
//----------------------------------------------------------------------------//
#ifdef ZTS_ENABLE_STATS
extern struct stats_ lwip_stats;
int zts_get_all_stats(struct zts_stats* statsDest)
{
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
#if LWIP_STATS
if (! statsDest) {
return ZTS_ERR_ARG;
}
memset(statsDest, 0, sizeof(struct zts_stats));
// Copy lwIP stats
memcpy(&(statsDest->link), &(lwip_stats.link), sizeof(struct stats_proto));
memcpy(&(statsDest->etharp), &(lwip_stats.etharp), sizeof(struct stats_proto));
memcpy(&(statsDest->ip_frag), &(lwip_stats.ip_frag), sizeof(struct stats_proto));
memcpy(&(statsDest->ip), &(lwip_stats.ip), sizeof(struct stats_proto));
memcpy(&(statsDest->icmp), &(lwip_stats.icmp), sizeof(struct stats_proto));
// memcpy(&(statsDest->igmp), &(lwip_stats.igmp), sizeof(struct stats_igmp));
memcpy(&(statsDest->udp), &(lwip_stats.udp), sizeof(struct stats_proto));
memcpy(&(statsDest->tcp), &(lwip_stats.tcp), sizeof(struct stats_proto));
// mem omitted
// memp omitted
memcpy(&(statsDest->sys), &(lwip_stats.sys), sizeof(struct stats_sys));
memcpy(&(statsDest->ip6), &(lwip_stats.ip6), sizeof(struct stats_proto));
memcpy(&(statsDest->icmp6), &(lwip_stats.icmp6), sizeof(struct stats_proto));
memcpy(&(statsDest->ip6_frag), &(lwip_stats.ip6_frag), sizeof(struct stats_proto));
memcpy(&(statsDest->mld6), &(lwip_stats.mld6), sizeof(struct stats_igmp));
memcpy(&(statsDest->nd6), &(lwip_stats.nd6), sizeof(struct stats_proto));
memcpy(&(statsDest->ip_frag), &(lwip_stats.ip_frag), sizeof(struct stats_proto));
// mib2 omitted
// Copy ZT stats
// ...
return ZTS_ERR_OK;
#else
return ZTS_ERR_NO_RESULT;
#endif
}
int zts_get_protocol_stats(int protocolType, void* protoStatsDest)
{
if (! (_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) {
return ZTS_ERR_SERVICE;
}
#if LWIP_STATS
if (! protoStatsDest) {
return ZTS_ERR_ARG;
}
memset(protoStatsDest, 0, sizeof(struct stats_proto));
switch (protocolType) {
case ZTS_STATS_PROTOCOL_LINK:
memcpy(protoStatsDest, &(lwip_stats.link), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_ETHARP:
memcpy(protoStatsDest, &(lwip_stats.etharp), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_IP:
memcpy(protoStatsDest, &(lwip_stats.ip), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_UDP:
memcpy(protoStatsDest, &(lwip_stats.udp), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_TCP:
memcpy(protoStatsDest, &(lwip_stats.tcp), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_ICMP:
memcpy(protoStatsDest, &(lwip_stats.icmp), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_IP_FRAG:
memcpy(protoStatsDest, &(lwip_stats.ip_frag), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_IP6:
memcpy(protoStatsDest, &(lwip_stats.ip6), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_ICMP6:
memcpy(protoStatsDest, &(lwip_stats.icmp6), sizeof(struct stats_proto));
break;
case ZTS_STATS_PROTOCOL_IP6_FRAG:
memcpy(protoStatsDest, &(lwip_stats.ip6_frag), sizeof(struct stats_proto));
break;
default: return ZTS_ERR_ARG;
}
return ZTS_ERR_OK;
#else
return ZTS_ERR_NO_RESULT;
#endif
}
#endif // ZTS_ENABLE_STATS
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

92
src/Utilities.cpp Normal file
View File

@@ -0,0 +1,92 @@
/*
* Copyright (c)2013-2021 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2026-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#include "Utilities.hpp"
#include "ZeroTierSockets.h"
#include <netinet/in.h>
#include <string.h>
#ifdef __WINDOWS__
#include <windows.h>
#elif _POSIX_C_SOURCE >= 199309L
#include <time.h> // for nanosleep
#else
#include <unistd.h> // for usleep
#endif
#ifdef __cplusplus
extern "C" {
#endif
int zts_util_get_ip_family(const char* ipstr)
{
if (! ipstr) {
return ZTS_ERR_ARG;
}
int family = -1;
struct zts_sockaddr_in sa4;
if (zts_inet_pton(ZTS_AF_INET, ipstr, &(sa4.sin_addr)) == 1) {
family = ZTS_AF_INET;
}
struct zts_sockaddr_in6 sa6;
if (zts_inet_pton(ZTS_AF_INET6, ipstr, &(sa6.sin6_addr)) == 1) {
family = ZTS_AF_INET6;
}
return family;
}
void zts_util_delay(long milliseconds)
{
#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);
#else
usleep(milliseconds * 1000);
#endif
}
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;
}
}
#ifdef __cplusplus
}
#endif

29
src/Utilities.hpp Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (c)2013-2021 ZeroTier, Inc.
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory.
*
* Change Date: 2026-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2.0 of the Apache License.
*/
/****/
#ifndef ZTS_UTILITIES_HPP
#define ZTS_UTILITIES_HPP
#include "ZeroTierSockets.h"
#ifdef __cplusplus
extern "C" {
#endif
void native_ss_to_zts_ss(struct zts_sockaddr_storage* ss_out, const struct sockaddr_storage* ss_in);
#ifdef __cplusplus
}
#endif
#endif // ZTS_UTILITIES_HPP

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -14,7 +14,7 @@
/** /**
* @file * @file
* *
* Virtual ethernet tap device and combined network stack driver * Virtual Ethernet tap device and combined network stack driver
*/ */
#include "Constants.hpp" #include "Constants.hpp"
@@ -22,6 +22,7 @@
#include "MAC.hpp" #include "MAC.hpp"
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "Mutex.hpp" #include "Mutex.hpp"
#include "OSUtils.hpp"
#include "lwip/etharp.h" #include "lwip/etharp.h"
#include "lwip/ethip6.h" #include "lwip/ethip6.h"
#include "lwip/netif.h" #include "lwip/netif.h"
@@ -30,18 +31,17 @@
#include "netif/ethernet.h" #include "netif/ethernet.h"
#ifdef LWIP_STATS #ifdef LWIP_STATS
#include "lwip/stats.h" #include "lwip/stats.h"
#endif #endif
#include "Debug.hpp"
#include "Events.hpp" #include "Events.hpp"
#include "VirtualTap.hpp" #include "VirtualTap.hpp"
#include "ZeroTierSockets.h" #include "ZeroTierSockets.h"
#if defined(__WINDOWS__) #if defined(__WINDOWS__)
#include "Synchapi.h" #include "Synchapi.h"
#include <time.h> #include <time.h>
#endif #endif
#define ZTS_TAP_THREAD_POLLING_INTERVAL 50 #define ZTS_TAP_THREAD_POLLING_INTERVAL 50
@@ -49,18 +49,18 @@
namespace ZeroTier { namespace ZeroTier {
extern void _enqueueEvent(int16_t eventCode, void* arg = NULL); extern Events* zts_events;
/** /**
* A virtual tap device. The ZeroTier core service creates one of these for each * Virtual tap device. ZeroTier will create one per joined network. It will
* virtual network joined. It will be destroyed upon leave(). * then be destroyed upon leaving the network.
*/ */
VirtualTap::VirtualTap( VirtualTap::VirtualTap(
const char* homePath, const char* homePath,
const MAC& mac, const MAC& mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t net_id,
const char* friendlyName, const char* friendlyName,
void (*handler)( void (*handler)(
void*, void*,
@@ -81,13 +81,11 @@ VirtualTap::VirtualTap(
, _run(true) , _run(true)
, _mac(mac) , _mac(mac)
, _mtu(mtu) , _mtu(mtu)
, _nwid(nwid) , _net_id(net_id)
, _unixListenSocket((PhySocket*)0) , _unixListenSocket((PhySocket*)0)
, _phy(this, false, true) , _phy(this, false, true)
{ {
memset(vtap_full_name, 0, sizeof(vtap_full_name)); OSUtils::ztsnprintf(vtap_full_name, VTAP_NAME_LEN, "libzt-vtap-%llx", _net_id);
snprintf(vtap_full_name, sizeof(vtap_full_name), "libzt%llx", (unsigned long long)_nwid);
_dev = vtap_full_name;
#ifndef __WINDOWS__ #ifndef __WINDOWS__
::pipe(_shutdownSignalPipe); ::pipe(_shutdownSignalPipe);
#endif #endif
@@ -97,8 +95,8 @@ VirtualTap::VirtualTap(
VirtualTap::~VirtualTap() VirtualTap::~VirtualTap()
{ {
struct zts_network_details* nd = new zts_network_details; zts_net_info_t* nd = new zts_net_info_t;
nd->nwid = _nwid; nd->net_id = _net_id;
_run = false; _run = false;
#ifndef __WINDOWS__ #ifndef __WINDOWS__
::write(_shutdownSignalPipe[1], "\0", 1); ::write(_shutdownSignalPipe[1], "\0", 1);
@@ -108,7 +106,7 @@ VirtualTap::~VirtualTap()
netif4 = NULL; netif4 = NULL;
_lwip_remove_netif(netif6); _lwip_remove_netif(netif6);
netif6 = NULL; netif6 = NULL;
_enqueueEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); _events->enqueue(ZTS_EVENT_NETWORK_DOWN, (void*)nd);
Thread::join(_thread); Thread::join(_thread);
#ifndef __WINDOWS__ #ifndef __WINDOWS__
::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[0]);
@@ -131,6 +129,11 @@ bool VirtualTap::enabled() const
return _enabled; return _enabled;
} }
void VirtualTap::setUserEventSystem(Events* events)
{
_events = events;
}
bool VirtualTap::hasIpv4Addr() bool VirtualTap::hasIpv4Addr()
{ {
Mutex::Lock _l(_ips_m); Mutex::Lock _l(_ips_m);
@@ -159,21 +162,21 @@ bool VirtualTap::hasIpv6Addr()
bool VirtualTap::addIp(const InetAddress& ip) bool VirtualTap::addIp(const InetAddress& ip)
{ {
char ipbuf[128]; // TODO: Rewrite to allow for more addresses
// ip.toString(ipbuf); char ipbuf[128] = { 0 };
// DEBUG_INFO("addr=%s", ipbuf);
/* Limit address assignments to one per type. /* Limit address assignments to one per type.
This limitation can be removed if some changes This limitation can be removed if some changes
are made in the netif driver. */ are made in the netif driver. */
if (ip.isV4() && hasIpv4Addr()) { if (ip.isV4() && hasIpv4Addr()) {
ip.toString(ipbuf); ip.toString(ipbuf);
DEBUG_INFO("failed to add IP (%s), only one per type per netif allowed\n", ipbuf); // DEBUG_INFO("failed to add IP (%s), only one per type per netif
// allowed\n", ipbuf);
return false; return false;
} }
if (ip.isV6() && hasIpv6Addr()) { if (ip.isV6() && hasIpv6Addr()) {
ip.toString(ipbuf); ip.toString(ipbuf);
DEBUG_INFO("failed to add IP (%s), only one per type per netif allowed\n", ipbuf); // DEBUG_INFO("failed to add IP (%s), only one per type per netif
// allowed\n", ipbuf);
return false; return false;
} }
@@ -218,16 +221,6 @@ void VirtualTap::put(
} }
} }
std::string VirtualTap::deviceName() const
{
return _dev;
}
void VirtualTap::setFriendlyName(const char* friendlyName)
{
DEBUG_INFO("%s", friendlyName);
}
void VirtualTap::scanMulticastGroups( void VirtualTap::scanMulticastGroups(
std::vector<MulticastGroup>& added, std::vector<MulticastGroup>& added,
std::vector<MulticastGroup>& removed) std::vector<MulticastGroup>& removed)
@@ -299,10 +292,13 @@ void VirtualTap::phyOnDatagram(
void* data, void* data,
unsigned long len) unsigned long len)
{ {
// Intentionally empty
} }
void VirtualTap::phyOnTcpConnect(PhySocket* sock, void** uptr, bool success) void VirtualTap::phyOnTcpConnect(PhySocket* sock, void** uptr, bool success)
{ {
} }
void VirtualTap::phyOnTcpAccept( void VirtualTap::phyOnTcpAccept(
PhySocket* sockL, PhySocket* sockL,
PhySocket* sockN, PhySocket* sockN,
@@ -311,22 +307,26 @@ void VirtualTap::phyOnTcpAccept(
const struct sockaddr* from) const struct sockaddr* from)
{ {
} }
void VirtualTap::phyOnTcpClose(PhySocket* sock, void** uptr) void VirtualTap::phyOnTcpClose(PhySocket* sock, void** uptr)
{ {
} }
void VirtualTap::phyOnTcpData(PhySocket* sock, void** uptr, void* data, unsigned long len) void VirtualTap::phyOnTcpData(PhySocket* sock, void** uptr, void* data, unsigned long len)
{ {
} }
void VirtualTap::phyOnTcpWritable(PhySocket* sock, void** uptr) void VirtualTap::phyOnTcpWritable(PhySocket* sock, void** uptr)
{ {
} }
void VirtualTap::phyOnUnixClose(PhySocket* sock, void** uptr) void VirtualTap::phyOnUnixClose(PhySocket* sock, void** uptr)
{ {
} }
////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------//
// Netif driver code for lwIP network stack // // Netif driver code for lwIP network stack //
////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------//
bool _has_exited = false; bool _has_exited = false;
bool _has_started = false; bool _has_started = false;
@@ -342,39 +342,39 @@ static void _tcpip_init_done(void* arg)
{ {
sys_sem_t* sem; sys_sem_t* sem;
sem = (sys_sem_t*)arg; sem = (sys_sem_t*)arg;
_setState(ZTS_STATE_STACK_RUNNING); zts_events->setState(ZTS_STATE_STACK_RUNNING);
_has_started = true; _has_started = true;
_enqueueEvent(ZTS_EVENT_STACK_UP); zts_events->enqueue(ZTS_EVENT_STACK_UP, NULL);
sys_sem_signal(sem); sys_sem_signal(sem);
} }
static void _main_lwip_driver_loop(void* arg) static void _main_lwip_driver_loop(void* arg)
{ {
#if defined(__linux__) #if defined(__linux__)
pthread_setname_np(pthread_self(), ZTS_LWIP_DRIVER_THREAD_NAME); pthread_setname_np(pthread_self(), ZTS_LWIP_THREAD_NAME);
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
pthread_setname_np(ZTS_LWIP_DRIVER_THREAD_NAME); pthread_setname_np(ZTS_LWIP_THREAD_NAME);
#endif #endif
sys_sem_t sem; sys_sem_t sem;
LWIP_UNUSED_ARG(arg); LWIP_UNUSED_ARG(arg);
if (sys_sem_new(&sem, 0) != ERR_OK) { if (sys_sem_new(&sem, 0) != ERR_OK) {
DEBUG_ERROR("failed to create semaphore"); // DEBUG_ERROR("failed to create semaphore");
} }
tcpip_init(_tcpip_init_done, &sem); tcpip_init(_tcpip_init_done, &sem);
sys_sem_wait(&sem); sys_sem_wait(&sem);
// Main loop // Main loop
while (_getState(ZTS_STATE_STACK_RUNNING)) { while (zts_events->getState(ZTS_STATE_STACK_RUNNING)) {
zts_delay_ms(LWIP_DRIVER_LOOP_INTERVAL); zts_util_delay(LWIP_DRIVER_LOOP_INTERVAL);
} }
_has_exited = true; _has_exited = true;
_enqueueEvent(ZTS_EVENT_STACK_DOWN); zts_events->enqueue(ZTS_EVENT_STACK_DOWN, NULL);
} }
bool _lwip_is_up() bool _lwip_is_up()
{ {
Mutex::Lock _l(stackLock); Mutex::Lock _l(stackLock);
return _getState(ZTS_STATE_STACK_RUNNING); return zts_events->getState(ZTS_STATE_STACK_RUNNING);
} }
void _lwip_driver_init() void _lwip_driver_init()
@@ -390,7 +390,7 @@ void _lwip_driver_init()
sys_init(); // Required for win32 init of critical sections sys_init(); // Required for win32 init of critical sections
#endif #endif
sys_thread_new( sys_thread_new(
ZTS_LWIP_DRIVER_THREAD_NAME, ZTS_LWIP_THREAD_NAME,
_main_lwip_driver_loop, _main_lwip_driver_loop,
NULL, NULL,
DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_STACKSIZE,
@@ -404,11 +404,11 @@ void _lwip_driver_shutdown()
} }
Mutex::Lock _l(stackLock); Mutex::Lock _l(stackLock);
// Set flag to stop sending frames into the core // Set flag to stop sending frames into the core
_clrState(ZTS_STATE_STACK_RUNNING); zts_events->clrState(ZTS_STATE_STACK_RUNNING);
// Wait until the main lwIP thread has exited // Wait until the main lwIP thread has exited
if (_has_started) { if (_has_started) {
while (! _has_exited) { while (! _has_exited) {
zts_delay_ms(LWIP_DRIVER_LOOP_INTERVAL); zts_util_delay(LWIP_DRIVER_LOOP_INTERVAL);
} }
} }
} }
@@ -432,7 +432,7 @@ err_t _lwip_eth_tx(struct netif* n, struct pbuf* p)
return ERR_IF; return ERR_IF;
} }
struct pbuf* q; struct pbuf* q;
char buf[ZT_MAX_MTU + 32]; char buf[ZT_MAX_MTU + 32] = { 0 };
char* bufptr; char* bufptr;
int totalLength = 0; int totalLength = 0;
@@ -454,29 +454,8 @@ err_t _lwip_eth_tx(struct netif* n, struct pbuf* p)
char* data = buf + sizeof(struct eth_hdr); char* data = buf + sizeof(struct eth_hdr);
int len = totalLength - sizeof(struct eth_hdr); int len = totalLength - sizeof(struct eth_hdr);
int proto = Utils::ntoh((uint16_t)ethhdr->type); int proto = Utils::ntoh((uint16_t)ethhdr->type);
tap->_handler(tap->_arg, NULL, tap->_nwid, src_mac, dest_mac, proto, 0, data, len); tap->_handler(tap->_arg, NULL, tap->_net_id, src_mac, dest_mac, proto, 0, data, len);
if (ZT_MSG_TRANSFER == true) {
char flagbuf[32];
memset(&flagbuf, 0, 32);
char macBuf[ZTS_MAC_ADDRSTRLEN], nodeBuf[16];
snprintf(
macBuf,
ZTS_MAC_ADDRSTRLEN,
"%02x:%02x:%02x:%02x:%02x:%02x",
ethhdr->dest.addr[0],
ethhdr->dest.addr[1],
ethhdr->dest.addr[2],
ethhdr->dest.addr[3],
ethhdr->dest.addr[4],
ethhdr->dest.addr[5]);
MAC mac;
mac.setTo(ethhdr->dest.addr, 6);
mac.toAddress(tap->_nwid).toString(nodeBuf);
/*
DEBUG_TRANS("len=%5d dst=%s [%s TX <-- %s] ethertype=0x%04x %s", totalLength, macBuf,
nodeBuf, tap->nodeId().c_str(), Utils::ntoh(ethhdr->type), flagbuf);
*/
}
return ERR_OK; return ERR_OK;
} }
@@ -491,7 +470,7 @@ void _lwip_eth_rx(
#ifdef LWIP_STATS #ifdef LWIP_STATS
stats_display(); stats_display();
#endif #endif
if (! _getState(ZTS_STATE_STACK_RUNNING)) { if (! zts_events->getState(ZTS_STATE_STACK_RUNNING)) {
return; return;
} }
struct pbuf *p, *q; struct pbuf *p, *q;
@@ -500,40 +479,19 @@ void _lwip_eth_rx(
to.copyTo(ethhdr.dest.addr, 6); to.copyTo(ethhdr.dest.addr, 6);
ethhdr.type = Utils::hton((uint16_t)etherType); ethhdr.type = Utils::hton((uint16_t)etherType);
if (ZT_MSG_TRANSFER == true) {
char flagbuf[32];
memset(&flagbuf, 0, 32);
char macBuf[ZTS_MAC_ADDRSTRLEN], nodeBuf[16];
snprintf(
macBuf,
ZTS_MAC_ADDRSTRLEN,
"%02x:%02x:%02x:%02x:%02x:%02x",
ethhdr.dest.addr[0],
ethhdr.dest.addr[1],
ethhdr.dest.addr[2],
ethhdr.dest.addr[3],
ethhdr.dest.addr[4],
ethhdr.dest.addr[5]);
MAC mac;
mac.setTo(ethhdr.src.addr, 6);
mac.toAddress(tap->_nwid).toString(nodeBuf);
/*
DEBUG_TRANS("len=%5d dst=%s [%s RX --> %s] ethertype=0x%04x %s", len, macBuf, nodeBuf,
tap->nodeId().c_str(), Utils::ntoh(ethhdr.type), flagbuf);
*/
}
p = pbuf_alloc(PBUF_RAW, (uint16_t)len + sizeof(struct eth_hdr), PBUF_RAM); p = pbuf_alloc(PBUF_RAW, (uint16_t)len + sizeof(struct eth_hdr), PBUF_RAM);
if (! p) { if (! p) {
DEBUG_ERROR("dropped packet: unable to allocate memory for pbuf"); // DEBUG_ERROR("dropped packet: unable to allocate memory for
// pbuf");
return; return;
} }
// First pbuf gets ethernet header at start // First pbuf gets Ethernet header at start
q = p; q = p;
if (q->len < sizeof(ethhdr)) { if (q->len < sizeof(ethhdr)) {
pbuf_free(p); pbuf_free(p);
p = NULL; p = NULL;
DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header"); // DEBUG_ERROR("dropped packet: first pbuf smaller than Ethernet
// header");
return; return;
} }
// Copy frame data into pbuf // Copy frame data into pbuf
@@ -554,7 +512,7 @@ void _lwip_eth_rx(
if (tap->netif4) { if (tap->netif4) {
if ((err = ((struct netif*)tap->netif4)->input(p, (struct netif*)tap->netif4)) if ((err = ((struct netif*)tap->netif4)->input(p, (struct netif*)tap->netif4))
!= ERR_OK) { != ERR_OK) {
DEBUG_ERROR("packet input error (%d)", err); // DEBUG_ERROR("packet input error (%d)", err);
pbuf_free(p); pbuf_free(p);
} }
} }
@@ -563,7 +521,7 @@ void _lwip_eth_rx(
if (tap->netif6) { if (tap->netif6) {
if ((err = ((struct netif*)tap->netif6)->input(p, (struct netif*)tap->netif6)) if ((err = ((struct netif*)tap->netif6)->input(p, (struct netif*)tap->netif6))
!= ERR_OK) { != ERR_OK) {
DEBUG_ERROR("packet input error (%d)", err); // DEBUG_ERROR("packet input error (%d)", err);
pbuf_free(p); pbuf_free(p);
} }
} }
@@ -581,59 +539,6 @@ bool _lwip_is_netif_up(void* n)
return result; return result;
} }
/*
static struct zts_netif_details *_lwip_prepare_netif_status_msg(struct netif *n)
{
if (!n || !n->state) {
return NULL;
}
VirtualTap *tap = (VirtualTap *)n->state;
struct zts_netif_details *details = new zts_netif_details();
details->nwid = tap->_nwid;
memcpy(&(details->mac), n->hwaddr, n->hwaddr_len);
details->mtu = n->mtu;
return details;
}
static void _netif_remove_callback(struct netif *n)
{
// Called from core, no need to lock
if (!n || !n->state) {
return;
}
VirtualTap *tap = (VirtualTap *)n->state;
struct zts_netif_details *details = new zts_netif_details();
details->nwid = tap->_nwid;
details->mac = 0;
details->mtu = 0;
_enqueueEvent(ZTS_EVENT_NETIF_REMOVED, (void*)details);
}
static void _netif_status_callback(struct netif *n)
{
// Called from core, no need to lock
if (!n) {
return;
} if (netif_is_up(n)) {
_enqueueEvent(ZTS_EVENT_NETIF_UP, (void*)_lwip_prepare_netif_status_msg(n));
} else {
_enqueueEvent(ZTS_EVENT_NETIF_DOWN, (void*)_lwip_prepare_netif_status_msg(n));
}
}
static void _netif_link_callback(struct netif *n)
{
// Called from core, no need to lock
if (!n) {
return;
} if (netif_is_link_up(n)) {
_enqueueEvent(ZTS_EVENT_NETIF_LINK_UP, (void*)_lwip_prepare_netif_status_msg(n));
} else {
_enqueueEvent(ZTS_EVENT_NETIF_LINK_DOWN, (void*)_lwip_prepare_netif_status_msg(n));
}
}
*/
static err_t _netif_init4(struct netif* n) static err_t _netif_init4(struct netif* n)
{ {
if (! n || ! n->state) { if (! n || ! n->state) {
@@ -676,7 +581,7 @@ static err_t _netif_init6(struct netif* n)
void _lwip_init_interface(void* tapref, const InetAddress& ip) void _lwip_init_interface(void* tapref, const InetAddress& ip)
{ {
char macbuf[ZTS_MAC_ADDRSTRLEN]; char macbuf[ZTS_MAC_ADDRSTRLEN] = { 0 };
VirtualTap* vtap = (VirtualTap*)tapref; VirtualTap* vtap = (VirtualTap*)tapref;
struct netif* n = NULL; struct netif* n = NULL;
@@ -691,11 +596,7 @@ void _lwip_init_interface(void* tapref, const InetAddress& ip)
isNewNetif = true; isNewNetif = true;
netifCount++; netifCount++;
} }
/*
netif_set_status_callback(n, _netif_status_callback);
netif_set_remove_callback(n, _netif_remove_callback);
netif_set_link_callback(n, _netif_link_callback);
*/
static ip4_addr_t ip4, netmask, gw; static ip4_addr_t ip4, netmask, gw;
IP4_ADDR(&gw, 127, 0, 0, 1); IP4_ADDR(&gw, 127, 0, 0, 1);
ip4.addr = *((u32_t*)ip.rawIpData()); ip4.addr = *((u32_t*)ip.rawIpData());
@@ -714,12 +615,6 @@ void _lwip_init_interface(void* tapref, const InetAddress& ip)
n->hwaddr[3], n->hwaddr[3],
n->hwaddr[4], n->hwaddr[4],
n->hwaddr[5]); n->hwaddr[5]);
/*
char nmbuf[INET6_ADDRSTRLEN];
char ipbuf[INET6_ADDRSTRLEN];
DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, nm=%s, tap=%p]",n,
macbuf, ip.toString(ipbuf), ip.netmask().toString(nmbuf), vtap);
*/
} }
if (ip.isV6()) { if (ip.isV6()) {
if (vtap->netif6) { if (vtap->netif6) {
@@ -729,11 +624,7 @@ void _lwip_init_interface(void* tapref, const InetAddress& ip)
n = new struct netif; n = new struct netif;
isNewNetif = true; isNewNetif = true;
netifCount++; netifCount++;
} /* }
netif_set_status_callback(n, _netif_status_callback);
netif_set_remove_callback(n, _netif_remove_callback);
netif_set_link_callback(n, _netif_link_callback);
*/
static ip6_addr_t ip6; static ip6_addr_t ip6;
memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr)); memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr));
LOCK_TCPIP_CORE(); LOCK_TCPIP_CORE();
@@ -760,10 +651,6 @@ void _lwip_init_interface(void* tapref, const InetAddress& ip)
n->hwaddr[3], n->hwaddr[3],
n->hwaddr[4], n->hwaddr[4],
n->hwaddr[5]); n->hwaddr[5]);
/*
DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n,
macbuf, ip.toString(ipbuf), vtap);
*/
} }
} }

View File

@@ -4,7 +4,7 @@
* Use of this software is governed by the Business Source License included * Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file in the project's root directory. * 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 * 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. * of this software will be governed by version 2.0 of the Apache License.
@@ -14,16 +14,18 @@
/** /**
* @file * @file
* *
* Header for virtual ethernet tap device and combined network stack driver * Header for virtual Ethernet tap device and combined network stack driver
*/ */
#ifndef ZT_VIRTUAL_TAP_HPP #ifndef ZTS_VIRTUAL_TAP_HPP
#define ZT_VIRTUAL_TAP_HPP #define ZTS_VIRTUAL_TAP_HPP
#include "lwip/err.h" #include "lwip/err.h"
#define ZTS_LWIP_DRIVER_THREAD_NAME "ZTNetworkStackThread" #define ZTS_LWIP_THREAD_NAME "ZTNetworkStackThread"
#define VTAP_NAME_LEN 64
#include "Events.hpp"
#include "MAC.hpp" #include "MAC.hpp"
#include "Phy.hpp" #include "Phy.hpp"
#include "Thread.hpp" #include "Thread.hpp"
@@ -36,8 +38,8 @@ class MulticastGroup;
struct InetAddress; struct InetAddress;
/** /**
* A virtual tap device. The ZeroTier Node Service will create one per * Virtual tap device. ZeroTier will create one per joined network. It will
* joined network. It will be destroyed upon leave(). * then be destroyed upon leaving the network.
*/ */
class VirtualTap { class VirtualTap {
friend class Phy<VirtualTap*>; friend class Phy<VirtualTap*>;
@@ -48,7 +50,7 @@ class VirtualTap {
const MAC& mac, const MAC& mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t net_id,
const char* friendlyName, const char* friendlyName,
void (*handler)( void (*handler)(
void*, void*,
@@ -67,10 +69,18 @@ class VirtualTap {
void setEnabled(bool en); void setEnabled(bool en);
bool enabled() const; bool enabled() const;
/**
* System to ingest events from this class and emit them to the user
*/
Events* _events;
/** /**
* Mutex for protecting IP address container for this tap. * Mutex for protecting IP address container for this tap.
*/ */
Mutex _ips_m; // Public because we want it accessible by the driver layer Mutex _ips_m; // Public because we want it accessible by the driver
// layer
void setUserEventSystem(Events* events);
/** /**
* Return whether this tap has been assigned an IPv4 address. * Return whether this tap has been assigned an IPv4 address.
@@ -83,13 +93,15 @@ class VirtualTap {
bool hasIpv6Addr(); bool hasIpv6Addr();
/** /**
* Adds an address to the user-space stack interface associated with this VirtualTap * Adds an address to the user-space stack interface associated with
* this VirtualTap
* - Starts VirtualTap main thread ONLY if successful * - Starts VirtualTap main thread ONLY if successful
*/ */
bool addIp(const InetAddress& ip); bool addIp(const InetAddress& ip);
/** /**
* Removes an address from the user-space stack interface associated with this VirtualTap * Removes an address from the user-space stack interface associated
* with this VirtualTap
*/ */
bool removeIp(const InetAddress& ip); bool removeIp(const InetAddress& ip);
@@ -99,16 +111,6 @@ class VirtualTap {
void void
put(const MAC& from, const MAC& to, unsigned int etherType, const void* data, unsigned int len); put(const MAC& from, const MAC& to, unsigned int etherType, const void* data, unsigned int len);
/**
* Get VirtualTap device name (e.g. 'libzt17d72843bc2c5760')
*/
std::string deviceName() const;
/**
* Set friendly name
*/
void setFriendlyName(const char* friendlyName);
/** /**
* Scan multicast groups * Scan multicast groups
*/ */
@@ -142,16 +144,15 @@ class VirtualTap {
void* netif4 = NULL; void* netif4 = NULL;
void* netif6 = NULL; void* netif6 = NULL;
/** // The last time that this virtual tap received a network config update
* The last time that this virtual tap received a network config update from the core // from the core
*/
uint64_t _lastConfigUpdateTime = 0; uint64_t _lastConfigUpdateTime = 0;
void lastConfigUpdate(uint64_t lastConfigUpdateTime); void lastConfigUpdate(uint64_t lastConfigUpdateTime);
int _networkStatus = 0; int _networkStatus = 0;
char vtap_full_name[64]; char vtap_full_name[VTAP_NAME_LEN] = { 0 };
std::vector<InetAddress> ips() const; std::vector<InetAddress> ips() const;
std::vector<InetAddress> _ips; std::vector<InetAddress> _ips;
@@ -163,22 +164,20 @@ class VirtualTap {
volatile bool _run; volatile bool _run;
MAC _mac; MAC _mac;
unsigned int _mtu; unsigned int _mtu;
uint64_t _nwid; uint64_t _net_id;
PhySocket* _unixListenSocket; PhySocket* _unixListenSocket;
Phy<VirtualTap*> _phy; Phy<VirtualTap*> _phy;
Thread _thread; Thread _thread;
int _shutdownSignalPipe[2]; int _shutdownSignalPipe[2] = { 0 };
std::string _dev; // path to Unix domain socket
std::vector<MulticastGroup> _multicastGroups; std::vector<MulticastGroup> _multicastGroups;
Mutex _multicastGroups_m; Mutex _multicastGroups_m;
////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------//
// Not used in this implementation // // Not used in this implementation //
////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------//
void phyOnDatagram( void phyOnDatagram(
PhySocket* sock, PhySocket* sock,
@@ -210,7 +209,8 @@ bool _lwip_is_netif_up(void* netif);
/** /**
* @brief Increase the delay multiplier for the main driver loop * @brief Increase the delay multiplier for the main driver loop
* *
* @usage This should be called when we know the stack won't be used by any virtual taps * @usage This should be called when we know the stack won't be used by any
* virtual taps
*/ */
void _lwip_hibernate_driver(); void _lwip_hibernate_driver();
@@ -229,17 +229,19 @@ bool _lwip_is_up();
/** /**
* @brief Initialize network stack semaphores, threads, and timers. * @brief Initialize network stack semaphores, threads, and timers.
* *
* @usage This is called during the initial setup of each VirtualTap but is only allowed to execute * @usage This is called during the initial setup of each VirtualTap but is
* once * only allowed to execute once
*/ */
void _lwip_driver_init(); void _lwip_driver_init();
/** /**
* @brief Shutdown the stack as completely as possible (not officially supported by lwIP) * @brief Shutdown the stack as completely as possible (not officially
* supported by lwIP)
* *
* @usage This is to be called after it is determined that no further network activity will take * @usage This is to be called after it is determined that no further
* place. The tcpip thread will be stopped, all interfaces will be brought down and all resources * network activity will take place. The tcpip thread will be stopped, all
* will be deallocated. A full application restart will be required to bring the stack back online. * interfaces will be brought down and all resources will be deallocated. A
* full application restart will be required to bring the stack back online.
*/ */
void _lwip_driver_shutdown(); void _lwip_driver_shutdown();
@@ -280,7 +282,8 @@ static void _netif_link_callback(struct netif* netif);
/** /**
* @brief Set up an interface in the network stack for the VirtualTap. * @brief Set up an interface in the network stack for the VirtualTap.
* *
* @param tapref Reference to VirtualTap that will be responsible for sending and receiving data * @param tapref Reference to VirtualTap that will be responsible for
* sending and receiving data
* @param ip Virtual IP address for this ZeroTier VirtualTap interface * @param ip Virtual IP address for this ZeroTier VirtualTap interface
*/ */
void _lwip_init_interface(void* tapref, const InetAddress& ip); void _lwip_init_interface(void* tapref, const InetAddress& ip);
@@ -294,12 +297,13 @@ void _lwip_init_interface(void* tapref, const InetAddress& ip);
void _lwip_remove_address_from_netif(void* tapref, const InetAddress& ip); void _lwip_remove_address_from_netif(void* tapref, const InetAddress& ip);
/** /**
* @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier * @brief Called from the stack, outbound Ethernet frames from the network
* virtual wire here. * stack enter the ZeroTier virtual wire here.
* *
* @usage This shall only be called from the stack or the stack driver. Not the application thread. * @usage This shall only be called from the stack or the stack driver. Not
* @param netif Transmits an outgoing Ethernet fram from the network stack onto the ZeroTier virtual * the application thread.
* wire * @param netif Transmits an outgoing Ethernet frame from the network stack
* onto the ZeroTier virtual wire
* @param p A pointer to the beginning of a chain pf struct pbufs * @param p A pointer to the beginning of a chain pf struct pbufs
* @return * @return
*/ */
@@ -308,10 +312,12 @@ err_t _lwip_eth_tx(struct netif* netif, struct pbuf* p);
/** /**
* @brief Receives incoming Ethernet frames from the ZeroTier virtual wire * @brief Receives incoming Ethernet frames from the ZeroTier virtual wire
* *
* @usage This shall be called from the VirtualTap's I/O thread (via VirtualTap::put()) * @usage This shall be called from the VirtualTap's I/O thread (via
* VirtualTap::put())
* @param tap Pointer to VirtualTap from which this data comes * @param tap Pointer to VirtualTap from which this data comes
* @param from Origin address (virtual ZeroTier hardware address) * @param from Origin address (virtual ZeroTier hardware address)
* @param to Intended destination address (virtual ZeroTier hardware address) * @param to Intended destination address (virtual ZeroTier hardware
* address)
* @param etherType Protocol type * @param etherType Protocol type
* @param data Pointer to Ethernet frame * @param data Pointer to Ethernet frame
* @param len Length of Ethernet frame * @param len Length of Ethernet frame

View File

@@ -1,835 +0,0 @@
/**
* Selftest. To be run for every commit.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#include <ZeroTierSockets.h>
#pragma GCC diagnostic ignored "-Wunused-value"
int random32() {
const int BITS_PER_RAND = (int)(log2(RAND_MAX/2 + 1) + 1.0);
int ret = 0;
for (int i = 0; i < sizeof(int) * CHAR_BIT; i += BITS_PER_RAND) {
ret <<= BITS_PER_RAND;
ret |= rand();
}
return ret;
}
uint64_t random64() {
return ((uint64_t)random32() << 32) | random32();
}
int is_online = 0;
int has_ip4 = 0;
int has_ip6 = 0;
//----------------------------------------------------------------------------//
// Event Handler //
//----------------------------------------------------------------------------//
void on_zts_event(void *msgPtr)
{
struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr;
fprintf(stderr, "event=%d\n", msg->eventCode);
if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) {
fprintf(stderr, "ZTS_EVENT_NODE_ONLINE\n");
is_online = 1;
}
if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) {
fprintf(stderr, "ZTS_EVENT_NETWORK_READY_IP4\n");
has_ip4 = 1;
}
if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) {
fprintf(stderr, "ZTS_EVENT_NETWORK_READY_IP6\n");
has_ip6 = 1;
}
}
void api_value_arg_test(
int8_t i8, int16_t i16, int32_t i32, int64_t i64, void* nullable)
{
//fprintf(stderr, "%d, %d, %d, %lld, %p\n", i8, i16, i32, i64, nullable);
int res = ZTS_ERR_OK;
//----------------------------------------------------------------------------//
// Test uninitialized Network Stack API usage //
//----------------------------------------------------------------------------//
/*
res = zts_get_all_stats((struct zts_stats *)nullable);
assert(("pre-init call to zts_get_all_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_get_protocol_stats(i32, nullable);
assert(("pre-init call to zts_get_protocol_stats(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
*/
res = zts_dns_set_server(i8, (const zts_ip_addr *)nullable);
assert(("pre-init call to zts_add_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
const zts_ip_addr *res_ptr = zts_dns_get_server(i8);
assert(("pre-init call to zts_del_dns_nameserver(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//----------------------------------------------------------------------------//
// Test uninitialized Node API usage //
//----------------------------------------------------------------------------//
res = zts_stop();
assert(("pre-init call to zts_stop(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_restart();
assert(("pre-init call to zts_restart(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_free();
assert(("pre-init call to zts_free(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_join(i64);
assert(("pre-init call to zts_join(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_leave(i64);
assert(("pre-init call to zts_leave(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_orbit(i64,i64);
assert(("pre-init call to zts_orbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_deorbit(i64);
assert(("pre-init call to zts_deorbit(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
//----------------------------------------------------------------------------//
// Test uninitialized Socket API usage //
//----------------------------------------------------------------------------//
res = zts_socket(i32,i32,i32);
assert(("pre-init call to zts_socket(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_connect(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_connect(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_bind(i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_bind(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_listen(i32, i32);
assert(("pre-init call to zts_listen(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_accept(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_accept(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_setsockopt(i32, i32, i32, nullable, i32);
assert(("pre-init call to zts_setsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockopt(i32, i32, i32, nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockopt(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getsockname(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getsockname(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_getpeername(i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_getpeername(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_close(i32);
assert(("pre-init call to zts_close(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_select(i32, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (zts_fd_set *)nullable, (struct zts_timeval *)nullable);
assert(("pre-init call to zts_select(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_fcntl(i32, i32, i32);
assert(("pre-init call to zts_fcntl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_poll((struct zts_pollfd *)nullable, i32, i32);
assert(("pre-init call to zts_poll(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_ioctl(i32, i64, nullable);
assert(("pre-init call to zts_ioctl(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_send(i32, nullable, i32, i32);
assert(("pre-init call to zts_send(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendto(i32, nullable, i32, i32, (const struct zts_sockaddr *)nullable, i32);
assert(("pre-init call to zts_sendto(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_sendmsg(i32, (const struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_sendmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recv(i32, nullable, i32, i32);
assert(("pre-init call to zts_recv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvfrom(i32, nullable, i32, i32, (struct zts_sockaddr *)nullable, (zts_socklen_t *)nullable);
assert(("pre-init call to zts_recvfrom(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_recvmsg(i32, (struct zts_msghdr *)nullable, i32);
assert(("pre-init call to zts_recvmsg(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_read(i32, nullable, i32);
assert(("pre-init call to zts_read(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_readv(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_readv(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_write(i32, nullable, i32);
assert(("pre-init call to zts_write(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_writev(i32, (const struct zts_iovec *)nullable, i32);
assert(("pre-init call to zts_writev(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
res = zts_shutdown(i32, i32);
assert(("pre-init call to zts_shutdown(): res != ZTS_ERR_SERVICE",
res == ZTS_ERR_SERVICE));
}
void test_pre_service()
{
//----------------------------------------------------------------------------//
// Test service-related API functions before initializing service //
//----------------------------------------------------------------------------//
// Test null values
api_value_arg_test(0,0,0,0,NULL);
// Test wild values
for (int i=0; i<4096; i++) {
int8_t i8 = (uint8_t)random64();
int16_t i16 = (uint16_t)random64();
int32_t i32 = (uint32_t)random64();
int64_t i64 = (uint64_t)random64();
int x;
void* nullable = &x;
api_value_arg_test(i8,i16,i32,i64,nullable);
}
//----------------------------------------------------------------------------//
// Test non-service helper functions //
//----------------------------------------------------------------------------//
// (B) Test zts_inet_ntop
char ipstr[ZTS_INET6_ADDRSTRLEN];
int16_t port = 0;
struct zts_sockaddr_in in4;
in4.sin_port = htons(8080);
#if defined(_WIN32)
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.S_addr));
#else
zts_inet_pton(ZTS_AF_INET, "192.168.22.1", &(in4.sin_addr.s_addr));
#endif
in4.sin_family = ZTS_AF_INET;
struct zts_sockaddr *sa = (struct zts_sockaddr *)&in4;
if (sa->sa_family == ZTS_AF_INET) {
struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa;
zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr),
ipstr, ZTS_INET_ADDRSTRLEN);
port = ntohs(in4->sin_port);
}
if (sa->sa_family == ZTS_AF_INET6) {
struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa;
zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr),
ipstr, ZTS_INET6_ADDRSTRLEN);
}
assert(("zts_inet_ntop(): port != 8080", port == 8080));
assert(("zts_inet_ntop(): strcmp(ipstr, \"192.168.22.1\") != 0",
!strcmp(ipstr, "192.168.22.1")));
// (C) Test zts_inet_pton
uint8_t buf[sizeof(struct zts_in6_addr)];
char str[ZTS_INET6_ADDRSTRLEN];
zts_inet_pton(ZTS_AF_INET, "192.168.22.2", buf);
zts_inet_ntop(ZTS_AF_INET, buf, str, ZTS_INET6_ADDRSTRLEN);
assert(("zts_inet_pton(): strcmp(ipstr, \"192.168.22.2\") != 0",
!strcmp(str, "192.168.22.2")));
}
void test_service()
{
int res = ZTS_ERR_OK;
//----------------------------------------------------------------------------//
// Test simplified API, proxy for setsockopt/getsockopt/ioctl etc //
//----------------------------------------------------------------------------//
int s4 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(s4 >= 0);
// TCP_NODELAY
// Check value before doing anything
res = zts_get_no_delay(s4);
assert(res == 0);
// Turn on
res = zts_set_no_delay(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_no_delay(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_no_delay(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_no_delay(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
// SO_LINGER
// Check value before doing anything
res = zts_get_linger_enabled(s4);
assert(res == 0);
res = zts_get_linger_value(s4);
assert(res == 0);
// Turn on, set to 7 seconds
res = zts_set_linger(s4, 1, 7);
res = zts_get_linger_enabled(s4);
assert(res == 1);
res = zts_get_linger_value(s4);
assert(res == 7);
res = zts_set_linger(s4, 0, 0);
// Turn off
res = zts_get_linger_enabled(s4);
assert(res == 0);
res = zts_get_linger_value(s4);
assert(res == 0);
// SO_REUSEADDR
// Check value before doing anything
res = zts_get_reuse_addr(s4);
assert(res == 0);
// Turn on
res = zts_set_reuse_addr(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_reuse_addr(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_reuse_addr(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_reuse_addr(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
// SO_RCVTIMEO
// Check value before doing anything
res = zts_get_recv_timeout(s4);
assert(res == 0);
// Set to value
res = zts_set_recv_timeout(s4, 3, 0);
res = zts_get_recv_timeout(s4);
assert(res == 3);
res = zts_set_recv_timeout(s4, 0, 0);
// Set to zero
res = zts_get_recv_timeout(s4);
assert(res == 0);
// SO_SNDTIMEO
// Check value before doing anything
res = zts_get_send_timeout(s4);
assert(res == 0);
// Set to value
res = zts_set_send_timeout(s4, 4, 0);
res = zts_get_send_timeout(s4);
assert(res == 4);
res = zts_set_send_timeout(s4, 0, 0);
// Set to zero
res = zts_get_send_timeout(s4);
assert(res == 0);
// SO_SNDBUF
// Check value before doing anything
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
// Set to 7 seconds
res = zts_set_send_buf_size(s4, 1024);
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
res = zts_set_send_buf_size(s4, 0);
// Set to zero
res = zts_get_send_buf_size(s4);
assert(res == -1); // Unimplemented as of writing of test
// SO_RCVBUF
// Check value before doing anything
res = zts_get_recv_buf_size(s4);
assert(res > 0);
// Set to value
res = zts_set_recv_buf_size(s4, 1024);
res = zts_get_recv_buf_size(s4);
assert(res == 1024);
res = zts_set_recv_buf_size(s4, 0);
// Set to zero
res = zts_get_recv_buf_size(s4);
assert(res == 0);
// IP_TTL
// Check value before doing anything
res = zts_get_ttl(s4);
assert(res == 255); // Defaults to max
// Set to value
res = zts_set_ttl(s4, 128);
res = zts_get_ttl(s4);
assert(res == 128);
res = zts_set_ttl(s4, 0);
// Set to zero
res = zts_get_ttl(s4);
assert(res == 0);
// O_NONBLOCK
// Check value before doing anything
res = zts_get_blocking(s4);
assert(res == 1);
// Turn off (non-blocking)
res = zts_set_blocking(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_blocking(s4);
// Should return value instead of error code
assert(res == 0);
// Turn off
res = zts_set_blocking(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_blocking(s4);
assert(res == 1);
// SO_KEEPALIVE
// Check value before doing anything
res = zts_get_keepalive(s4);
assert(res == 0);
// Turn on
res = zts_set_keepalive(s4, 1);
assert(res == ZTS_ERR_OK);
res = zts_get_keepalive(s4);
// Should return value instead of error code
assert(res == 1);
// Turn off
res = zts_set_keepalive(s4, 0);
assert(res == ZTS_ERR_OK);
res = zts_get_keepalive(s4);
assert(res == ZTS_ERR_OK);
assert(res == 0);
//----------------------------------------------------------------------------//
// Test DNS client functionality //
//----------------------------------------------------------------------------//
/*
// Set first nameserver
char *ns1_addr_str = "FCC5:205E:4FF5:5311:DFF0::1";
zts_ip_addr ns1;
zts_ipaddr_aton(ns1_addr_str, &ns1);
zts_dns_set_server(0, &ns1);
// Get first nameserver
const zts_ip_addr *ns1_result;
ns1_result = zts_dns_get_server(0);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns1_result));
// Set second nameserver
char *ns2_addr_str = "192.168.22.1";
zts_ip_addr ns2;
zts_ipaddr_aton(ns2_addr_str, &ns2);
zts_dns_set_server(1, &ns2);
// Get second nameserver
const zts_ip_addr *ns2_result;
ns2_result = zts_dns_get_server(1);
printf("dns1 = %s\n", zts_ipaddr_ntoa(ns2_result));
// Check that each nameserver address was properly set and get
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns1_addr_str, zts_ipaddr_ntoa(ns1_result))));
assert(("zts_dns_get_server(): Address mismatch", !strcmp(ns2_addr_str, zts_ipaddr_ntoa(ns2_result))));
*/
//----------------------------------------------------------------------------//
// Test shutting down the service //
//----------------------------------------------------------------------------//
zts_stop();
s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s4 != ZTS_ERR_SERVICE, not shut down", s4 == ZTS_ERR_SERVICE));
}
//----------------------------------------------------------------------------//
// Server //
//----------------------------------------------------------------------------//
#define MAX_CONNECT_TIME 60 // outer re-attempt loop
#define CONNECT_TIMEOUT 30 // zts_connect_easy, ms
#define BUFLEN 128
char *msg = "welcome to the machine";
void start_server_app(uint16_t port4, uint16_t port6)
{
int err = ZTS_ERR_OK;
int bytes_read = 0;
int bytes_sent = 0;
int msglen = strlen(msg);
char dstbuf[BUFLEN];
int buflen = BUFLEN;
struct timespec start, now;
int time_diff = 0;
//
// IPv4 test
//
fprintf(stderr, "server4: will listen on: 0.0.0.0:%d\n", port4);
int s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(s4 == 0 && zts_errno == 0);
err = zts_bind_easy(s4, ZTS_AF_INET, "0.0.0.0", port4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
err = zts_listen(s4, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
struct zts_sockaddr_in in4;
zts_socklen_t addrlen4 = sizeof(in4);
int acc4 = -1;
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "server4: accepting...\n");
acc4 = zts_accept(s4, &in4, &addrlen4);
zts_delay_ms(250);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(acc4 == 1 && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(acc4, dstbuf, buflen);
fprintf(stderr, "server4: read (%d) bytes\n", bytes_read);
assert(bytes_read == msglen && zts_errno == 0);
// Send message
bytes_sent = zts_write(acc4, msg, msglen);
fprintf(stderr, "server4: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
zts_close(s4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_close(acc4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
assert(bytes_sent == bytes_read);
if (bytes_sent == bytes_read) {
fprintf(stderr, "server4: Test OK\n");
} else {
fprintf(stderr, "server4: Test FAIL\n");
}
//
// IPv6 test
//
fprintf(stderr, "server: will listen on: [::]:%d\n", port6);
int s6 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(s6 == 0 && zts_errno == 0);
err = zts_bind_easy(s6, ZTS_AF_INET6, "::", port6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
err = zts_listen(s6, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
struct zts_sockaddr_in6 in6;
zts_socklen_t addrlen6 = sizeof(in6);
int acc6 = -1;
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "server6: accepting...\n");
acc6 = zts_accept(s6, &in6, &addrlen6);
zts_delay_ms(250);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
fprintf(stderr, "server6: accepted connection (fd=%d)\n", acc6);
assert(acc6 == 1 && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(acc6, dstbuf, buflen);
fprintf(stderr, "server6: read (%d) bytes\n", bytes_read);
assert(bytes_read == msglen && zts_errno == 0);
// Send message
bytes_sent = zts_write(acc6, msg, msglen);
fprintf(stderr, "server6: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
zts_close(s6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_close(acc6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_stop();
assert(err == ZTS_ERR_OK && zts_errno == 0);
int s = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s != ZTS_ERR_SERVICE, not shut down", s == ZTS_ERR_SERVICE));
assert(bytes_sent == bytes_read);
if (bytes_sent == bytes_read) {
fprintf(stderr, "server6: Test OK\n");
} else {
fprintf(stderr, "server6: Test FAIL\n");
}
}
//----------------------------------------------------------------------------//
// Client //
//----------------------------------------------------------------------------//
void start_client_app(char *ip4, uint16_t port4, char *ip6, uint16_t port6)
{
int err = ZTS_ERR_OK;
int bytes_read = 0;
int bytes_sent = 0;
int msglen = strlen(msg);
char dstbuf[BUFLEN];
int buflen = BUFLEN;
struct timespec start, now;
int time_diff = 0;
//
// IPv4 test
//
err = ZTS_ERR_OK;
int s4 = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_set_blocking(s4, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "client4: connecting to: %s:%d\n", ip4, port4);
err = zts_connect_easy(s4, ZTS_AF_INET, ip4, port4, CONNECT_TIMEOUT);
zts_delay_ms(500);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(err == ZTS_ERR_OK && zts_errno == 0);
fprintf(stderr, "client4: connected\n");
// Send message
bytes_sent = zts_write(s4, msg, msglen);
fprintf(stderr, "client4: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(s4, dstbuf, buflen);
assert(bytes_read == msglen && zts_errno == 0);
fprintf(stderr, "client4: read (%d) bytes\n", bytes_read);
assert(bytes_sent == bytes_read && zts_errno == 0);
zts_close(s4);
assert(err == ZTS_ERR_OK && zts_errno == 0);
assert(bytes_sent == bytes_read);
if (bytes_sent == bytes_read) {
fprintf(stderr, "client4: Test OK\n");
} else {
fprintf(stderr, "client4: Test FAIL\n");
}
//
// IPv6 test
//
err = ZTS_ERR_OK;
int s6 = zts_socket(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_set_blocking(s6, 1);
assert(err == ZTS_ERR_OK && zts_errno == 0);
clock_gettime(CLOCK_MONOTONIC, &start);
do {
fprintf(stderr, "client6: connecting to: %s:%d\n", ip6, port6);
err = zts_connect_easy(s6, ZTS_AF_INET6, ip6, port6, CONNECT_TIMEOUT);
zts_delay_ms(500);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (err < 0 && time_diff < MAX_CONNECT_TIME);
assert(err == ZTS_ERR_OK && zts_errno == 0);
fprintf(stderr, "client6: connected\n");
// Send message
bytes_sent = zts_write(s6, msg, msglen);
fprintf(stderr, "client6: wrote (%d) bytes\n", bytes_sent);
assert(bytes_sent == msglen && zts_errno == 0);
// Read message
memset(dstbuf, 0, buflen);
bytes_read = zts_read(s6, dstbuf, buflen);
assert(bytes_read == msglen && zts_errno == 0);
fprintf(stderr, "client6: read (%d) bytes\n", bytes_read);
assert(bytes_sent == bytes_read && zts_errno == 0);
zts_close(s6);
assert(err == ZTS_ERR_OK && zts_errno == 0);
zts_stop();
assert(err == ZTS_ERR_OK && zts_errno == 0);
int s = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0);
assert(("s != ZTS_ERR_SERVICE, not shut down", s == ZTS_ERR_SERVICE));
assert(bytes_sent == bytes_read);
if (bytes_sent == bytes_read) {
fprintf(stderr, "client6: Test OK\n");
} else {
fprintf(stderr, "client6: Test FAIL\n");
}
}
//----------------------------------------------------------------------------//
// Start node //
//----------------------------------------------------------------------------//
void start_node(char *path, uint64_t nwid)
{
struct timespec start, now;
int time_diff = 0;
fprintf(stderr, "starting node...\n");
clock_gettime(CLOCK_MONOTONIC, &start);
int res = zts_start(path, &on_zts_event, 0);
assert(("error starting service: res != ZTS_ERR_OK", res == ZTS_ERR_OK));
do {
zts_delay_ms(25);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while (!is_online && (time_diff < MAX_CONNECT_TIME));
if (!is_online) {
fprintf(stderr, "node failed to come online\n");
exit(-1);
}
fprintf(stderr, "joining: %llx\n", nwid);
clock_gettime(CLOCK_MONOTONIC, &start);
if (nwid) {
zts_join(nwid);
do {
zts_delay_ms(25);
clock_gettime(CLOCK_MONOTONIC, &now);
time_diff = (now.tv_sec - start.tv_sec);
} while ((!has_ip4 || !has_ip6) && (time_diff < MAX_CONNECT_TIME));
if (!has_ip4 || !has_ip6) {
fprintf(stderr, "node failed to receive assigned addresses\n");
exit(-1);
}
}
}
//----------------------------------------------------------------------------//
// Main //
//----------------------------------------------------------------------------//
int main(int argc, char **argv)
{
if (argc != 1 && argc != 5 && argc != 7) {
fprintf(stderr, "Invalid number of arguments.\n");
exit(-1);
}
//
// API fuzz test
//
test_pre_service();
//
// Default test
//
// selftest
if (argc == 1) {
srand(time(NULL));
// Store identities in cwd, join 0x0
start_node(".",0x0);
test_service();
exit(0);
}
// Default test (single node)
// selftest <id-path>
/*
if (argc == 2) {
srand(time(NULL));
start_node(argv[1],0x0);
test_service();
exit(0);
}*/
//
// Client/Server communication test
//
// Server test
if (argc == 5) {
//fprintf(stderr, "server.path = %s\n", argv[1]);
//fprintf(stderr, "server.nwid = %s\n", argv[2]);
//fprintf(stderr, "server.port4 = %s\n", argv[3]);
//fprintf(stderr, "server.port6 = %s\n", argv[4]);
uint64_t nwid = strtoull(argv[2],NULL,16);
int port4 = atoi(argv[3]);
int port6 = atoi(argv[4]);
start_node(argv[1],nwid);
start_server_app(port4, port6);
exit(0);
}
// Client test
if (argc == 7) {
//fprintf(stderr, "client.path = %s\n", argv[1]);
//fprintf(stderr, "client.nwid = %s\n", argv[2]);
//fprintf(stderr, "client.port4 = %s\n", argv[3]);
//fprintf(stderr, "client.ip4 = %s\n", argv[4]);
//fprintf(stderr, "client.port6 = %s\n", argv[5]);
//fprintf(stderr, "client.ip6 = %s\n", argv[6]);
uint64_t nwid = strtoull(argv[2],NULL,16);
int port4 = atoi(argv[3]);
int port6 = atoi(argv[5]);
start_node(argv[1],nwid);
start_client_app(argv[4], port4, argv[6], port6);
exit(0);
}
return 0;
}

1887
test/selftest.c Normal file

File diff suppressed because it is too large Load Diff