From bca8e1cda01072f7ca19cf6af357cad94ad208f6 Mon Sep 17 00:00:00 2001 From: Evan Olcott Date: Mon, 6 May 2019 14:47:36 -0500 Subject: [PATCH 01/78] iOS Simulator support Added support for the iOS Simulator in zt.framework for iOS. --- dist.sh | 61 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/dist.sh b/dist.sh index d9a8b93..19a24ea 100755 --- a/dist.sh +++ b/dist.sh @@ -36,8 +36,8 @@ BUILD_CONCURRENCY= OSNAME=$(uname | tr '[A-Z]' '[a-z]') BUILD_TMP=$(pwd)/tmp ANDROID_PROJ_DIR=$(pwd)/ports/android -XCODE_IOS_ARM64_PROJ_DIR=$(pwd)/ports/xcode_ios-arm64 -#XCODE_IOS_ARMV7_PROJ_DIR=$(pwd)/ports/xcode_ios-armv7 +XCODE_IOS_PROJ_DIR=$(pwd)/ports/xcode_ios +XCODE_IOS_SIMULATOR_PROJ_DIR=$(pwd)/ports/xcode_ios_simulator XCODE_MACOS_PROJ_DIR=$(pwd)/ports/xcode_macos # Generates projects if needed @@ -49,23 +49,24 @@ generate_projects() echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" if [[ $OSNAME = *"darwin"* ]]; then # iOS (SDK 11+, 64-bit only, arm64) - if [ ! -d "$XCODE_IOS_ARM64_PROJ_DIR" ]; then - mkdir -p $XCODE_IOS_ARM64_PROJ_DIR - cd $XCODE_IOS_ARM64_PROJ_DIR + if [ ! -d "$XCODE_IOS_PROJ_DIR" ]; then + mkdir -p $XCODE_IOS_PROJ_DIR + cd $XCODE_IOS_PROJ_DIR cmake -G Xcode ../../ -DIOS_FRAMEWORK=1 -DIOS_ARM64=1 # Manually replace arch strings in project file sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj cd - fi - # iOS (SDK <11, 32-bit only, armv7, armv7s) - #if [ ! -d "$XCODE_IOS_ARMV7_PROJ_DIR" ]; then - # mkdir -p $XCODE_IOS_ARMV7_PROJ_DIR - # cd $XCODE_IOS_ARMV7_PROJ_DIR - # cmake -G Xcode ../../ -DIOS_FRAMEWORK=1 -DIOS_ARMV7=1 + + if [ ! -d "$XCODE_IOS_SIMULATOR_PROJ_DIR" ]; then + mkdir -p $XCODE_IOS_SIMULATOR_PROJ_DIR + cd $XCODE_IOS_SIMULATOR_PROJ_DIR + cmake -G Xcode ../../ -DIOS_FRAMEWORK=1 # Manually replace arch strings in project file - # sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj - # cd - - #fi + #sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj + cd - + fi + # macOS if [ ! -d "$XCODE_MACOS_PROJ_DIR" ]; then mkdir -p $XCODE_MACOS_PROJ_DIR @@ -86,27 +87,29 @@ ios() echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" UPPERCASE_CONFIG="$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}" - # 64-bit - cd $XCODE_IOS_ARM64_PROJ_DIR + cd $XCODE_IOS_PROJ_DIR # Framework xcodebuild -arch arm64 -target zt -configuration "$UPPERCASE_CONFIG" -sdk "iphoneos" cd - - OUTPUT_DIR=$(pwd)/lib/$1/ios-arm64 - mkdir -p $OUTPUT_DIR - rm -rf $OUTPUT_DIR/zt.framework # Remove prior to move to prevent error - mv $XCODE_IOS_ARM64_PROJ_DIR/$UPPERCASE_CONFIG-iphoneos/* $OUTPUT_DIR + IOS_OUTPUT_DIR=$(pwd)/lib/$1/ios + mkdir -p $IOS_OUTPUT_DIR + rm -rf $IOS_OUTPUT_DIR/zt.framework # Remove prior to move to prevent error + mv $XCODE_IOS_PROJ_DIR/$UPPERCASE_CONFIG-iphoneos/* $IOS_OUTPUT_DIR - # 32-bit - #cd $XCODE_IOS_ARMV7_PROJ_DIR + cd $XCODE_IOS_SIMULATOR_PROJ_DIR # Framework - #xcodebuild -target zt -configuration "$UPPERCASE_CONFIG" -sdk "iphoneos10.0" - # Manually replace arch strings in project file - #sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj - #cd - - #OUTPUT_DIR=$(pwd)/lib/$1/ios-armv7 - #mkdir -p $OUTPUT_DIR - #rm -rf $OUTPUT_DIR/* - #mv $XCODE_IOS_ARMV7_PROJ_DIR/$UPPERCASE_CONFIG-iphoneos/* $OUTPUT_DIR + xcodebuild -target zt -configuration "$UPPERCASE_CONFIG" -sdk "iphonesimulator" + cd - + SIMULATOR_OUTPUT_DIR=$(pwd)/lib/$1/ios-simulator + mkdir -p $SIMULATOR_OUTPUT_DIR + rm -rf $SIMULATOR_OUTPUT_DIR/zt.framework # Remove prior to move to prevent error + mv $XCODE_IOS_SIMULATOR_PROJ_DIR/$UPPERCASE_CONFIG-iphonesimulator/* $SIMULATOR_OUTPUT_DIR + + # Combine the two archs + lipo -create $IOS_OUTPUT_DIR/zt.framework/zt $SIMULATOR_OUTPUT_DIR/zt.framework/zt -output $IOS_OUTPUT_DIR/zt.framework/zt + + # Clean up + rm -rf $SIMULATOR_OUTPUT_DIR } # Build framework for current host (macOS only) From d07b3a4f188a45f51bfca1646b2d44e540400242 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 6 May 2019 15:01:19 -0700 Subject: [PATCH 02/78] Updated Makefile clean_* section to correspond with new port/xcode_* paths --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a201f6d..fbe95fb 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,8 @@ patch: # Target-specific clean clean_ios: - -rm -rf ports/xcode_ios-arm64 + -rm -rf ports/xcode_ios + -rm -rf ports/xcode_ios_simulator clean_macos: -rm -rf ports/xcode_macos clean_android: From 726216a22d51c9a069b01dda2e8493a3cc9c2975 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 7 May 2019 12:14:49 -0700 Subject: [PATCH 03/78] Added .gitattributes to exclude certain directories from git-archive --- .gitattributes | 5 ++++ ports/Homebrew/README.md | 15 +++++++++++ ports/Homebrew/libzt.rb | 58 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 .gitattributes create mode 100644 ports/Homebrew/README.md create mode 100644 ports/Homebrew/libzt.rb diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6513868 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +/examples export-ignore +/ports export-ignore +/test export-ignore +.gitattributes export-ignore +.gitignore export-ignore \ No newline at end of file diff --git a/ports/Homebrew/README.md b/ports/Homebrew/README.md new file mode 100644 index 0000000..23e789c --- /dev/null +++ b/ports/Homebrew/README.md @@ -0,0 +1,15 @@ +Homebrew formula for libzt/libztcore +====== + +This formula script is only here for archival purposes. To install `libzt` and `libztcore` via Homebrew use: + +``` +brew tap zerotier/libzt +brew install libzt +``` + +Once accepted into Homebrew core this will be shortened to: + +``` +brew install libzt +``` diff --git a/ports/Homebrew/libzt.rb b/ports/Homebrew/libzt.rb new file mode 100644 index 0000000..68cf0cf --- /dev/null +++ b/ports/Homebrew/libzt.rb @@ -0,0 +1,58 @@ +class Libzt < Formula + desc "ZeroTier: libzt -- An encrypted P2P networking library for applications" + homepage "https://www.zerotier.com" + + version "1.3.0" + + stable do + url "https://github.com/zerotier/libzt.git", :branch => "master", :revision => "3d1159882117278fcb5fabb623bd62175b6c7e6c" + end + + bottle do + root_url "https://download.zerotier.com/dist/homebrew" + cellar :any + sha256 "e1ac8425fd0ea510c7db734af8d6c41cd3650b12f66a571f9d818c0121422eee" => :mojave + end + + devel do + version "1.3.1" + url "https://github.com/zerotier/libzt.git", :branch => "dev" + end + + head do + url "https://github.com/zerotier/libzt.git" + end + + depends_on "cmake" => :build + depends_on "make" => :build + + def install + system "make", "update" + system "cmake", ".", *std_cmake_args + system "cmake", "--build", "." + system "make", "install" + cp "LICENSE.GPL-3", "#{prefix}/LICENSE" + end + + def caveats + <<~EOS + Visit https://my.zerotier.com to create virtual networks and authorize devices. + Visit https://www.zerotier.com/manual.shtml to learn more about how ZeroTier works. + Visit https://github.com/zerotier/ZeroTierOne/tree/master/controller to learn how to run your own network controller (advanced). + EOS + end + + test do + # Writes a simple test program to test.cpp which calls a library function. The expected output of this + # function is -2. This test verifies the following: + # - The library was installed correctly + # - The library was linked correctly + # - Library code executes successfully and sends the proper error code to the test program + (testpath/"test.cpp").write <<-EOS + #include\n#include\nint main(){return zts_socket(0,0,0)!=-2;} + EOS + + system ENV.cc, "-v", "test.cpp", "-o", "test", "-L#{lib}/Release", "-lzt" + system "./test" + end +end From ddd8e2c96e456aa40272e1008347a6c3d4795268 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 7 May 2019 12:20:47 -0700 Subject: [PATCH 04/78] Extended .gitattributes --- .gitattributes | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.gitattributes b/.gitattributes index 6513868..642f37d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,24 @@ /examples export-ignore /ports export-ignore /test export-ignore + +/ext/lwip-contrib/apps/ export-ignore +/ext/lwip-contrib/addons/ export-ignore +/ext/lwip-contrib/Coverity/ export-ignore +/ext/lwip-contrib/examples/ export-ignore +/ext/lwip-contrib/*.mk export-ignore +/ext/lwip-contrib/*.cmake export-ignore +/ext/lwip/test export-ignore +/ext/lwip/doc export-ignore + +/ext/ZeroTierOne/artwork export-ignore +/ext/ZeroTierOne/attic export-ignore +/ext/ZeroTierOne/debian export-ignore +/ext/ZeroTierOne/doc export-ignore +/ext/ZeroTierOne/docker export-ignore +/ext/ZeroTierOne/java export-ignore +/ext/ZeroTierOne/macui export-ignore +/ext/ZeroTierOne/rule-compiler export-ignore + .gitattributes export-ignore .gitignore export-ignore \ No newline at end of file From 326ca817671005a20ad09448fd45f2339076bc00 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 7 May 2019 12:22:58 -0700 Subject: [PATCH 05/78] Tweaked .gitattributes --- .gitattributes | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/.gitattributes b/.gitattributes index 642f37d..7cab7ae 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,24 +1,24 @@ -/examples export-ignore -/ports export-ignore -/test export-ignore +examples export-ignore +ports export-ignore +test export-ignore -/ext/lwip-contrib/apps/ export-ignore -/ext/lwip-contrib/addons/ export-ignore -/ext/lwip-contrib/Coverity/ export-ignore -/ext/lwip-contrib/examples/ export-ignore -/ext/lwip-contrib/*.mk export-ignore -/ext/lwip-contrib/*.cmake export-ignore -/ext/lwip/test export-ignore -/ext/lwip/doc export-ignore +ext/lwip-contrib/apps export-ignore +ext/lwip-contrib/addons export-ignore +ext/lwip-contrib/Coverity export-ignore +ext/lwip-contrib/examples export-ignore +ext/lwip-contrib/*.mk export-ignore +ext/lwip-contrib/*.cmake export-ignore +ext/lwip/test export-ignore +ext/lwip/doc export-ignore -/ext/ZeroTierOne/artwork export-ignore -/ext/ZeroTierOne/attic export-ignore -/ext/ZeroTierOne/debian export-ignore -/ext/ZeroTierOne/doc export-ignore -/ext/ZeroTierOne/docker export-ignore -/ext/ZeroTierOne/java export-ignore -/ext/ZeroTierOne/macui export-ignore -/ext/ZeroTierOne/rule-compiler export-ignore +ext/ZeroTierOne/artwork export-ignore +ext/ZeroTierOne/attic export-ignore +ext/ZeroTierOne/debian export-ignore +ext/ZeroTierOne/doc export-ignore +ext/ZeroTierOne/docker export-ignore +ext/ZeroTierOne/java export-ignore +ext/ZeroTierOne/macui export-ignore +ext/ZeroTierOne/rule-compiler export-ignore .gitattributes export-ignore .gitignore export-ignore \ No newline at end of file From eaac3429f0f88686c7da0630bcdb9d1aea94c4f0 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 10 May 2019 10:39:27 -0700 Subject: [PATCH 06/78] Re-worked the CMakeLists.txt install section --- CMakeLists.txt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a67292..549318a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -505,18 +505,12 @@ endif () # | INSTALL | # ----------------------------------------------------------------------------- -set(PUBLIC_ZT_HEADERS +set (PUBLIC_ZT_HEADERS ${PROJECT_SOURCE_DIR}/include/ZeroTier.h ${PROJECT_SOURCE_DIR}/include/ZeroTierConstants.h) -set(PUBLIC_ZTCORE_HEADERS - ${PROJECT_SOURCE_DIR}/ext/ZeroTierOne/include/ZeroTierOne.h) - -set(include_dest "include") -set(main_lib_dest "lib") -set(lib_dest "${main_lib_dest}/${CMAKE_BUILD_TYPE}") -install(TARGETS ${STATIC_LIB_NAME} EXPORT ${STATIC_LIB_NAME} DESTINATION "${lib_dest}") -install(TARGETS ${DYNAMIC_LIB_NAME} EXPORT ${DYNAMIC_LIB_NAME} DESTINATION "${lib_dest}") -install(FILES ${PUBLIC_ZT_HEADERS} DESTINATION "${include_dest}") -install(FILES ${PUBLIC_ZTCORE_HEADERS} DESTINATION "${include_dest}") -install(EXPORT ${STATIC_LIB_NAME} DESTINATION "${lib_dest}") +set_target_properties(${STATIC_LIB_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_ZT_HEADERS}") +install (TARGETS ${STATIC_LIB_NAME} + LIBRARY DESTINATION "lib" + PUBLIC_HEADER DESTINATION "include" +) From b5c49ebd35e490d33259d123086e502d4612e18e Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 10 May 2019 12:24:00 -0700 Subject: [PATCH 07/78] Tweaked Makefile patch target for compatibility with older versions of git. Differentiated bdist and sdist in dist.sh --- CMakeLists.txt | 5 +-- Makefile | 22 ++++++++----- dist.sh | 83 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 92 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 549318a..ea3a2fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -511,6 +511,7 @@ set (PUBLIC_ZT_HEADERS set_target_properties(${STATIC_LIB_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_ZT_HEADERS}") install (TARGETS ${STATIC_LIB_NAME} - LIBRARY DESTINATION "lib" - PUBLIC_HEADER DESTINATION "include" + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include ) diff --git a/Makefile b/Makefile index fbe95fb..ff90a2e 100644 --- a/Makefile +++ b/Makefile @@ -17,14 +17,14 @@ list: # Pull all submodules update: - git submodule update --init - git submodule status + @git submodule update --init + @git submodule status # Patch submodules (issue update first) patch: - -git -C ext/lwip apply ../lwip.patch - -git -C ext/lwip-contrib apply ../lwip-contrib.patch - -git -C ext/ZeroTierOne apply ../ZeroTierOne.patch + -cd ext/lwip; git apply ../lwip.patch; + -cd ext/lwip-contrib; git apply ../lwip-contrib.patch; + -cd ext/ZeroTierOne; git apply ../ZeroTierOne.patch; # Target-specific clean clean_ios: @@ -98,7 +98,13 @@ all: host host_jar macos ios android wrap: $(DIST_BUILD_SCRIPT) wrap -# [For distribution process only] Marge and package everything into a tarball -dist: +# Binary distribution +bdist: $(DIST_BUILD_SCRIPT) merge - $(DIST_BUILD_SCRIPT) dist + $(DIST_BUILD_SCRIPT) bdist + +# Source distribution +sdist: update patch + $(DIST_BUILD_SCRIPT) sdist + +dist: bdist sdist diff --git a/dist.sh b/dist.sh index 19a24ea..0bb7ba0 100755 --- a/dist.sh +++ b/dist.sh @@ -292,6 +292,7 @@ clean() prep_android_example() { echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" + mkdir -p examples/android/ExampleAndroidApp/app/libs/ cp -f lib/$1/android-armeabi-v7a/libzt-$1.aar \ examples/android/ExampleAndroidApp/app/libs/libzt.aar } @@ -448,6 +449,19 @@ wrap() tar --exclude=$PROD_FILENAME -zcvf $PROD_FILENAME -C $ARCH_WRAP_DIR . } +# Renames and copies licenses for libzt and each of its dependencies +package_licenses() +{ + CURR_DIR=$1 + DEST_DIR=$2 + mkdir -p $DEST_DIR + cp $CURR_DIR/ext/lwip/COPYING $DEST_DIR/LWIP-LICENSE.BSD + cp $CURR_DIR/ext/concurrentqueue/LICENSE.md $DEST_DIR/CONCURRENTQUEUE-LICENSE.BSD + cp $CURR_DIR/LICENSE.GPL-3 $DEST_DIR/ZEROTIER-LICENSE.GPL-3 + cp $CURR_DIR/include/net/ROUTE_H-LICENSE.APSL $DEST_DIR/ROUTE_H-LICENSE.APSL + cp $CURR_DIR/include/net/ROUTE_H-LICENSE $DEST_DIR/ROUTE_H-LICENSE +} + # Copies binaries, documentation, licenses, etc into a products # dir and then tarballs everything together package_everything() @@ -457,14 +471,8 @@ package_everything() PROD_NAME=$LIBZT_VERSION-$(date '+%Y%m%d_%H-%M')-$1 PROD_DIR=$(pwd)/products/$PROD_NAME/ # Make products directory - LICENSE_DIR=$PROD_DIR/licenses - mkdir -p $LICENSE_DIR # Licenses - cp $(pwd)/ext/lwip/COPYING $LICENSE_DIR/LWIP-LICENSE.BSD - cp $(pwd)/ext/concurrentqueue/LICENSE.md $LICENSE_DIR/CONCURRENTQUEUE-LICENSE.BSD - cp $(pwd)/LICENSE.GPL-3 $LICENSE_DIR/ZEROTIER-LICENSE.GPL-3 - cp $(pwd)/include/net/ROUTE_H-LICENSE.APSL $LICENSE_DIR/ROUTE_H-LICENSE.APSL - cp $(pwd)/include/net/ROUTE_H-LICENSE $LICENSE_DIR/ROUTE_H-LICENSE + package_licenses $(pwd) $PROD_DIR/licenses # Documentation mkdir -p $PROD_DIR/doc # Copy the errno header from lwIP for customer reference @@ -523,8 +531,67 @@ package_everything() done } +# Generates a source-only tarball +sdist() +{ + VERSION=$(git describe --abbrev=0) + TARBALL_DIR="libzt-${VERSION}" + TARBALL_NAME=libzt-${VERSION}-source.tar.gz + PROD_DIR=$(pwd)/products/ + mkdir -p $PROD_DIR + # + mkdir ${TARBALL_DIR} + # primary sources + cp -rf src ${TARBALL_DIR}/src + cp -rf include ${TARBALL_DIR}/include + # important build scripts + cp Makefile ${TARBALL_DIR} + cp CMakeLists.txt ${TARBALL_DIR} + cp *.md ${TARBALL_DIR} + cp *.sh ${TARBALL_DIR} + cp *.bat ${TARBALL_DIR} + # submodules/dependencies + # lwIP + mkdir ${TARBALL_DIR}/ext + mkdir -p ${TARBALL_DIR}/ext/lwip/src + cp -rf ext/lwip/src/api ${TARBALL_DIR}/ext/lwip/src + cp -rf ext/lwip/src/core ${TARBALL_DIR}/ext/lwip/src + cp -rf ext/lwip/src/include ${TARBALL_DIR}/ext/lwip/src + cp -rf ext/lwip/src/netif ${TARBALL_DIR}/ext/lwip/src + # lwIP ports + mkdir -p ${TARBALL_DIR}/ext/lwip-contrib/ports + cp -rf ext/lwip-contrib/ports/unix ${TARBALL_DIR}/ext/lwip-contrib/ports + cp -rf ext/lwip-contrib/ports/win32 ${TARBALL_DIR}/ext/lwip-contrib/ports + # ZeroTierOne + mkdir ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/*.h ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/controller ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/ext ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/include ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/node ${TARBALL_DIR}/ext/ZeroTierOne + cp -rf ext/ZeroTierOne/osdep ${TARBALL_DIR}/ext/ZeroTierOne + # + # Perform selective removal + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/bin + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/tap-mac + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/librethinkdbxx + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/installfiles + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/curl-* + rm -rf ${TARBALL_DIR}/ext/ZeroTierOne/ext/http-parser + # + mkdir ${TARBALL_DIR}/ext/concurrentqueue + cp -rf ext/concurrentqueue/*.h ${TARBALL_DIR}/ext/concurrentqueue + # Licenses + package_licenses $(pwd) $TARBALL_DIR/licenses + # Tarball everything and display the results + tar -cvf ${TARBALL_NAME} ${TARBALL_DIR} + tree ${TARBALL_DIR} + rm -rf ${TARBALL_DIR} + mv ${TARBALL_NAME} ${PROD_DIR} +} + # Package both debug and release -dist() +bdist() { echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" package_everything "debug" From 9dbcc8eb45bf8863261599962c799b1de3557d14 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 10 May 2019 12:39:49 -0700 Subject: [PATCH 08/78] Added dynamic library to install section of CMakeLists.txt --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea3a2fb..add188e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -515,3 +515,6 @@ install (TARGETS ${STATIC_LIB_NAME} ARCHIVE DESTINATION lib PUBLIC_HEADER DESTINATION include ) +install (TARGETS ${DYNAMIC_LIB_NAME} + LIBRARY DESTINATION lib +) From 26ba6add126dd425c73627706753044e10b606af Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 13 May 2019 12:36:44 -0700 Subject: [PATCH 09/78] Updated ZeroTierOne submodule to incorperate minor SDK-related changes --- Makefile | 2 +- ext/ZeroTierOne | 2 +- ext/ZeroTierOne.patch | 123 ------------------------------------------ 3 files changed, 2 insertions(+), 125 deletions(-) delete mode 100644 ext/ZeroTierOne.patch diff --git a/Makefile b/Makefile index ff90a2e..77ea515 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ update: patch: -cd ext/lwip; git apply ../lwip.patch; -cd ext/lwip-contrib; git apply ../lwip-contrib.patch; - -cd ext/ZeroTierOne; git apply ../ZeroTierOne.patch; + #-cd ext/ZeroTierOne; git apply ../ZeroTierOne.patch; # Target-specific clean clean_ios: diff --git a/ext/ZeroTierOne b/ext/ZeroTierOne index 21f4f16..4743ad0 160000 --- a/ext/ZeroTierOne +++ b/ext/ZeroTierOne @@ -1 +1 @@ -Subproject commit 21f4f16e63f45577a614e25066e71080740fe175 +Subproject commit 4743ad0b16a72c00039de1aed98499fe0adf633f diff --git a/ext/ZeroTierOne.patch b/ext/ZeroTierOne.patch deleted file mode 100644 index cce5bf7..0000000 --- a/ext/ZeroTierOne.patch +++ /dev/null @@ -1,123 +0,0 @@ -diff --git a/node/Node.cpp b/node/Node.cpp -index 30c722b2..f0503e1c 100644 ---- a/node/Node.cpp -+++ b/node/Node.cpp -@@ -317,7 +317,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 - - // Update online status, post status change as event - const bool oldOnline = _online; -- _online = (((now - lastReceivedFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amUpstream())); -+ _online = (((now - lastReceivedFromUpstream) < (ZT_PEER_ACTIVITY_TIMEOUT / 16))||(RR->topology->amUpstream())); - if (oldOnline != _online) - postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE); - } catch ( ... ) { -diff --git a/node/RingBuffer.hpp b/node/RingBuffer.hpp -index dab81b9e..0d90152b 100644 ---- a/node/RingBuffer.hpp -+++ b/node/RingBuffer.hpp -@@ -72,6 +72,11 @@ public: - memset(buf, 0, sizeof(T) * size); - } - -+ ~RingBuffer() -+ { -+ delete [] buf; -+ } -+ - /** - * @return A pointer to the underlying buffer - */ -diff --git a/node/Switch.cpp b/node/Switch.cpp -index 74c22d33..3e4f53b4 100644 ---- a/node/Switch.cpp -+++ b/node/Switch.cpp -@@ -425,16 +425,16 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const - from.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(data,len); -- if (!network->config().disableCompression()) -- outp.compress(); -+ //if (!network->config().disableCompression()) -+ // outp.compress(); - aqm_enqueue(tPtr,network,outp,true,qosBucket); - } else { - Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); - outp.append(network->id()); - outp.append((uint16_t)etherType); - outp.append(data,len); -- if (!network->config().disableCompression()) -- outp.compress(); -+ //if (!network->config().disableCompression()) -+ // outp.compress(); - aqm_enqueue(tPtr,network,outp,true,qosBucket); - } - -@@ -532,7 +532,7 @@ void Switch::aqm_enqueue(void *tPtr, const SharedPtr &network, Packet & - TXQueueEntry *txEntry = new TXQueueEntry(dest,RR->node->now(),packet,encrypt); - - ManagedQueue *selectedQueue = nullptr; -- for (int i=0; ioldQueues.size()) { // search old queues first (I think this is best since old would imply most recent usage of the queue) - if (nqcb->oldQueues[i]->id == qosBucket) { - selectedQueue = nqcb->oldQueues[i]; -@@ -568,7 +568,7 @@ void Switch::aqm_enqueue(void *tPtr, const SharedPtr &network, Packet & - { - // DEBUG_INFO("too many enqueued packets (%d), finding packet to drop", nqcb->_currEnqueuedPackets); - int maxQueueLength = 0; -- for (int i=0; ioldQueues.size()) { - if (nqcb->oldQueues[i]->byteLength > maxQueueLength) { - maxQueueLength = nqcb->oldQueues[i]->byteLength; -diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp -index 676f6335..2834fd22 100644 ---- a/osdep/OSUtils.cpp -+++ b/osdep/OSUtils.cpp -@@ -452,6 +452,7 @@ std::string OSUtils::platformDefaultHomePath() - #endif // __UNIX_LIKE__ or not... - } - -+#ifndef OMIT_JSON_SUPPORT - // Inline these massive JSON operations in one place only to reduce binary footprint and compile time - nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf.c_str()); } - std::string OSUtils::jsonDump(const nlohmann::json &j,int indentation) { return j.dump(indentation); } -@@ -543,6 +544,8 @@ std::string OSUtils::jsonBinFromHex(const nlohmann::json &jv) - return std::string(); - } - -+#endif // OMIT_JSON_SUPPORT -+ - // Used to convert HTTP header names to ASCII lower case - const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; - -diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp -index 2c4283b0..afdb1bf1 100644 ---- a/osdep/OSUtils.hpp -+++ b/osdep/OSUtils.hpp -@@ -55,7 +55,9 @@ - #endif - #endif - -+#ifndef OMIT_JSON_SUPPORT - #include "../ext/json/json.hpp" -+#endif - - namespace ZeroTier { - -@@ -284,6 +286,7 @@ public: - */ - static std::string platformDefaultHomePath(); - -+#ifndef OMIT_JSON_SUPPORT - static nlohmann::json jsonParse(const std::string &buf); - static std::string jsonDump(const nlohmann::json &j,int indentation = 1); - static uint64_t jsonInt(const nlohmann::json &jv,const uint64_t dfl); -@@ -291,6 +294,7 @@ public: - static bool jsonBool(const nlohmann::json &jv,const bool dfl); - static std::string jsonString(const nlohmann::json &jv,const char *dfl); - static std::string jsonBinFromHex(const nlohmann::json &jv); -+#endif // OMIT_JSON_SUPPORT - - private: - static const unsigned char TOLOWER_TABLE[256]; From a108609c3d6175a4e84cc03eba14218686e40982 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 13 May 2019 15:58:45 -0700 Subject: [PATCH 10/78] Added function in dist.sh to generate markdown-formatted changelogs --- CHANGELOG.md | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++ dist.sh | 19 +++++++++ 2 files changed, 137 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2120c5d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,118 @@ +## [1.3.1-hb0] - 2019-05-13 +*** +- Minor tweaks for Homebrew-core compliance +## [1.3.1] - 2019-05-12 +*** +- Minor build-related improvements. No API or core functionality changes +## [1.3.0] - 2019-04-22 +*** +- Added new callback API. Significant improvements to performance and reliability +## [1.2.0] - 2019-03-01 +*** +- ZeroTier core version upgrade to 1.2.12, major bugfixes, improved platform compatibility, revamped build process +## [1.1.6] - 2018-02-08 +*** +- Improved concurrent thread performance +## [1.1.4] - 2017-06-06 +*** +- Important Dual-stack IPv4/IPv6 bug fix +## [1.1.3] - 2017-06-06 +*** +- Updated picoTCP to 1.4.0, improved selftest +## [1.1.2] - 2017-06-06 +*** +- Minor ZTO source update, improved selftest, documentation +## [1.1.1] - 2017-06-06 +*** +- Updated ZTO source to 1.2.4, bug fixes +## [1.1.0] - 2017-06-06 +*** +- Added new socket multiplexing logic, significant restructuring of picoTCP stack driver +## [1.0.0] - 2017-06-06 +*** +- Refactored library structure. Dynamic loading of stack no longer required. lwIP Support dropped +## [0.9.0] - 2017-06-06 +*** +- Updated ZTO source for tptr feature +## [0.8.0] - 2017-06-06 +*** +- Updated ZTO source to 1.2.0, bug fixes +## [0.7.0] - 2017-06-06 +*** +- Updated ZTO source to 1.1.17, updated documentation, bug fixes +## [0.6.2] - 2017-06-06 +*** +- Updated ZTO source for reliability +## [0.6.1] - 2017-06-06 +*** +- Stack driver support for IPv4 XOR IPv6 +## [0.6.0] - 2017-06-06 +*** +- Updated ZTO source to 1.1.14 +## [0.5.0] - 2017-06-06 +*** +- Implemented driver for picoTCP network stack +## [0.4.2] - 2017-06-06 +*** +- IPv6-related fixes +## [0.4.1] - 2017-06-06 +*** +- Added IPv6 support to lwIP stack driver +## [0.4.0] - 2017-06-06 +*** +- Upgraded to lwIP 2.0.0 +## [0.3.5] - 2017-06-06 +*** +- Introduced prettier and less complex Swift API +## [0.3.4] - 2017-06-06 +*** +- Improved debugging traces +## [0.3.3] - 2017-06-06 +*** +- Improved API parameter naming consistency +## [0.3.2] - 2017-06-06 +*** +- Improved proxy server controls +## [0.3.1] - 2017-06-06 +*** +- API consistency improvements +## [0.3.0] - 2017-06-06 +*** +- Updated ZTO source to 1.1.4 +## [0.2.1] - 2017-06-06 +*** +- Normalized zt_ API naming conventions +## [0.2.0] - 2017-06-06 +*** +- Old netcon project moved into its own repo + + +[1.3.1-hb0]: https://github.com/zerotier/libzt/compare/1.3.1..1.3.1-hb0 +[1.3.1]: https://github.com/zerotier/libzt/compare/1.3.0..1.3.1 +[1.3.0]: https://github.com/zerotier/libzt/compare/1.2.0..1.3.0 +[1.2.0]: https://github.com/zerotier/libzt/compare/1.1.6..1.2.0 +[1.1.6]: https://github.com/zerotier/libzt/compare/1.1.4..1.1.6 +[1.1.4]: https://github.com/zerotier/libzt/compare/1.1.3..1.1.4 +[1.1.3]: https://github.com/zerotier/libzt/compare/1.1.2..1.1.3 +[1.1.2]: https://github.com/zerotier/libzt/compare/1.1.1..1.1.2 +[1.1.1]: https://github.com/zerotier/libzt/compare/1.1.0..1.1.1 +[1.1.0]: https://github.com/zerotier/libzt/compare/1.0.0..1.1.0 +[1.0.0]: https://github.com/zerotier/libzt/compare/0.9.0..1.0.0 +[0.9.0]: https://github.com/zerotier/libzt/compare/0.8.0..0.9.0 +[0.8.0]: https://github.com/zerotier/libzt/compare/0.7.0..0.8.0 +[0.7.0]: https://github.com/zerotier/libzt/compare/0.6.2..0.7.0 +[0.6.2]: https://github.com/zerotier/libzt/compare/0.6.1..0.6.2 +[0.6.1]: https://github.com/zerotier/libzt/compare/0.6.0..0.6.1 +[0.6.0]: https://github.com/zerotier/libzt/compare/0.5.0..0.6.0 +[0.5.0]: https://github.com/zerotier/libzt/compare/0.4.2..0.5.0 +[0.4.2]: https://github.com/zerotier/libzt/compare/0.4.1..0.4.2 +[0.4.1]: https://github.com/zerotier/libzt/compare/0.4.0..0.4.1 +[0.4.0]: https://github.com/zerotier/libzt/compare/0.3.5..0.4.0 +[0.3.5]: https://github.com/zerotier/libzt/compare/0.3.4..0.3.5 +[0.3.4]: https://github.com/zerotier/libzt/compare/0.3.3..0.3.4 +[0.3.3]: https://github.com/zerotier/libzt/compare/0.3.2..0.3.3 +[0.3.2]: https://github.com/zerotier/libzt/compare/0.3.1..0.3.2 +[0.3.1]: https://github.com/zerotier/libzt/compare/0.3.0..0.3.1 +[0.3.0]: https://github.com/zerotier/libzt/compare/0.2.1..0.3.0 +[0.2.1]: https://github.com/zerotier/libzt/compare/0.2.0..0.2.1 +[0.2.0]: https://github.com/zerotier/libzt/compare/e073a35b72781a8947e197419677e82b9ed9f9bc..0.2.0 diff --git a/dist.sh b/dist.sh index 0bb7ba0..f850519 100755 --- a/dist.sh +++ b/dist.sh @@ -598,6 +598,25 @@ bdist() package_everything "release" } +# Generate a markdown CHANGELOG from git-log +update_changelog() +{ + first_commit=$(git rev-list --max-parents=0 HEAD) + git for-each-ref --sort=-refname --format="## [%(refname:short)] - %(taggerdate:short) &(newline)*** &(newline)- %(subject) %(body)" refs/tags > CHANGELOG.md + gsed -i '''s/\&(newline)/\n/' CHANGELOG.md # replace first instance + gsed -i '''s/\&(newline)/\n/' CHANGELOG.md # replace second instance + echo -e "\n" >> CHANGELOG.md + for curr_tag in $(git tag -l --sort=-v:refname) + do + prev_tag=$(git describe --abbrev=0 ${curr_tag}^) + if [ -z "${prev_tag}" ] + then + prev_tag=${first_commit} + fi + echo "[${curr_tag}]: https://github.com/zerotier/libzt/compare/${prev_tag}..${curr_tag}" >> CHANGELOG.md + done +} + # List all functions in this script (just for convenience) list() { From 24ee19183931b40e1c9547a2b7ff87f5f0dc820b Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 14 May 2019 11:12:16 -0700 Subject: [PATCH 11/78] Removed submodule lwip and lwip-contrib --- .gitmodules | 10 ---------- ext/lwip | 1 - ext/lwip-contrib | 1 - 3 files changed, 12 deletions(-) delete mode 160000 ext/lwip delete mode 160000 ext/lwip-contrib diff --git a/.gitmodules b/.gitmodules index e55c450..19f0e34 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,13 +1,3 @@ -[submodule "zto"] - path = zto - url = https://github.com/zerotier/ZeroTierOne.git - branch = dev -[submodule "ext/lwip"] - path = ext/lwip - url = https://git.savannah.nongnu.org/git/lwip.git -[submodule "ext/lwip-contrib"] - path = ext/lwip-contrib - url = https://git.savannah.nongnu.org/git/lwip/lwip-contrib.git [submodule "ext/ZeroTierOne"] path = ext/ZeroTierOne url = https://github.com/zerotier/ZeroTierOne.git diff --git a/ext/lwip b/ext/lwip deleted file mode 160000 index 159e31b..0000000 --- a/ext/lwip +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 159e31b689577dbf69cf0683bbaffbd71fa5ee10 diff --git a/ext/lwip-contrib b/ext/lwip-contrib deleted file mode 160000 index 35b011d..0000000 --- a/ext/lwip-contrib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 35b011d4cf4c4b480f8859c456587a884ec9d287 From 3562625cd938c1eaa8a1ce45507db9be83c1a4cf Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 14 May 2019 11:35:41 -0700 Subject: [PATCH 12/78] Added forked joseph-henry/lwip as new lwip submodule --- .gitmodules | 3 +++ ext/lwip | 1 + 2 files changed, 4 insertions(+) create mode 160000 ext/lwip diff --git a/.gitmodules b/.gitmodules index 19f0e34..8defb23 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "ext/ZeroTierOne"] path = ext/ZeroTierOne url = https://github.com/zerotier/ZeroTierOne.git +[submodule "ext/lwip"] + path = ext/lwip + url = https://github.com/joseph-henry/lwip.git diff --git a/ext/lwip b/ext/lwip new file mode 160000 index 0000000..b3a9394 --- /dev/null +++ b/ext/lwip @@ -0,0 +1 @@ +Subproject commit b3a939417e936a933ba287aee79eff8662198cad From d5b064623e1161196fd5bc14668e83bec2c27717 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 14 May 2019 11:56:01 -0700 Subject: [PATCH 13/78] Added forked joseph-henry/lwip-contrib as new lwip-contrib submodule --- .gitmodules | 3 +++ ext/lwip-contrib | 1 + 2 files changed, 4 insertions(+) create mode 160000 ext/lwip-contrib diff --git a/.gitmodules b/.gitmodules index 8defb23..7862383 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "ext/lwip"] path = ext/lwip url = https://github.com/joseph-henry/lwip.git +[submodule "ext/lwip-contrib"] + path = ext/lwip-contrib + url = https://github.com/joseph-henry/lwip-contrib.git diff --git a/ext/lwip-contrib b/ext/lwip-contrib new file mode 160000 index 0000000..35b011d --- /dev/null +++ b/ext/lwip-contrib @@ -0,0 +1 @@ +Subproject commit 35b011d4cf4c4b480f8859c456587a884ec9d287 From ecd10fa46e7587cb04cc52d18df73014f2a78d2c Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 15 May 2019 10:31:16 -0700 Subject: [PATCH 14/78] Added Homebrew blurb, fixed test example, removed patches from Makefile --- Makefile | 4 ++-- README.md | 30 ++++++++++++++++++++++++++++-- test/simple.cpp | 13 +++++++------ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 77ea515..c5be365 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,8 @@ update: # Patch submodules (issue update first) patch: - -cd ext/lwip; git apply ../lwip.patch; - -cd ext/lwip-contrib; git apply ../lwip-contrib.patch; + #-cd ext/lwip; git apply ../lwip.patch; + #-cd ext/lwip-contrib; git apply ../lwip-contrib.patch; #-cd ext/ZeroTierOne; git apply ../ZeroTierOne.patch; # Target-specific clean diff --git a/README.md b/README.md index 99adf66..c74e2d4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,26 @@ The ZeroTier SDK is composed of two libraries: `libztcore` which is the platfor
-For a complete example, see [test/simple.cpp](test/simple.cpp) or [test/example.cpp](test/example.cpp). With no error checking, a paraphrased example is as follows: +## Downloads / Installation + + Tarballs: + + - [libzt-release.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-release.tar.gz) // [libzt-debug.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-debug.tar.gz) // [libzt-source.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-source.tar.gz) + +Homebrew + +``` +brew install libzt +``` + +*** + +
+ +## Example + + - Complete example: [test/simple.cpp](test/simple.cpp) + - Slightly more thorough example: [test/example.cpp](test/example.cpp) ``` #include "ZeroTier.h" @@ -38,11 +57,18 @@ int main() ... ``` +After you've created a virtual network and added its `nwid` to the sample code, run: + +``` +clang++ example.cpp -o example -lzt +./example +``` + The complete API specification can be found here: [API.md](API.md) *** -## Build +## Build from source Build scripts use a combination of make, and cmake. To retrieve sources for all submodules, patch them, and build all targets (debug and release) for your host machine, issue the following: diff --git a/test/simple.cpp b/test/simple.cpp index 7f0bdbd..6c39833 100644 --- a/test/simple.cpp +++ b/test/simple.cpp @@ -1,10 +1,10 @@ +#include + #include #include #include #include -#include "ZeroTier.h" - bool node_ready = false; bool network_ready = false; @@ -49,20 +49,21 @@ int main() // Set up ZeroTier service and wai for callbacks int port = 9994; - int nwid = 0x0123456789abcdef; - zts_start("test/path", &myZeroTierEventCallback, port); + uint64_t nwid = 0x0123456789abcdef; + zts_start("zt_config/path", &myZeroTierEventCallback, port); printf("Waiting for node to come online...\n"); while (!node_ready) { sleep(1); } zts_join(nwid); printf("Joined virtual network. Requesting configuration...\n"); while (!network_ready) { sleep(1); } + printf("I am %llx\n", zts_get_node_id()); // Socket API example if ((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("error creating socket\n"); } if ((err = zts_connect(fd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) { - printf("error connecting to remote host\n"); + printf("error connecting to remote host (%s)\n", remoteIp); } if ((err = zts_write(fd, str, strlen(str))) < 0) { printf("error writing to socket\n"); @@ -70,4 +71,4 @@ int main() zts_close(fd); zts_stop(); return 0; -} \ No newline at end of file +} From 48d8dccff83ce0c0952c5c0059a34de349cace41 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 16 May 2019 14:45:32 -0700 Subject: [PATCH 15/78] Updated submodule revision for lwip to include errno patch --- ext/lwip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/lwip b/ext/lwip index b3a9394..3bb5b07 160000 --- a/ext/lwip +++ b/ext/lwip @@ -1 +1 @@ -Subproject commit b3a939417e936a933ba287aee79eff8662198cad +Subproject commit 3bb5b0722ea01edd8dee439ba62eeee788d2894e From dcfd4b032a3dcd85a65c79418463b812e84a5f37 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 11 Jun 2019 16:31:59 -0700 Subject: [PATCH 16/78] Minor build fixes for Windows --- Makefile | 6 +++--- src/Controls.hpp | 4 ++++ src/Sockets.cpp | 7 ------- src/VirtualTap.cpp | 6 ++++++ src/lwipDriver.cpp | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index c5be365..6b3ff5d 100644 --- a/Makefile +++ b/Makefile @@ -5,9 +5,9 @@ else DIST_BUILD_SCRIPT := ./dist.sh endif -EXECUTABLES = cmake -build_reqs := $(foreach exec,$(EXECUTABLES),\ - $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH"))) +#EXECUTABLES = cmake +#build_reqs := $(foreach exec,$(EXECUTABLES),\ +# $(if $(shell which $(exec)),some string,$(error "No $(exec) in PATH"))) .PHONY: list list: diff --git a/src/Controls.hpp b/src/Controls.hpp index 4b3870e..c78246b 100644 --- a/src/Controls.hpp +++ b/src/Controls.hpp @@ -33,6 +33,10 @@ #ifndef LIBZT_CONTROLS_HPP #define LIBZT_CONTROLS_HPP +#ifdef _WIN32 +#include +#endif + namespace ZeroTier { ////////////////////////////////////////////////////////////////////////////// diff --git a/src/Sockets.cpp b/src/Sockets.cpp index df65521..844c90a 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -38,16 +38,9 @@ #include "ZeroTierConstants.h" #include "Options.h" #include "Debug.hpp" -#include "Controls.hpp" #ifdef SDK_JNI #include -#ifndef _MSC_VER - //#include - //#include - //#include - //#include -#endif #endif namespace ZeroTier { diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index a0dd2cb..190b599 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -79,7 +79,9 @@ VirtualTap::VirtualTap( memset(vtap_full_name, 0, sizeof(vtap_full_name)); snprintf(vtap_full_name, sizeof(vtap_full_name), "libzt%llx", (unsigned long long)_nwid); _dev = vtap_full_name; +#ifndef _WIN32 ::pipe(_shutdownSignalPipe); +#endif // Start virtual tap thread and stack I/O loops _thread = Thread::start(this); } @@ -90,13 +92,17 @@ VirtualTap::~VirtualTap() nd->nwid = _nwid; postEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); _run = false; +#ifndef _WIN32 ::write(_shutdownSignalPipe[1],"\0",1); +#endif _phy.whack(); lwip_remove_netif(netif); netif = NULL; Thread::join(_thread); +#ifndef _WIN32 ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); +#endif } void VirtualTap::lastConfigUpdate(uint64_t lastConfigUpdateTime) diff --git a/src/lwipDriver.cpp b/src/lwipDriver.cpp index f69d28d..db8dffe 100644 --- a/src/lwipDriver.cpp +++ b/src/lwipDriver.cpp @@ -155,7 +155,7 @@ void lwip_driver_init() #if defined(_WIN32) sys_init(); // Required for win32 init of critical sections #endif - void *st = sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, main_lwip_driver_loop, + void *st = (void*)sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, main_lwip_driver_loop, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); } From a514508c20cdb3d9b1a5dcf76ef9fdcb7ae446be Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 17 Jun 2019 14:31:15 -0700 Subject: [PATCH 17/78] Updated lwip-contrib submodule revision --- ext/lwip-contrib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/lwip-contrib b/ext/lwip-contrib index 35b011d..4fd612c 160000 --- a/ext/lwip-contrib +++ b/ext/lwip-contrib @@ -1 +1 @@ -Subproject commit 35b011d4cf4c4b480f8859c456587a884ec9d287 +Subproject commit 4fd612c9c72dfcd1db6618bd59c1a17d9f5b55f8 From 9f93552740fc3117e2a0fed80a46022c13408221 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 17 Jun 2019 16:03:30 -0700 Subject: [PATCH 18/78] Minor changes for Windows build --- include/ZeroTier.h | 51 +++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/include/ZeroTier.h b/include/ZeroTier.h index a60849b..bb22d8d 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -35,7 +35,10 @@ #include "ZeroTierConstants.h" -#include +#if defined(_MSC_VER) +#include +typedef int ssize_t; +#endif #ifdef _WIN32 #ifdef ADD_EXPORTS @@ -92,7 +95,11 @@ typedef uint16_t zts_in_port_t; typedef uint8_t zts_sa_family_t; struct zts_in_addr { +#if defined(_WIN32) + zts_in_addr_t S_addr; +#else zts_in_addr_t s_addr; +#endif }; struct zts_in6_addr { @@ -105,34 +112,34 @@ struct zts_in6_addr { struct zts_sockaddr_in { uint8_t sin_len; - zts_sa_family_t sin_family; - zts_in_port_t sin_port; - struct zts_in_addr sin_addr; + zts_sa_family_t sin_family; + zts_in_port_t sin_port; + struct zts_in_addr sin_addr; #define SIN_ZERO_LEN 8 - char sin_zero[SIN_ZERO_LEN]; + char sin_zero[SIN_ZERO_LEN]; }; struct zts_sockaddr_in6 { - uint8_t sin6_len; /* length of this structure */ - zts_sa_family_t sin6_family; /* AF_INET6 */ - zts_in_port_t sin6_port; /* Transport layer port # */ - uint32_t sin6_flowinfo; /* IPv6 flow information */ - struct zts_in6_addr sin6_addr; /* IPv6 address */ - uint32_t sin6_scope_id; /* Set of interfaces for scope */ + uint8_t sin6_len; // length of this structure + zts_sa_family_t sin6_family; // AF_INET6 + zts_in_port_t sin6_port; // Transport layer port # + uint32_t sin6_flowinfo; // IPv6 flow information + struct zts_in6_addr sin6_addr; // IPv6 address + uint32_t sin6_scope_id; // Set of interfaces for scope }; struct zts_sockaddr { - uint8_t sa_len; + uint8_t sa_len; zts_sa_family_t sa_family; - char sa_data[14]; + char sa_data[14]; }; struct zts_sockaddr_storage { - uint8_t s2_len; + uint8_t s2_len; zts_sa_family_t ss_family; - char s2_data1[2]; - uint32_t s2_data2[3]; - uint32_t s2_data3[3]; + char s2_data1[2]; + uint32_t s2_data2[3]; + uint32_t s2_data3[3]; }; #if !defined(zts_iovec) @@ -156,16 +163,14 @@ struct zts_msghdr { * Structure used for manipulating linger option. */ struct zts_linger { - int l_onoff; /* option on/off */ - int l_linger; /* linger time in seconds */ + int l_onoff; // option on/off + int l_linger; // linger time in seconds }; -/* -typedef struct fd_set +typedef struct zts_fd_set { unsigned char fd_bits [(FD_SETSIZE+7)/8]; -} fd_set; -*/ +} zts_fd_set; #ifdef __cplusplus } From f6f79b6305dfe634fdb07a2381a25e6f379a0227 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 17 Jun 2019 16:46:17 -0700 Subject: [PATCH 19/78] Minor changes for Windows build --- include/ZeroTier.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/ZeroTier.h b/include/ZeroTier.h index bb22d8d..1d78588 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -76,11 +76,6 @@ typedef int socklen_t; #include #endif -#if defined(_MSC_VER) -#include -typedef SSIZE_T ssize_t; -#endif - //namespace ZeroTier { #ifdef __cplusplus From 17e5b8172d9d227af147955c126bbab0fc800fdb Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 18 Jun 2019 11:06:05 -0700 Subject: [PATCH 20/78] Minor changes for Windows build --- src/Controls.cpp | 18 ++++++++++++------ src/Service.cpp | 1 - src/VirtualTap.hpp | 4 ---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Controls.cpp b/src/Controls.cpp index c2b8152..7265f09 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -94,9 +94,6 @@ bool _run_lwip_tcpip = false; //bool _startupError = false; -pthread_t service_thread; -pthread_t callback_thread; - // Global reference to ZeroTier service OneService *service; @@ -272,7 +269,7 @@ int __zts_can_perform_service_operation() void _api_sleep(int interval_ms) { -#if defined(_WIN32) +#if defined (_WIN32) Sleep(interval_ms); #else struct timespec sleepValue = {0}; @@ -283,11 +280,14 @@ void _api_sleep(int interval_ms) int _change_nice(int increment) { +#ifndef _WIN32 if (increment == 0) { return 0; } int priority = getpriority(PRIO_PROCESS, 0); return setpriority(PRIO_PROCESS, 0, priority+increment); +#endif + return 0; } ////////////////////////////////////////////////////////////////////////////// @@ -406,7 +406,10 @@ void *_zts_run_service(void *arg) // TODO: Find a more elegant solution _api_sleep(ZTS_CALLBACK_PROCESSING_INTERVAL*2); _run_callbacks = false; +#ifndef _WIN32 pthread_exit(0); +#endif + return NULL; } #ifdef __cplusplus @@ -566,13 +569,16 @@ int zts_start( WSAStartup(MAKEWORD(2, 2), &wsaData); HANDLE serviceThread = CreateThread(NULL, 0, _zts_run_service, (void*)params, 0, NULL); HANDLE callbackThread = CreateThread(NULL, 0, _zts_run_callbacks, NULL, 0, NULL); -#endif +#else + pthread_t service_thread; + pthread_t callback_thread; if ((err = pthread_create(&service_thread, NULL, _zts_run_service, NULL)) != 0) { retval = err; } if ((err = pthread_create(&callback_thread, NULL, _zts_run_callbacks, NULL)) != 0) { retval = err; } +#endif #if defined(__linux__) pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME); pthread_setname_np(callback_thread, ZTS_EVENT_CALLBACK_THREAD_NAME); @@ -674,7 +680,7 @@ int zts_restart() _userCallbackMethodRef = _tmpUserCallbackMethodRef; return zts_start(tmpPath.c_str(), NULL, tmpPort); #else - return zts_start(tmpPath.c_str(), _tmpUserEventCallbackFunc, tmpPort); + return ::zts_start(tmpPath.c_str(), _tmpUserEventCallbackFunc, tmpPort); #endif } #ifdef SDK_JNI diff --git a/src/Service.cpp b/src/Service.cpp index ebf8277..effb198 100644 --- a/src/Service.cpp +++ b/src/Service.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp index 1f45469..a66b3ce 100644 --- a/src/VirtualTap.hpp +++ b/src/VirtualTap.hpp @@ -165,10 +165,6 @@ public: void (*_handler)(void *, void *, uint64_t, const MAC &, const MAC &, unsigned int, unsigned int, const void *, unsigned int); - void phyOnUnixClose(PhySocket *sock, void **uptr); - void phyOnUnixData(PhySocket *sock, void **uptr, void *data, ssize_t len); - void phyOnUnixWritable(PhySocket *sock, void **uptr, bool stack_invoked); - ////////////////////////////////////////////////////////////////////////////// // Lower-level lwIP netif handling and traffic handling readiness // ////////////////////////////////////////////////////////////////////////////// From 3101f8a133c1522c7cb72e1484d5c27a211b48b5 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 18 Jun 2019 12:33:22 -0700 Subject: [PATCH 21/78] Updated example simple.cpp for Windows --- test/simple.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/test/simple.cpp b/test/simple.cpp index 6c39833..323c214 100644 --- a/test/simple.cpp +++ b/test/simple.cpp @@ -1,9 +1,17 @@ -#include - -#include #include + +#ifdef _WIN32 +#include +#include +#include +#include +#else +#include #include #include +#endif + +#include bool node_ready = false; bool network_ready = false; @@ -36,13 +44,22 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) } } +void delay(int n) +{ +#ifdef _WIN32 + Sleep(n * 1000); +#else + sleep(n); +#endif +} + int main() { char *str = "welcome to the machine"; char *remoteIp = "11.7.7.223"; int remotePort = 8082; int fd, err = 0; - struct zts_sockaddr_in addr; + struct sockaddr_in addr; addr.sin_family = ZTS_AF_INET; addr.sin_addr.s_addr = inet_addr(remoteIp); addr.sin_port = htons(remotePort); @@ -50,12 +67,13 @@ int main() // Set up ZeroTier service and wai for callbacks int port = 9994; uint64_t nwid = 0x0123456789abcdef; - zts_start("zt_config/path", &myZeroTierEventCallback, port); + zts_start("path", &myZeroTierEventCallback, port); printf("Waiting for node to come online...\n"); - while (!node_ready) { sleep(1); } + while (!node_ready) { delay(1); } + printf("joining network...\n"); zts_join(nwid); printf("Joined virtual network. Requesting configuration...\n"); - while (!network_ready) { sleep(1); } + while (!network_ready) { delay(1); } printf("I am %llx\n", zts_get_node_id()); // Socket API example From 1adb865a815a64e3ba352a1fd6359fbf62f01002 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 18 Jun 2019 12:45:09 -0700 Subject: [PATCH 22/78] Changes to dist.bat for Windows build --- dist.bat | 188 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 87 deletions(-) diff --git a/dist.bat b/dist.bat index b82f883..f665cda 100644 --- a/dist.bat +++ b/dist.bat @@ -1,88 +1,102 @@ -REM Build all target configurations and copy results into "prebuilt" - -set PrebuiltDebugWin32Dir=staging\debug\win32 -set PrebuiltDebugWin64Dir=staging\debug\win64 -set PrebuiltReleaseWin32Dir=staging\release\win32 -set PrebuiltReleaseWin64Dir=staging\release\win64 - -mkdir %PrebuiltDebugWin32Dir% -mkdir %PrebuiltDebugWin64Dir% -mkdir %PrebuiltReleaseWin32Dir% -mkdir %PrebuiltReleaseWin64Dir% - -set DebugWinBuildDir=bin\lib\Debug -set ReleaseWinBuildDir=bin\lib\Release - -mkdir WinBuild32 & pushd WinBuild32 -cmake -G "Visual Studio 15 2017" ../ -popd -mkdir WinBuild64 & pushd WinBuild64 -cmake -G "Visual Studio 15 2017 Win64" ../ -popd - -cmake --build WinBuild32 --config Release -cmake --build WinBuild32 --config Debug - -copy %DebugWinBuildDir%\zt-static.lib %PrebuiltDebugWin32Dir%\zt.lib -copy %DebugWinBuildDir%\zt-shared.dll %PrebuiltDebugWin32Dir%\zt.dll -copy %ReleaseWinBuildDir%\zt-static.lib %PrebuiltReleaseWin32Dir%\zt.lib -copy %ReleaseWinBuildDir%\zt-shared.dll %PrebuiltReleaseWin32Dir%\zt.dll - -cmake --build WinBuild64 --config Release -cmake --build WinBuild64 --config Debug - -copy %DebugWinBuildDir%\zt-static.lib %PrebuiltDebugWin64Dir%\zt.lib -copy %DebugWinBuildDir%\zt-shared.dll %PrebuiltDebugWin64Dir%\zt.dll -copy %ReleaseWinBuildDir%\zt-static.lib %PrebuiltReleaseWin64Dir%\zt.lib -copy %ReleaseWinBuildDir%\zt-shared.dll %PrebuiltReleaseWin64Dir%\zt.dll - -rd /S /Q bin - -# Build with JNI - -mkdir WinBuild32 & pushd WinBuild32 -cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017" ../ -popd -mkdir WinBuild64 & pushd WinBuild64 -cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017 Win64" ../ -popd - -cmake --build WinBuild32 --config Release -cmake --build WinBuild32 --config Debug - -REM Build JAR file -REM release variant -cd packages\java -del com/zerotier/libzt/*.class -move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll -javac com/zerotier/libzt/*.java -jar cf zt.jar zt.dll com/zerotier/libzt/*.class -move zt.jar ..\..\%PrebuiltReleaseWin32Dir% -REM debug variant -del com/zerotier/libzt/*.class -move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll -javac com/zerotier/libzt/*.java -jar cf zt.jar zt.dll com/zerotier/libzt/*.class -move zt.jar ..\..\%PrebuiltDebugWin32Dir% -popd -popd - -cmake --build WinBuild64 --config Release -cmake --build WinBuild64 --config Debug - -REM Build JAR file -REM release variant -cd packages\java -del com/zerotier/libzt/*.class -move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll -javac com/zerotier/libzt/*.java -jar cf zt.jar zt.dll com/zerotier/libzt/*.class -move zt.jar ..\..\%PrebuiltReleaseWin64Dir% -REM debug variant -del com/zerotier/libzt/*.class -move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll -javac com/zerotier/libzt/*.java -jar cf zt.jar zt.dll com/zerotier/libzt/*.class -move zt.jar ..\..\%PrebuiltDebugWin64Dir% -popd +REM build temp directories +set Win32ReleaseBuildDir=tmp\release\win32 +set Win64ReleaseBuildDir=tmp\release\win64 +set Win32DebugBuildDir=tmp\debug\win32 +set Win64DebugBuildDir=tmp\debug\win64 + +mkdir %Win32ReleaseBuildDir% +mkdir %Win64ReleaseBuildDir% +mkdir %Win32DebugBuildDir% +mkdir %Win64DebugBuildDir% + +REM final output directories +set Win32ReleaseOutputDir=lib\release\win32 +set Win64ReleaseOutputDir=lib\release\win64 +set Win32DebugOutputDir=lib\debug\win32 +set Win64DebugOutputDir=lib\debug\win64 + +mkdir %Win32ReleaseOutputDir% +mkdir %Win64ReleaseOutputDir% +mkdir %Win32DebugOutputDir% +mkdir %Win64DebugOutputDir% + +pushd %Win32ReleaseBuildDir% +cmake -G "Visual Studio 15 2017" ../../../ +cmake --build . --config Release +popd +copy %Win32ReleaseBuildDir%\Release\zt.lib %Win32ReleaseOutputDir%\zt.lib +copy %Win32ReleaseBuildDir%\Release\zt.dll %Win32ReleaseOutputDir%\zt.dll + +pushd %Win32DebugBuildDir% +cmake -G "Visual Studio 15 2017" ../../../ +cmake --build . --config Debug +popd +copy %Win32DebugBuildDir%\Debug\zt.lib %Win32DebugOutputDir%\zt.lib +copy %Win32DebugBuildDir%\Debug\zt.dll %Win32DebugOutputDir%\zt.dll + +pushd %Win64ReleaseBuildDir% +cmake -G "Visual Studio 15 2017 Win64" ../../../ +cmake --build . --config Release +popd +copy %Win64ReleaseBuildDir%\Release\zt.lib %Win64ReleaseOutputDir%\zt.lib +copy %Win64ReleaseBuildDir%\Release\zt.dll %Win64ReleaseOutputDir%\zt.dll + +pushd %Win64DebugBuildDir% +cmake -G "Visual Studio 15 2017 Win64" ../../../ +cmake --build . --config Debug +popd +copy %Win64DebugBuildDir%\Debug\zt.lib %Win64DebugOutputDir%\zt.lib +copy %Win64DebugBuildDir%\Debug\zt.dll %Win64DebugOutputDir%\zt.dll + +exit 0 + +rd /S /Q bin + +# Build with JNI + +mkdir WinBuild32 & pushd WinBuild32 +cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017" ../ +popd +mkdir WinBuild64 & pushd WinBuild64 +cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017 Win64" ../ +popd + +cmake --build WinBuild32 --config Release +cmake --build WinBuild32 --config Debug + +REM Build JAR file +REM release variant +cd packages\java +del com/zerotier/libzt/*.class +move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll +javac com/zerotier/libzt/*.java +jar cf zt.jar zt.dll com/zerotier/libzt/*.class +move zt.jar ..\..\%PrebuiltReleaseWin32Dir% +REM debug variant +del com/zerotier/libzt/*.class +move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll +javac com/zerotier/libzt/*.java +jar cf zt.jar zt.dll com/zerotier/libzt/*.class +move zt.jar ..\..\%PrebuiltDebugWin32Dir% +popd +popd + +cmake --build WinBuild64 --config Release +cmake --build WinBuild64 --config Debug + +REM Build JAR file +REM release variant +cd packages\java +del com/zerotier/libzt/*.class +move ..\..\%ReleaseWinBuildDir%\zt-shared.dll zt.dll +javac com/zerotier/libzt/*.java +jar cf zt.jar zt.dll com/zerotier/libzt/*.class +move zt.jar ..\..\%PrebuiltReleaseWin64Dir% +REM debug variant +del com/zerotier/libzt/*.class +move ..\..\%DebugWinBuildDir%\zt-shared.dll zt.dll +javac com/zerotier/libzt/*.java +jar cf zt.jar zt.dll com/zerotier/libzt/*.class +move zt.jar ..\..\%PrebuiltDebugWin64Dir% +popd popd \ No newline at end of file From b62d63592cf35b0987018702ae45bb7cbce276cd Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 19 Jun 2019 11:53:40 -0700 Subject: [PATCH 23/78] Added version resource file for Windows DLL build --- version.rc.in | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 version.rc.in diff --git a/version.rc.in b/version.rc.in new file mode 100644 index 0000000..891a5e8 --- /dev/null +++ b/version.rc.in @@ -0,0 +1,43 @@ +#define VER_FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,0 +#define VER_FILEVERSION_STR "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@.0\0" + +#define VER_PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,0 +#define VER_PRODUCTVERSION_STR "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@\0" + +#define VER_PRODUCTVERSION VER_FILEVERSION +#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR + +#define APSTUDIO_READONLY_SYMBOLS +#include "windows.h" +#undef APSTUDIO_READONLY_SYMBOLS + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VER_FILEVERSION + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "ZeroTier, Inc.\0" + VALUE "FileDescription", "ZeroTier SDK\0" + VALUE "InternalName", "zt\0" + VALUE "LegalCopyright", "Copyright (C) 2019 ZeroTier, Inc.\0" + VALUE "OriginalFilename", "libzt.dll\0" + VALUE "PrivateBuild", "0\0" + VALUE "ProductName", "libzt\0" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END \ No newline at end of file From b14c6e7a684a198c62d047551e0fbd8d68d68819 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 19 Jun 2019 12:58:02 -0700 Subject: [PATCH 24/78] Better naming scheme for Windows libraries --- dist.bat | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/dist.bat b/dist.bat index f665cda..5041cfa 100644 --- a/dist.bat +++ b/dist.bat @@ -10,43 +10,39 @@ mkdir %Win32DebugBuildDir% mkdir %Win64DebugBuildDir% REM final output directories -set Win32ReleaseOutputDir=lib\release\win32 -set Win64ReleaseOutputDir=lib\release\win64 -set Win32DebugOutputDir=lib\debug\win32 -set Win64DebugOutputDir=lib\debug\win64 +set WinReleaseOutputDir=lib\release +set WinDebugOutputDir=lib\debug -mkdir %Win32ReleaseOutputDir% -mkdir %Win64ReleaseOutputDir% -mkdir %Win32DebugOutputDir% -mkdir %Win64DebugOutputDir% +mkdir %WinReleaseOutputDir% +mkdir %WinDebugOutputDir% pushd %Win32ReleaseBuildDir% cmake -G "Visual Studio 15 2017" ../../../ cmake --build . --config Release popd -copy %Win32ReleaseBuildDir%\Release\zt.lib %Win32ReleaseOutputDir%\zt.lib -copy %Win32ReleaseBuildDir%\Release\zt.dll %Win32ReleaseOutputDir%\zt.dll +copy %Win32ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\libzt32.lib +copy %Win32ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\libzt32.dll pushd %Win32DebugBuildDir% cmake -G "Visual Studio 15 2017" ../../../ cmake --build . --config Debug popd -copy %Win32DebugBuildDir%\Debug\zt.lib %Win32DebugOutputDir%\zt.lib -copy %Win32DebugBuildDir%\Debug\zt.dll %Win32DebugOutputDir%\zt.dll +copy %Win32DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\libzt32d.lib +copy %Win32DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\libzt32d.dll pushd %Win64ReleaseBuildDir% cmake -G "Visual Studio 15 2017 Win64" ../../../ cmake --build . --config Release popd -copy %Win64ReleaseBuildDir%\Release\zt.lib %Win64ReleaseOutputDir%\zt.lib -copy %Win64ReleaseBuildDir%\Release\zt.dll %Win64ReleaseOutputDir%\zt.dll +copy %Win64ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\libzt64.lib +copy %Win64ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\libzt64.dll pushd %Win64DebugBuildDir% cmake -G "Visual Studio 15 2017 Win64" ../../../ cmake --build . --config Debug popd -copy %Win64DebugBuildDir%\Debug\zt.lib %Win64DebugOutputDir%\zt.lib -copy %Win64DebugBuildDir%\Debug\zt.dll %Win64DebugOutputDir%\zt.dll +copy %Win64DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\libzt64d.lib +copy %Win64DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\libzt64d.dll exit 0 From 3c2fe77dd73863eccbf17bdd13f18173a7f3066a Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 20 Jun 2019 14:48:33 -0700 Subject: [PATCH 25/78] Updated CMakeLists.txt for Windows build --- CMakeLists.txt | 62 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index add188e..7410d57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,29 @@ if (BUILDING_LINUX AND NOT BUILDING_ANDROID) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lpthread") endif () +# WINDOWS-specific MSVC flags and libraries +if (BUILDING_WIN) + # 32-bit + if(NOT BUILDING_WIN64) + set (WINLIBDIR, "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.16299.0/um/x86") + endif () + # 64-bit + if(BUILDING_WIN64) + set (WINLIBDIR, "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.16299.0/um/x64") + endif () + #find_library (ws2_32_LIBRARY_PATH NAMES WS2_32 HINTS ${WINLIBDIR}) + #find_library (shlwapi_LIBRARY_PATH NAMES ShLwApi HINTS ${WINLIBDIR}) + set (ws2_32_LIBRARY_PATH "${WINLIBDIR}/WS2_32.Lib") + set (shlwapi_LIBRARY_PATH "${WINLIBDIR}/ShLwApi.Lib") + set (iphlpapi_LIBRARY_PATH "${WINLIBDIR}/iphlpapi.Lib") + message (STATUS ${WINLIBDIR}) + message (STATUS "WS2_32=${ws2_32_LIBRARY_PATH}") + message (STATUS "ShLwApi=${shlwapi_LIBRARY_PATH}") + message (STATUS "liphlpapi=${iphlpapi_LIBRARY_PATH}") + add_definitions (-DZT_SDK=1) + add_definitions (-DADD_EXPORTS=1) +endif () + # ----------------------------------------------------------------------------- # | JNI | # ----------------------------------------------------------------------------- @@ -235,6 +258,7 @@ file (GLOB ztcoreSrcGlob file (GLOB libnatpmpSrcGlob ${ZTO_SRC_DIR}/ext/libnatpmp/natpmp.c + ${ZTO_SRC_DIR}/ext/libnatpmp/wingettimeofday.c ${ZTO_SRC_DIR}/ext/libnatpmp/getgateway.c) file (GLOB libminiupnpcSrcGlob @@ -408,22 +432,22 @@ set_target_properties (ztcore PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # libnatpmp.a -add_library (natpmp STATIC $) -set_target_properties (natpmp PROPERTIES - OUTPUT_NAME natpmp - LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) +#add_library (natpmp STATIC $) +#set_target_properties (natpmp PROPERTIES +# OUTPUT_NAME natpmp +# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # libminiupnpc.a -add_library (miniupnpc STATIC $) -set_target_properties (miniupnpc PROPERTIES - OUTPUT_NAME miniupnpc - LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) +#add_library (miniupnpc STATIC $) +#set_target_properties (miniupnpc PROPERTIES +# OUTPUT_NAME miniupnpc +# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # liblwip.a -add_library (lwip STATIC $) -set_target_properties (lwip PROPERTIES - OUTPUT_NAME lwip - LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) +#add_library (lwip STATIC $) +#set_target_properties (lwip PROPERTIES +# OUTPUT_NAME lwip +# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # libzt.a add_library (${STATIC_LIB_NAME} STATIC $ @@ -435,6 +459,13 @@ set_target_properties (${STATIC_LIB_NAME} PROPERTIES OUTPUT_NAME zt LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) set_target_properties (${STATIC_LIB_NAME} PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}") +if (BUILDING_WIN) + target_link_libraries ( + ${STATIC_LIB_NAME} + ${ws2_32_LIBRARY_PATH} + ${shlwapi_LIBRARY_PATH} + ${iphlpapi_LIBRARY_PATH}) +endif () # libzt.so/dylib/dll add_library (${DYNAMIC_LIB_NAME} SHARED ${libztSrcGlob}) @@ -442,6 +473,12 @@ target_link_libraries (${DYNAMIC_LIB_NAME} zt_pic lwip_pic zto_pic natpmp_pic mi set_target_properties (${DYNAMIC_LIB_NAME} PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}") set_target_properties (${DYNAMIC_LIB_NAME} PROPERTIES OUTPUT_NAME ${DYNAMIC_LIB_OUTPUT_NAME} WINDOWS_EXPORT_ALL_SYMBOLS true) +target_link_libraries ( + ${DYNAMIC_LIB_NAME} + ${ws2_32_LIBRARY_PATH} + ${shlwapi_LIBRARY_PATH} + ${iphlpapi_LIBRARY_PATH} zt_pic lwip_pic zto_pic natpmp_pic miniupnpc_pic) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) if (BUILDING_ANDROID) @@ -517,4 +554,5 @@ install (TARGETS ${STATIC_LIB_NAME} ) install (TARGETS ${DYNAMIC_LIB_NAME} LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib ) From fe30716143a4b59c82e151b05895f24fc70aae15 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 24 Jun 2019 13:59:58 -0700 Subject: [PATCH 26/78] Added arm64-v8a, x86, x86_64 ABIs to build.gradle --- dist.sh | 7 +++---- include/ZeroTier.h | 2 +- ports/android/app/build.gradle | 2 +- src/lwipopts.h | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/dist.sh b/dist.sh index f850519..fd41829 100755 --- a/dist.sh +++ b/dist.sh @@ -236,9 +236,8 @@ android() if [[ ! $OSNAME = *"darwin"* ]]; then exit 0 fi - ARCH="armeabi-v7a" # CMake build files - BUILD_DIR=$(pwd)/tmp/android-$ARCH-$1 + BUILD_DIR=$(pwd)/tmp/android-$1 mkdir -p $BUILD_DIR # If clean requested, remove temp build dir if [[ $1 = *"clean"* ]]; then @@ -246,7 +245,7 @@ android() exit 0 fi # Where to place results - LIB_OUTPUT_DIR=$(pwd)/lib/$1/android-$ARCH + LIB_OUTPUT_DIR=$(pwd)/lib/$1/android mkdir -p $LIB_OUTPUT_DIR # Build UPPERCASE_CONFIG="$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}" @@ -293,7 +292,7 @@ prep_android_example() { echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" mkdir -p examples/android/ExampleAndroidApp/app/libs/ - cp -f lib/$1/android-armeabi-v7a/libzt-$1.aar \ + cp -f lib/$1/android/libzt-$1.aar \ examples/android/ExampleAndroidApp/app/libs/libzt.aar } # Clean Android project diff --git a/include/ZeroTier.h b/include/ZeroTier.h index 1d78588..216efaa 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -55,7 +55,7 @@ typedef int ssize_t; #if !defined(_WIN32) && !defined(__ANDROID__) typedef unsigned int socklen_t; #else -typedef int socklen_t; +//typedef int socklen_t; //#include #endif diff --git a/ports/android/app/build.gradle b/ports/android/app/build.gradle index d168a4b..ca41335 100644 --- a/ports/android/app/build.gradle +++ b/ports/android/app/build.gradle @@ -16,7 +16,7 @@ android { ndk { // Tells Gradle to build outputs for the following ABIs and package // them into your APK. - abiFilters 'armeabi-v7a' + abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } } buildTypes { diff --git a/src/lwipopts.h b/src/lwipopts.h index 1d6b390..cce9749 100644 --- a/src/lwipopts.h +++ b/src/lwipopts.h @@ -55,7 +55,7 @@ #define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS 0 #endif #if __ANDROID__ -#define LWIP_PROVIDE_ERRNO 1 +//#define LWIP_PROVIDE_ERRNO 0 #define SOCKLEN_T_DEFINED #elif !defined(_MSC_VER) #define LWIP_PROVIDE_ERRNO 1 From b1c4b59c809f48080ff51e11971b863b689276f6 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 24 Jun 2019 15:03:45 -0700 Subject: [PATCH 27/78] Updated ZeroTierOne submodule revision to 1.2.99 (1.4.0pre) --- ext/ZeroTierOne | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ZeroTierOne b/ext/ZeroTierOne index 4743ad0..554e0b7 160000 --- a/ext/ZeroTierOne +++ b/ext/ZeroTierOne @@ -1 +1 @@ -Subproject commit 4743ad0b16a72c00039de1aed98499fe0adf633f +Subproject commit 554e0b7c6de6eb0a3c1014b0bbfb5b967d427ed8 From d489d50a18147da9fe60137a3e41030afcc9c9f0 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 25 Jun 2019 16:26:49 -0700 Subject: [PATCH 28/78] Added NATPMP_EXPORTS to libnatpmp targets --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7410d57..4d26203 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,7 +364,7 @@ endif () # libnatpmp_obj add_library (libnatpmp_obj OBJECT ${libnatpmpSrcGlob}) -set_target_properties (libnatpmp_obj PROPERTIES COMPILE_FLAGS "") +set_target_properties (libnatpmp_obj PROPERTIES COMPILE_FLAGS "-DNATPMP_EXPORTS") # miniupnpc_obj add_library (miniupnpc_obj OBJECT ${libminiupnpcSrcGlob}) @@ -397,6 +397,7 @@ set_target_properties (zto_pic PROPERTIES # libnatpmp_pic add_library (natpmp_pic ${libnatpmpSrcGlob}) set_target_properties (natpmp_pic PROPERTIES + COMPILE_FLAGS "-DNATPMP_EXPORTS" POSITION_INDEPENDENT_CODE ON) # miniupnpc_pic From 3e0c7a265cdcda0a890d73fb75e3795f14dd6c4b Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 28 Jun 2019 09:28:31 -0700 Subject: [PATCH 29/78] Added SWIG language wrapper generator section to dist.sh --- dist.sh | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/dist.sh b/dist.sh index fd41829..e235e77 100755 --- a/dist.sh +++ b/dist.sh @@ -1,7 +1,7 @@ #!/bin/bash # This script works in conjunction with the Makefile and CMakeLists.txt. It is -# intented to be called from the Makefile, it generates projects and builds +# intended to be called from the Makefile, it generates projects and builds # targets as specified in CMakeLists.txt. In addition, this script is # responsible for packaging all of the resultant builds, licenses, and # documentation as well as controlling the installation and remote execution of @@ -40,6 +40,60 @@ XCODE_IOS_PROJ_DIR=$(pwd)/ports/xcode_ios XCODE_IOS_SIMULATOR_PROJ_DIR=$(pwd)/ports/xcode_ios_simulator XCODE_MACOS_PROJ_DIR=$(pwd)/ports/xcode_macos +# Generates wrapper source files for various target languages +generate_swig_wrappers() +{ + SRC=../../src + + cd ports/swig; + + # C# + mkdir -p ${SRC}/csharp + swig -csharp -c++ zt.i + # Prepend our callback garb to libzt.cs, copy new source files into src/csharp + cat csharp/csharp_callback.cs libzt.cs > libzt_concat.cs + rm libzt.cs + mv libzt_concat.cs libzt.cs + mv -f *.cs zt_wrap.cxx ${SRC}/csharp/ + + # Javascript + # Build for all three engines. Why not? + ENGINE=jsc + mkdir -p ${SRC}/js/${ENGINE} + swig -javascript -${ENGINE} -c++ zt.i + mv zt_wrap.cxx ${SRC}/js/${ENGINE} + ENGINE=v8 + mkdir -p ${SRC}/js/${ENGINE} + swig -javascript -${ENGINE} -c++ zt.i + mv zt_wrap.cxx ${SRC}/js/${ENGINE} + ENGINE=node + mkdir -p ${SRC}/js/${ENGINE} + swig -javascript -${ENGINE} -c++ zt.i + mv -f zt_wrap.cxx ${SRC}/js/${ENGINE} + + # Python + mkdir -p ${SRC}/python + swig -python -c++ zt.i + mv -f zt_wrap.cxx *.py ${SRC}/python + + # Lua + mkdir -p ${SRC}/lua + swig -lua -c++ zt.i + mv -f zt_wrap.cxx ${SRC}/lua + + # Go 64 + mkdir -p ${SRC}/go64 + swig -intgosize 64 -go -c++ zt.i + mv -f zt_wrap.cxx *.go *.c ${SRC}/go64 + + # Go 32 + mkdir -p ${SRC}/go32 + swig -intgosize 32 -go -c++ zt.i + mv -f zt_wrap.cxx *.go *.c ${SRC}/go32 + + cd - +} + # Generates projects if needed generate_projects() { From 92a627eed916c9f0ee63e82d17b93f9182897c61 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 28 Jun 2019 12:18:52 -0700 Subject: [PATCH 30/78] Added SWIG wrapper paths to CMakeLists.txt --- CMakeLists.txt | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d26203..920c9ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,7 +242,7 @@ if (SDK_JNI OR BUILDING_ANDROID) endif () # SDK_JNI # ----------------------------------------------------------------------------- -# | SOURCE FILE GLOBS | +# | SOURCES | # ----------------------------------------------------------------------------- set (PROJ_DIR ${PROJECT_SOURCE_DIR}) @@ -250,6 +250,31 @@ set (LWIP_SRC_DIR "${PROJ_DIR}/ext/lwip/src") set (ZTO_SRC_DIR "${PROJ_DIR}/ext/ZeroTierOne") set (LIBZT_SRC_DIR "${PROJ_DIR}/src") +if (SWIG_CSHARP) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/csharp/*.cxx) +endif () +if (SWIG_PYTHON) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/python/*.cxx) +endif () +if (SWIG_LUA) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/lua/*.cxx) +endif () +if (SWIG_GO32) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/go32/*.cxx) +endif () +if (SWIG_GO64) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/go64/*.cxx) +endif () +if (SWIG_JS_JSC) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/jsc/*.cxx) +endif () +if (SWIG_JS_V8) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/v8/*.cxx) +endif () +if (SWIG_JS_NODE) +set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/node/*.cxx) +endif () + file (GLOB ztcoreSrcGlob ${ZTO_SRC_DIR}/node/*.cpp ${ZTO_SRC_DIR}/osdep/OSUtils.cpp @@ -276,7 +301,7 @@ file (GLOB libminiupnpcSrcGlob ${ZTO_SRC_DIR}/ext/miniupnpc/upnperrors.c ${ZTO_SRC_DIR}/ext/miniupnpc/upnpreplyparse.c) -file (GLOB libztSrcGlob ${LIBZT_SRC_DIR}/*.cpp) +file (GLOB libztSrcGlob ${LIBZT_SRC_DIR}/*.cpp ${libztSwigWrapperSrc}) if (UNIX) set (LWIP_PORT_DIR ${PROJ_DIR}/ext/lwip-contrib/ports/unix/port) From e9e1f8920d213a7ef568653bf7374184e65b5711 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 2 Jul 2019 10:14:39 -0700 Subject: [PATCH 31/78] Naming convention tweaks for SWIG wrapper compatibility --- dist.sh | 4 +- ports/csharp/csharp_callback.cs | 255 ++++++++++++++++++++++++++++++++ ports/zt.i | 30 ++++ src/Controls.cpp | 40 ++--- src/Controls.hpp | 4 +- 5 files changed, 309 insertions(+), 24 deletions(-) create mode 100644 ports/csharp/csharp_callback.cs create mode 100644 ports/zt.i diff --git a/dist.sh b/dist.sh index e235e77..ad1e388 100755 --- a/dist.sh +++ b/dist.sh @@ -43,9 +43,9 @@ XCODE_MACOS_PROJ_DIR=$(pwd)/ports/xcode_macos # Generates wrapper source files for various target languages generate_swig_wrappers() { - SRC=../../src + SRC=../src - cd ports/swig; + cd ports/; # C# mkdir -p ${SRC}/csharp diff --git a/ports/csharp/csharp_callback.cs b/ports/csharp/csharp_callback.cs new file mode 100644 index 0000000..b4babfd --- /dev/null +++ b/ports/csharp/csharp_callback.cs @@ -0,0 +1,255 @@ +using System.Runtime.InteropServices; + +public struct CallbackMessage +{ + public int eventCode; + /* Pointers to structures that contain details about the + subject of the callback */ + public System.IntPtr node; + public System.IntPtr network; + public System.IntPtr netif; + public System.IntPtr route; + public System.IntPtr path; + public System.IntPtr peer; + public System.IntPtr addr; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SockAddrStorage +{ + public byte Length; + public byte Family; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public byte[] Data1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] Data2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public uint[] Data3; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SockAddr +{ + public ushort Family; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public byte[] Data; +} + +[StructLayout(LayoutKind.Sequential)] +public struct SockAddrIn +{ + public byte Length; + public byte Family; + public ushort Port; + public uint Addr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] Zero; +} + +public struct NodeDetails +{ + /** + * The node ID + */ + public ulong address; + + /** + * The current clock value accord to the node + */ + public ulong clock; + + /** + * Whether or not this node is online + */ + public bool online; + + /** + * Whether port mapping is enabled + */ + public bool portMappingEnabled; + + /** + * Whether multipath support is enabled. If true, this node will + * be capable of utilizing multiple physical links simultaneosly + * to create higher quality or more robust aggregate links. + * + * See: https://www.zerotier.com/manual.shtml#2_1_5 + */ + public bool multipathEnabled; + + /** + * The port used by the service to send and receive + * all encapsulated traffic + */ + public ushort primaryPort; + + /** + * Planet ID + */ + public ulong planetWorldId; + public ulong planetWorldTimestamp; + public byte versionMajor; + public byte versionMinor; + public byte versionRev; +}; +struct AddrDetails +{ + public ulong nwid; + public SockAddrStorage addr; +}; + +struct NetifDetails +{ + /** + * The virtual network that this interface was commissioned for. + */ + public ulong nwid; + + /** + * The hardware address assigned to this interface + */ + public ulong mac; + + /** + * The MTU for this interface + */ + public int mtu; +}; + +struct RouteDetails +{ + /** + * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default + */ + public System.IntPtr target; + + /** + * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) + */ + public System.IntPtr via; + + /** + * Route flags + */ + public ushort flags; + + /** + * Route metric (not currently used) + */ + public ushort metric; +}; + +struct NetworkDetails +{ + /** + * Network ID + */ + public ulong nwid; + + /** + * Maximum Transmission Unit size for this network + */ + public int mtu; + + /** + * Number of addresses (actually) assigned to the node on this network + */ + public short num_addresses; + + /** + * Array of IPv4 and IPv6 addresses assigned to the node on this network + */ + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)] + public System.IntPtr[] addr; + + /** + * Number of routes + */ + public uint num_routes; + + /** + * Array of IPv4 and IPv6 addresses assigned to the node on this network + */ + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 32)] + public System.IntPtr[] routes; +}; + +struct PathDetails +{ + /** + * Address of endpoint + */ + public System.IntPtr address; + + /** + * Time of last send in milliseconds or 0 for never + */ + public ulong lastSend; + + /** + * Time of last receive in milliseconds or 0 for never + */ + public ulong lastReceive; + + /** + * Is this a trusted path? If so this will be its nonzero ID. + */ + public ulong trustedPathId; + + /** + * Is path expired? + */ + int expired; + + /** + * Is path preferred? + */ + int preferred; +}; + +struct PeerDetails +{ + /** + * ZeroTier address (40 bits) + */ + public ulong address; + + /** + * Remote major version or -1 if not known + */ + int versionMajor; + + /** + * Remote minor version or -1 if not known + */ + int versionMinor; + + /** + * Remote revision or -1 if not known + */ + int versionRev; + + /** + * Last measured latency in milliseconds or -1 if unknown + */ + int latency; + + /** + * What trust hierarchy role does this device have? + */ + public int role; + + /** + * Number of paths (size of paths[]) + */ + public uint pathCount; + + /** + * Known network paths to peer + */ + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)] + public System.IntPtr[] paths; +}; + +[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] +public delegate void CSharpCallback(System.IntPtr msg); diff --git a/ports/zt.i b/ports/zt.i new file mode 100644 index 0000000..69aa836 --- /dev/null +++ b/ports/zt.i @@ -0,0 +1,30 @@ +/* libzt.i */ + +%begin +%{ +#define SWIG_PYTHON_CAST_MODE +%} + +%include + +#define PYTHON_BUILD 1 + +%module libzt +%{ +#include "../include/ZeroTier.h" +#include "../include/ZeroTierConstants.h" +%} + +%define %cs_callback(TYPE, CSTYPE) + %typemap(ctype) TYPE, TYPE& "void *" + %typemap(in) TYPE %{ $1 = ($1_type)$input; %} + %typemap(in) TYPE& %{ $1 = ($1_type)&$input; %} + %typemap(imtype, out="IntPtr") TYPE, TYPE& "CSTYPE" + %typemap(cstype, out="IntPtr") TYPE, TYPE& "CSTYPE" + %typemap(csin) TYPE, TYPE& "$csinput" +%enddef + +%cs_callback(userCallbackFunc, CSharpCallback) + +%include "../include/ZeroTier.h" +%include "../include/ZeroTierConstants.h" diff --git a/src/Controls.cpp b/src/Controls.cpp index 7265f09..c7d4a8a 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -253,12 +253,12 @@ void _clear_registered_callback() _callback_lock.unlock(); } -int __zts_node_online() +int _zts_node_online() { return service && service->getNode() && service->getNode()->online(); } -int __zts_can_perform_service_operation() +int _zts_can_perform_service_operation() { return service && service->isRunning() @@ -436,7 +436,7 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_init( int zts_join(const uint64_t nwid) { Mutex::Lock _l(_service_lock); - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } else { @@ -455,7 +455,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( int zts_leave(const uint64_t nwid) { Mutex::Lock _l(_service_lock); - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } else { @@ -474,7 +474,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( int zts_leave_all() { Mutex::Lock _l(_service_lock); - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } else { @@ -489,7 +489,7 @@ int zts_orbit(uint64_t moonWorldId, uint64_t moonSeed) { Mutex::Lock _l(_service_lock); void *tptr = NULL; - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } else { service->getNode()->orbit(tptr, moonWorldId, moonSeed); @@ -503,7 +503,7 @@ int zts_deorbit(uint64_t moonWorldId) { Mutex::Lock _l(_service_lock); void *tptr = NULL; - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } else { service->getNode()->deorbit(tptr, moonWorldId); @@ -514,7 +514,7 @@ int zts_deorbit(uint64_t moonWorldId) #endif int zts_start( - const char *path, void (*callback)(struct zts_callback_msg*), int port) + const char *path, userCallbackFunc callback, int port) { Mutex::Lock _l(_service_lock); lwip_driver_init(); @@ -627,7 +627,7 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_start( int zts_stop() { Mutex::Lock _l(_service_lock); - if (__zts_can_perform_service_operation()) { + if (_zts_can_perform_service_operation()) { _run_service = false; service->terminate(); #if defined(_WIN32) @@ -658,7 +658,7 @@ int zts_restart() int tmpPort = _port; std::string tmpPath = _path; // Stop the service - if (__zts_can_perform_service_operation()) { + if (_zts_can_perform_service_operation()) { _run_service = false; service->terminate(); #if defined(_WIN32) @@ -713,7 +713,7 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( uint64_t zts_get_node_id() { Mutex::Lock _l(_service_lock); - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } return service->getNode()->address(); @@ -733,7 +733,7 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( int zts_get_peer_count() { Mutex::Lock _l(_service_lock); - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } return service->getNode()->peers()->peerCount; @@ -752,7 +752,7 @@ int zts_get_peers(struct zts_peer_details *pds, int *num) if (!pds || !num) { return ZTS_ERR_INVALID_ARG; } - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } ZT_PeerList *pl = service->getNode()->peers(); @@ -782,7 +782,7 @@ int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) if (!pd || !peerId) { return ZTS_ERR_INVALID_ARG; } - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } ZT_PeerList *pl = service->getNode()->peers(); @@ -814,7 +814,7 @@ int zts_get_num_joined_networks() { Mutex::Lock _l(_service_lock); int retval = ZTS_ERR_OK; - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } return service->networkCount(); @@ -831,7 +831,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1num_1joined_1networ // Network Details // ////////////////////////////////////////////////////////////////////////////// -void __get_network_details_helper(uint64_t nwid, struct zts_network_details *nd) +void _get_network_details_helper(uint64_t nwid, struct zts_network_details *nd) { /* socklen_t addrlen; @@ -854,7 +854,7 @@ void _get_network_details(uint64_t nwid, struct zts_network_details *nd) { /* _vtaps_lock.lock(); - __get_network_details_helper(nwid, nd); + _get_network_details_helper(nwid, nd); _vtaps_lock.unlock(); */ } @@ -1061,7 +1061,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1protocol_1stats( int zts_get_node_status() { Mutex::Lock _l(_service_lock); - // Don't check __zts_can_perform_service_operation() here. + // Don't check _zts_can_perform_service_operation() here. return service && service->getNode() && service->getNode()->online() ? ZTS_EVENT_NODE_ONLINE : ZTS_EVENT_NODE_OFFLINE; @@ -1080,7 +1080,7 @@ int zts_get_network_status(uint64_t networkId) if (!networkId) { return ZTS_ERR_INVALID_ARG; } - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } /* @@ -1103,7 +1103,7 @@ int zts_get_peer_status(uint64_t peerId) { Mutex::Lock _l(_service_lock); int retval = ZTS_ERR_OK; - if (!__zts_can_perform_service_operation()) { + if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } return service->getPeerStatus(peerId); diff --git a/src/Controls.hpp b/src/Controls.hpp index c78246b..2981546 100644 --- a/src/Controls.hpp +++ b/src/Controls.hpp @@ -94,7 +94,7 @@ void *_zts_run_service(void *thread_id); * @usage Can be called at any time * @return 1 or 0 */ -int __zts_can_perform_service_operation(); +int _zts_can_perform_service_operation(); /** * @brief [Should not be called from user application] Returns whether or not the node is @@ -102,7 +102,7 @@ int __zts_can_perform_service_operation(); * @usage Can be called at any time * @return 1 or 0 */ -int __zts_node_online(); +int _zts_node_online(); /** * @brief [Should not be called from user application] Adjusts the delay multiplier for the From 04d31e82955a355135adbbaf4c019186c1602cf1 Mon Sep 17 00:00:00 2001 From: SleepyJesse Date: Fri, 14 Feb 2020 12:16:32 +0800 Subject: [PATCH 32/78] Fix a minor typo in the Java demo. --- examples/java/src/main/java/ExampleApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/java/src/main/java/ExampleApp.java b/examples/java/src/main/java/ExampleApp.java index 9eedc0d..3d024b4 100644 --- a/examples/java/src/main/java/ExampleApp.java +++ b/examples/java/src/main/java/ExampleApp.java @@ -44,7 +44,7 @@ public class ExampleApp // Wait for EVENT_NODE_ONLINE System.out.println("waiting for node to come online..."); while (listener.isOnline == false) { sleep(50); } - System.out.println("joinging network"); + System.out.println("joining network"); ZeroTier.join(0x0123456789abcdefL); // Wait for EVENT_NETWORK_READY_IP4/6 System.out.println("waiting for network config..."); From 50960b15b325f7c3325af6cb11cabf55d737e2ff Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 6 Apr 2020 14:41:44 -0700 Subject: [PATCH 33/78] Update ZTO subproject commit to 28df0c2e, (1.2.99 to 1.4.6) --- ext/ZeroTierOne | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ZeroTierOne b/ext/ZeroTierOne index 554e0b7..28df0c2 160000 --- a/ext/ZeroTierOne +++ b/ext/ZeroTierOne @@ -1 +1 @@ -Subproject commit 554e0b7c6de6eb0a3c1014b0bbfb5b967d427ed8 +Subproject commit 28df0c2e383ddb88d1afef2d0ac52046edd6cdbf From 51dfa84ba99b70f05c2bfbfa76c7f2b13965b8db Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 13 Apr 2020 23:38:06 -0700 Subject: [PATCH 34/78] Fix netif IPv6 handling, misc cleanup, license update from GPL-3 to BSL 1.1 --- API.md | 6 +- LICENSE.GPL-3 | 674 -------------------------------- LICENSE.txt | 149 +++++++ dist.sh | 4 +- ext/lwip.patch | 101 ++++- include/Xcode-Bridging-Header.h | 27 +- include/ZeroTier.h | 36 +- include/ZeroTierConstants.h | 50 ++- ports/Homebrew/libzt.rb | 2 +- src/Controls.cpp | 48 ++- src/Controls.hpp | 27 +- src/Debug.hpp | 27 +- src/Intercept.cpp | 0 src/Intercept.hpp | 0 src/Options.h | 13 + src/Service.cpp | 31 +- src/Service.hpp | 27 +- src/Sockets.cpp | 31 +- src/VirtualTap.cpp | 39 +- src/VirtualTap.hpp | 30 +- src/lwipDriver.cpp | 177 ++++++--- src/lwipDriver.hpp | 27 +- src/lwipRawDriver.cpp | 0 src/lwipRawDriver.hpp | 0 24 files changed, 506 insertions(+), 1020 deletions(-) delete mode 100644 LICENSE.GPL-3 create mode 100644 LICENSE.txt delete mode 100644 src/Intercept.cpp delete mode 100644 src/Intercept.hpp delete mode 100644 src/lwipRawDriver.cpp delete mode 100644 src/lwipRawDriver.hpp diff --git a/API.md b/API.md index 4f230bb..1cb842e 100644 --- a/API.md +++ b/API.md @@ -4,9 +4,11 @@ ZeroTier SDK

-The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard [socket API](https://en.wikipedia.org/wiki/Berkeley_sockets) and simple network control API. The full source for these products can be found at [github.com/zerotier/libzt](https://github.com/zerotier/libzt) for the SDK and [github.com/zerotier/ZeroTierOne](https://github.com/zerotier/ZeroTierOne) for the desktop client. With these libraries the network stack and virtual link are exclusive to your app and traffic is fully encrypted via the [Salsa20](https://en.wikipedia.org/wiki/Salsa20) cipher. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml?pk_campaign=github_libzt) +The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard [socket API](https://en.wikipedia.org/wiki/Berkeley_sockets) and simple network control API. The network stack and virtual link are exclusive to your app and traffic is fully encrypted end-to-end. -The ZeroTier source code is open source and is licensed under the GNU GPL v3 (not LGPL). If you'd like to embed it in a closed-source commercial product or appliance, please e-mail contact@zerotier.com to discuss commercial licensing. Otherwise it can be used for free. +ZeroTier is licensed under the BSL version 1.1. See LICENSE.txt and the ZeroTier pricing page for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. + +A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md] for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.).
diff --git a/LICENSE.GPL-3 b/LICENSE.GPL-3 deleted file mode 100644 index 94a9ed0..0000000 --- a/LICENSE.GPL-3 +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9784015 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,149 @@ +----------------------------------------------------------------------------- + +Business Source License 1.1 + +License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. +"Business Source License" is a trademark of MariaDB Corporation Ab. + +----------------------------------------------------------------------------- + +Parameters + +Licensor: ZeroTier, Inc. +Licensed Work: ZeroTier Network Virtualization Engine 1.4.4 + The Licensed Work is (c)2019 ZeroTier, Inc. +Additional Use Grant: You may make use of the Licensed Work, provided you + do not use it in any of the following ways: + + * Sell hosted ZeroTier services as a "SaaS" Product + + (1) Operate or sell access to ZeroTier root servers, + network controllers, or authorization key or certificate + generation components of the Licensed Work as a + for-profit service, regardless of whether the use of + these components is sold alone or is bundled with other + services. Note that this does not apply to the use of + ZeroTier behind the scenes to operate a service not + related to ZeroTier network administration. + + * Create Non-Open-Source Commercial Derviative Works + + (2) Link or directly include the Licensed Work in a + commercial or for-profit application or other product + not distributed under an Open Source Initiative (OSI) + compliant license. See: https://opensource.org/licenses + + (3) Remove the name, logo, copyright, or other branding + material from the Licensed Work to create a "rebranded" + or "white labeled" version to distribute as part of + any commercial or for-profit product or service. + + * Certain Government Uses + + (4) Use or deploy the Licensed Work in a government + setting in support of any active government function + or operation with the exception of the following: + physical or mental health care, family and social + services, social welfare, senior care, child care, and + the care of persons with disabilities. + +Change Date: 2023-01-01 + +Change License: Apache License version 2.0 as published by the Apache + Software Foundation + https://www.apache.org/licenses/ + +Alternative Licensing + +If you would like to use the Licensed Work in any way that conflicts with +the stipulations of the Additional Use Grant, contact ZeroTier, Inc. to +obtain an alternative commercial license. + +Visit us on the web at: https://www.zerotier.com/ + +Notice + +The Business Source License (this document, or the "License") is not an Open +Source license. However, the Licensed Work will eventually be made available +under an Open Source License, as stated in this License. + +For more information on the use of the Business Source License for ZeroTier +products, please visit our pricing page which contains license details and +and license FAQ: https://zerotier.com/pricing + +For more information on the use of the Business Source License generally, +please visit the Adopting and Developing Business Source License FAQ at +https://mariadb.com/bsl-faq-adopting. + +----------------------------------------------------------------------------- + +Business Source License 1.1 + +Terms + +The Licensor hereby grants you the right to copy, modify, create derivative +works, redistribute, and make non-production use of the Licensed Work. The +Licensor may make an Additional Use Grant, above, permitting limited +production use. + +Effective on the Change Date, or the fourth anniversary of the first publicly +available distribution of a specific version of the Licensed Work under this +License, whichever comes first, the Licensor hereby grants you rights under +the terms of the Change License, and the rights granted in the paragraph +above terminate. + +If your use of the Licensed Work does not comply with the requirements +currently in effect as described in this License, you must purchase a +commercial license from the Licensor, its affiliated entities, or authorized +resellers, or you must refrain from using the Licensed Work. + +All copies of the original and modified Licensed Work, and derivative works +of the Licensed Work, are subject to this License. This License applies +separately for each version of the Licensed Work and the Change Date may vary +for each version of the Licensed Work released by Licensor. + +You must conspicuously display this License on each original or modified copy +of the Licensed Work. If you receive the Licensed Work in original or +modified form from a third party, the terms and conditions set forth in this +License apply to your use of that work. + +Any use of the Licensed Work in violation of this License will automatically +terminate your rights under this License for the current and all other +versions of the Licensed Work. + +This License does not grant you any right in any trademark or logo of +Licensor or its affiliates (provided that you may use a trademark or logo of +Licensor as expressly required by this License). + +TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +TITLE. + +----------------------------------------------------------------------------- + +MariaDB hereby grants you permission to use this License’s text to license +your works, and to refer to it using the trademark "Business Source License", +as long as you comply with the Covenants of Licensor below. + +Covenants of Licensor + +In consideration of the right to use this License’s text and the "Business +Source License" name and trademark, Licensor covenants to MariaDB, and to all +other recipients of the licensed work to be provided by Licensor: + +1. To specify as the Change License the GPL Version 2.0 or any later version, + or a license that is compatible with GPL Version 2.0 or a later version, + where "compatible" means that software provided under the Change License can + be included in a program with software provided under GPL Version 2.0 or a + later version. Licensor may specify additional Change Licenses without + limitation. + +2. To either: (a) specify an additional grant of rights to use that does not + impose any additional restriction on the right granted in this License, as + the Additional Use Grant; or (b) insert the text "None". + +3. To specify a Change Date. + +4. Not to modify this License in any other way. \ No newline at end of file diff --git a/dist.sh b/dist.sh index ad1e388..c7dfa17 100755 --- a/dist.sh +++ b/dist.sh @@ -510,7 +510,7 @@ package_licenses() mkdir -p $DEST_DIR cp $CURR_DIR/ext/lwip/COPYING $DEST_DIR/LWIP-LICENSE.BSD cp $CURR_DIR/ext/concurrentqueue/LICENSE.md $DEST_DIR/CONCURRENTQUEUE-LICENSE.BSD - cp $CURR_DIR/LICENSE.GPL-3 $DEST_DIR/ZEROTIER-LICENSE.GPL-3 + cp $CURR_DIR/LICENSE.txt $DEST_DIR/ZEROTIER-LICENSE.BSL-1.1 cp $CURR_DIR/include/net/ROUTE_H-LICENSE.APSL $DEST_DIR/ROUTE_H-LICENSE.APSL cp $CURR_DIR/include/net/ROUTE_H-LICENSE $DEST_DIR/ROUTE_H-LICENSE } @@ -572,7 +572,7 @@ package_everything() doc/errno.h licenses/LWIP-LICENSE.BSD licenses/CONCURRENTQUEUE-LICENSE.BSD - licenses/ZEROTIER-LICENSE.GPL-3 + licenses/ZEROTIER-LICENSE.BSL-1.1 licenses/ROUTE_H-LICENSE.APSL licenses/ROUTE_H-LICENSE licenses/LWIP-LICENSE.BSD" diff --git a/ext/lwip.patch b/ext/lwip.patch index 6ef5d1c..d0ef7c8 100644 --- a/ext/lwip.patch +++ b/ext/lwip.patch @@ -1,18 +1,87 @@ -diff --git a/src/include/lwip/errno.h b/src/include/lwip/errno.h -index 48d6b539..9f59afc8 100644 ---- a/src/include/lwip/errno.h -+++ b/src/include/lwip/errno.h -@@ -174,7 +174,12 @@ extern "C" { - #define EMEDIUMTYPE 124 /* Wrong medium type */ +diff --git a/src/api/sockets.c b/src/api/sockets.c +index 2d231739..a0c24829 100644 +--- a/src/api/sockets.c ++++ b/src/api/sockets.c +@@ -38,6 +38,7 @@ + */ - #ifndef errno --extern int errno; -+//extern int errno; -+#if defined(__linux__) && !defined(__ANDROID__) -+ #include -+#else -+ extern int errno; -+#endif - #endif + #include "lwip/opt.h" ++#include "ZeroTierConstants.h" - #else /* LWIP_PROVIDE_ERRNO */ + #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ + +@@ -281,6 +282,7 @@ static struct lwip_select_cb *select_cb_list; + #define sock_set_errno(sk, e) do { \ + const int sockerr = (e); \ + set_errno(sockerr); \ ++ zts_errno = sockerr; \ + } while (0) + + /* Forward declaration of some functions */ +diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c +index 039b7963..6647c90c 100644 +--- a/src/core/ipv6/nd6.c ++++ b/src/core/ipv6/nd6.c +@@ -1644,9 +1644,27 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif) + * addresses (from autoconfiguration) have no implied subnet assignment, and + * are thus effectively /128 assignments. See RFC 5942 for more on this. */ + for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { +- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && ++ /*if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && + netif_ip6_addr_isstatic(netif, i) && +- ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { ++ ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {*/ ++ int prefix_match = 0; ++ if (ip6_addr_is_zt_6plane(ip6addr)) { ++ prefix_match = ip6_addr_6plane_cmp(ip6addr, netif_ip6_addr(netif, i)); ++ DEBUG_INFO("6plane, prefix_match=%d", prefix_match); ++ } ++ if (ip6_addr_is_zt_adhoc(ip6addr)) { ++ prefix_match = ip6_addr_adhoc_cmp(ip6addr, netif_ip6_addr(netif, i)); ++ DEBUG_INFO("adhoc, prefix_match=%d", prefix_match); ++ } ++ if (ip6_addr_is_zt_rfc4193(ip6addr)) { ++ prefix_match = ip6_addr_rfc4193_cmp(ip6addr, netif_ip6_addr(netif, i)); ++ DEBUG_INFO("rfc4193, prefix_match=%d", prefix_match); ++ } ++ if (!prefix_match) { ++ prefix_match = ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i)); ++ DEBUG_INFO("traditional match, prefix_match=%d", prefix_match); ++ } ++ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && prefix_match) { + return 1; + } + } +diff --git a/src/include/lwip/ip6_addr.h b/src/include/lwip/ip6_addr.h +index 29c2a34d..205b0e1f 100644 +--- a/src/include/lwip/ip6_addr.h ++++ b/src/include/lwip/ip6_addr.h +@@ -160,6 +160,28 @@ typedef struct ip6_addr ip6_addr_t; + #define ip6_addr_netcmp(addr1, addr2) (ip6_addr_netcmp_zoneless((addr1), (addr2)) && \ + ip6_addr_cmp_zone((addr1), (addr2))) + ++/* Determine if an address *could* be a ZeroTier 6PLANE address */ ++#define ip6_addr_is_zt_6plane(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xfc000000UL)) ++ ++/* Compare first 40 bits of address (ff + first 32 bits of nwid XOR'd with second 32 bits of nwid) */ ++#define ip6_addr_6plane_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ ++ ((addr1)->addr[1] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[1] & PP_HTONL(0xff000000UL))) ++ ++/* Determine if an address *could* be a ZeroTier Ad-hoc address */ ++#define ip6_addr_is_zt_adhoc(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) ++ ++/* Compare first 40 bits of address (ff + first 32 bits of nwid XOR'd with second 32 bits of nwid) */ ++#define ip6_addr_adhoc_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ ++ ((addr1)->addr[1] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[1] & PP_HTONL(0xff000000UL))) ++ ++/* Determine if an address *could* be an RFC4193 address */ ++#define ip6_addr_is_zt_rfc4193(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xfd000000UL)) ++ ++/* Compare first 72 bits of address (fd + 64 bits of nwid) */ ++#define ip6_addr_rfc4193_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ ++ ((addr1)->addr[1] == (addr2)->addr[1]) && \ ++ ((addr1)->addr[2] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[2] & PP_HTONL(0xff000000UL))) ++ + /* Exact-host comparison *after* ip6_addr_netcmp() succeeded, for efficiency. */ + #define ip6_addr_nethostcmp(addr1, addr2) (((addr1)->addr[2] == (addr2)->addr[2]) && \ + ((addr1)->addr[3] == (addr2)->addr[3])) diff --git a/include/Xcode-Bridging-Header.h b/include/Xcode-Bridging-Header.h index 482033a..1317ec1 100644 --- a/include/Xcode-Bridging-Header.h +++ b/include/Xcode-Bridging-Header.h @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file diff --git a/include/ZeroTier.h b/include/ZeroTier.h index 216efaa..ee42aed 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -82,9 +69,6 @@ typedef unsigned int socklen_t; extern "C" { #endif -// Custom errno to prevent conflicts with platform's own errno -extern int zts_errno; - typedef uint32_t zts_in_addr_t; typedef uint16_t zts_in_port_t; typedef uint8_t zts_sa_family_t; @@ -230,7 +214,7 @@ struct zts_node_details /** * Whether multipath support is enabled. If true, this node will - * be capable of utilizing multiple physical links simultaneosly + * be capable of utilizing multiple physical links simultaneously * to create higher quality or more robust aggregate links. * * See: https://www.zerotier.com/manual.shtml#2_1_5 @@ -678,7 +662,7 @@ ZT_SOCKET_API int ZTCALL zts_get_address( * @param nodeId Node ID * @return */ -ZT_SOCKET_API void ZTCALL zts_get_6plane_addr( +ZT_SOCKET_API int ZTCALL zts_get_6plane_addr( struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** @@ -690,7 +674,7 @@ ZT_SOCKET_API void ZTCALL zts_get_6plane_addr( * @param nodeId Node ID * @return */ -ZT_SOCKET_API void ZTCALL zts_get_rfc4193_addr( +ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); /** diff --git a/include/ZeroTierConstants.h b/include/ZeroTierConstants.h index 579b2bb..d818dca 100644 --- a/include/ZeroTierConstants.h +++ b/include/ZeroTierConstants.h @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -33,21 +20,32 @@ #ifndef ZEROTIER_CONSTANTS_H #define ZEROTIER_CONSTANTS_H +#ifdef __cplusplus +extern "C" { +#endif + +// Custom errno to prevent conflicts with platform's own errno +extern int zts_errno; + +#ifdef __cplusplus +} +#endif + ////////////////////////////////////////////////////////////////////////////// // Control API error codes // ////////////////////////////////////////////////////////////////////////////// -// Everything is ok +// No error. #define ZTS_ERR_OK 0 -// A argument provided by the user application is invalid (e.g. out of range, NULL, etc) +// A argument provided is invalid (e.g. out of range, NULL, etc) #define ZTS_ERR_INVALID_ARG -1 -// The service isn't initialized or is for some reason currently unavailable. Try again. +// The service isn't initialized or is currently unavailable. Try again. #define ZTS_ERR_SERVICE -2 -// For some reason this API operation is not permitted or doesn't make sense at this time. +// This API operation is not permitted or doesn't make sense at this time. #define ZTS_ERR_INVALID_OP -3 -// The call succeeded, but no object or relevant result was available +// The call succeeded, but no object or relevant result was available. #define ZTS_ERR_NO_RESULT -4 -// General internal failure +// General internal failure. Consider filing a bug report. #define ZTS_ERR_GENERAL -5 /** diff --git a/ports/Homebrew/libzt.rb b/ports/Homebrew/libzt.rb index 68cf0cf..13b2cd7 100644 --- a/ports/Homebrew/libzt.rb +++ b/ports/Homebrew/libzt.rb @@ -31,7 +31,7 @@ class Libzt < Formula system "cmake", ".", *std_cmake_args system "cmake", "--build", "." system "make", "install" - cp "LICENSE.GPL-3", "#{prefix}/LICENSE" + cp "LICENSE.txt", "#{prefix}/LICENSE" end def caveats diff --git a/src/Controls.cpp b/src/Controls.cpp index c7d4a8a..c7c5ae9 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -142,6 +129,7 @@ void postEvent(int eventCode, void *arg) } if (ADDR_EVENT_TYPE(eventCode)) { msg->addr = (struct zts_addr_details*)arg; } + _callbackMsgQueue.enqueue(msg); } @@ -514,7 +502,7 @@ int zts_deorbit(uint64_t moonWorldId) #endif int zts_start( - const char *path, userCallbackFunc callback, int port) + const char *path, void (*callback)(struct zts_callback_msg*), int port) { Mutex::Lock _l(_service_lock); lwip_driver_init(); @@ -726,6 +714,24 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( } #endif +int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +{ + if (!addr || !nwid || !nodeId) { + return ZTS_ERR_INVALID_ARG; + } + InetAddress _6planeAddr = InetAddress::makeIpv66plane(nwid,nodeId); + memcpy(addr, _6planeAddr.rawIpData(), sizeof(struct sockaddr_storage)); +} + +int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +{ + if (!addr || !nwid || !nodeId) { + return ZTS_ERR_INVALID_ARG; + } + InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(nwid,nodeId); + memcpy(addr, _rfc4193Addr.rawIpData(), sizeof(struct sockaddr_storage)); +} + ////////////////////////////////////////////////////////////////////////////// // Peers // ////////////////////////////////////////////////////////////////////////////// diff --git a/src/Controls.hpp b/src/Controls.hpp index 2981546..06b464d 100644 --- a/src/Controls.hpp +++ b/src/Controls.hpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file diff --git a/src/Debug.hpp b/src/Debug.hpp index 8651888..fc35225 100644 --- a/src/Debug.hpp +++ b/src/Debug.hpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file diff --git a/src/Intercept.cpp b/src/Intercept.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/Intercept.hpp b/src/Intercept.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/Options.h b/src/Options.h index 3b7970f..f4ca86e 100644 --- a/src/Options.h +++ b/src/Options.h @@ -1,3 +1,16 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-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 LIBZT_OPTIONS_H #define LIBZT_OPTIONS_H diff --git a/src/Service.cpp b/src/Service.cpp index effb198..24f14da 100644 --- a/src/Service.cpp +++ b/src/Service.cpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. */ +/****/ #include #include @@ -868,10 +855,10 @@ public: postEvent(ZTS_EVENT_NETWORK_REQUESTING_CONFIG, (void*)prepare_network_details_msg(nwid)); break; case ZT_NETWORK_STATUS_OK: - if (tap->hasIpv4Addr() && lwip_is_netif_up(tap->netif)) { + if (tap->hasIpv4Addr() && lwip_is_netif_up(tap->netif4)) { postEvent(ZTS_EVENT_NETWORK_READY_IP4, (void*)prepare_network_details_msg(nwid)); } - if (tap->hasIpv6Addr() && lwip_is_netif_up(tap->netif)) { + if (tap->hasIpv6Addr() && lwip_is_netif_up(tap->netif6)) { postEvent(ZTS_EVENT_NETWORK_READY_IP6, (void*)prepare_network_details_msg(nwid)); } // In addition to the READY messages, send one OK message diff --git a/src/Service.hpp b/src/Service.hpp index d977c5e..cc44b61 100644 --- a/src/Service.hpp +++ b/src/Service.hpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. */ +/****/ #ifndef ZT_ONESERVICE_HPP #define ZT_ONESERVICE_HPP diff --git a/src/Sockets.cpp b/src/Sockets.cpp index 844c90a..b64323f 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -56,8 +43,6 @@ extern bool _run_lwip_tcpip; extern "C" { #endif -extern int zts_errno; - #ifdef SDK_JNI void ss2zta(JNIEnv *env, struct sockaddr_storage *ss, jobject addr); void zta2ss(JNIEnv *env, struct sockaddr_storage *ss, jobject addr); @@ -74,7 +59,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_socket( JNIEnv *env, jobject thisObj, jint family, jint type, jint protocol) { int retval = zts_socket(family, type, protocol); - return retval > -1 ? retval : -(zts_errno); // Encode lwip errno in return value for JNI functions only + return retval > -1 ? retval : -(zts_errno); // Encode lwIP errno into return value for JNI functions only } #endif diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index 190b599..b4272d9 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -96,8 +83,10 @@ VirtualTap::~VirtualTap() ::write(_shutdownSignalPipe[1],"\0",1); #endif _phy.whack(); - lwip_remove_netif(netif); - netif = NULL; + lwip_remove_netif(netif4); + netif4 = NULL; + lwip_remove_netif(netif6); + netif6 = NULL; Thread::join(_thread); #ifndef _WIN32 ::close(_shutdownSignalPipe[0]); @@ -144,7 +133,7 @@ bool VirtualTap::hasIpv6Addr() bool VirtualTap::addIp(const InetAddress &ip) { - //char ipbuf[128]; + char ipbuf[128]; //ip.toString(ipbuf); //DEBUG_INFO("addr=%s", ipbuf); @@ -152,9 +141,13 @@ bool VirtualTap::addIp(const InetAddress &ip) This limitation can be removed if some changes are made in the netif driver. */ if (ip.isV4() && hasIpv4Addr()) { + ip.toString(ipbuf); + DEBUG_INFO("failed to add IP (%s), only one per type per netif allowed\n", ipbuf); return false; } if (ip.isV6() && hasIpv6Addr()) { + ip.toString(ipbuf); + DEBUG_INFO("failed to add IP (%s), only one per type per netif allowed\n", ipbuf); return false; } diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp index a66b3ce..3703e96 100644 --- a/src/VirtualTap.hpp +++ b/src/VirtualTap.hpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -169,7 +156,8 @@ public: // Lower-level lwIP netif handling and traffic handling readiness // ////////////////////////////////////////////////////////////////////////////// - void *netif = NULL; + void *netif4 = NULL; + void *netif6 = NULL; /** * The last time that this virtual tap received a network config update from the core diff --git a/src/lwipDriver.cpp b/src/lwipDriver.cpp index db8dffe..454b602 100644 --- a/src/lwipDriver.cpp +++ b/src/lwipDriver.cpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file @@ -76,6 +63,7 @@ namespace ZeroTier { bool _has_exited = false; int hibernationDelayMultiplier = 1; +int netifCount = 0; extern bool _run_lwip_tcpip; Mutex lwip_driver_m; @@ -178,6 +166,9 @@ void lwip_driver_shutdown() void lwip_remove_netif(void *netif) { + if (!netif) { + return; + } struct netif *n = (struct netif*)netif; LOCK_TCPIP_CORE(); netif_remove(n); @@ -188,6 +179,9 @@ void lwip_remove_netif(void *netif) err_t lwip_eth_tx(struct netif *n, struct pbuf *p) { + if (!n) { + return ERR_IF; + } struct pbuf *q; char buf[ZT_MAX_MTU+32]; char *bufptr; @@ -212,7 +206,6 @@ err_t lwip_eth_tx(struct netif *n, struct pbuf *p) int len = totalLength - sizeof(struct eth_hdr); int proto = Utils::ntoh((uint16_t)ethhdr->type); tap->_handler(tap->_arg, NULL, tap->_nwid, src_mac, dest_mac, proto, 0, data, len); -/* if (ZT_MSG_TRANSFER == true) { char flagbuf[32]; memset(&flagbuf, 0, 32); @@ -223,10 +216,11 @@ err_t lwip_eth_tx(struct netif *n, struct pbuf *p) MAC mac; mac.setTo(ethhdr->dest.addr, 6); mac.toAddress(tap->_nwid).toString(nodeBuf); - DEBUG_TRANS("len=%5d dst=%s [%s TX <-- %s] proto=0x%04x %s", totalLength, macBuf, nodeBuf, tap->nodeId().c_str(), + /* + 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; } @@ -244,7 +238,7 @@ void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int e from.copyTo(ethhdr.src.addr, 6); to.copyTo(ethhdr.dest.addr, 6); ethhdr.type = Utils::hton((uint16_t)etherType); -/* + if (ZT_MSG_TRANSFER == true) { char flagbuf[32]; memset(&flagbuf, 0, 32); @@ -255,10 +249,12 @@ void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int e MAC mac; mac.setTo(ethhdr.src.addr, 6); mac.toAddress(tap->_nwid).toString(nodeBuf); - DEBUG_TRANS("len=%5d dst=%s [%s RX --> %s] proto=0x%04x %s", len, macBuf, nodeBuf, tap->nodeId().c_str(), + /* + 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, len+sizeof(struct eth_hdr), PBUF_RAM); if (!p) { DEBUG_ERROR("dropped packet: unable to allocate memory for pbuf"); @@ -285,9 +281,18 @@ void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int e } // Feed packet into stack int err; - if ((err = ((struct netif *)tap->netif)->input(p, (struct netif *)tap->netif)) != ERR_OK) { - DEBUG_ERROR("packet input error (%d)", err); - pbuf_free(p); + + if (Utils::ntoh(ethhdr.type) == 0x800 || Utils::ntoh(ethhdr.type) == 0x806) { + if ((err = ((struct netif *)tap->netif4)->input(p, (struct netif *)tap->netif4)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } + } + if (Utils::ntoh(ethhdr.type) == 0x86DD) { + if ((err = ((struct netif *)tap->netif6)->input(p, (struct netif *)tap->netif6)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } } } @@ -315,6 +320,9 @@ static void print_netif_info(struct netif *n) { bool lwip_is_netif_up(void *n) { + if (!n) { + return false; + } LOCK_TCPIP_CORE(); bool result = netif_is_up((struct netif*)n); UNLOCK_TCPIP_CORE(); @@ -328,7 +336,7 @@ bool lwip_is_netif_up(void *n) static void netif_remove_callback(struct netif *n) { // Called from core, no need to lock - if (!n->state) { + if (!n || !n->state) { return; } VirtualTap *tap = (VirtualTap *)n->state; @@ -349,7 +357,7 @@ static void netif_remove_callback(struct netif *n) static void netif_link_callback(struct netif *n) { // Called from core, no need to lock - if (!n->state) { + if (!n || !n->state) { return; } VirtualTap *tap = (VirtualTap *)n->state; @@ -374,6 +382,9 @@ static void netif_link_callback(struct netif *n) void lwip_set_callbacks(struct netif *n) { + if (!n) { + return; + } #if LWIP_NETIF_STATUS_CALLBACK // Not currently used netif_set_status_callback(n, netif_status_callback); @@ -388,13 +399,13 @@ void lwip_set_callbacks(struct netif *n) 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 *ifd = new zts_netif_details; - // nwid ifd->nwid = tap->_nwid; - // mtu ifd->mtu = n->mtu; - // MAC memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); ifd->mac = htonll(ifd->mac) >> 16; return ifd; @@ -402,10 +413,13 @@ static struct zts_netif_details *lwip_prepare_netif_status_msg(struct netif *n) static err_t netif_init(struct netif *n) { + if (!n || !n->state) { + return ERR_IF; + } // Called from netif code, no need to lock n->hwaddr_len = 6; - n->name[0] = 'z'; - n->name[1] = '4'; + n->name[0] = '4'; + n->name[1] = 'a'+netifCount; n->linkoutput = lwip_eth_tx; n->output = etharp_output; n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; @@ -422,6 +436,31 @@ static err_t netif_init(struct netif *n) return ERR_OK; } +static err_t netif_init6(struct netif *n) +{ + if (!n || !n->state) { + return ERR_IF; + } + n->hwaddr_len = sizeof(n->hwaddr); + VirtualTap *tap = (VirtualTap*)(n->state); + tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); + // Called from netif code, no need to lock + n->hwaddr_len = 6; + n->name[0] = '6'; + n->name[1] = 'a'+netifCount; + n->linkoutput = lwip_eth_tx; + n->output_ip6 = ethip6_output; + n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; + n->flags = NETIF_FLAG_BROADCAST + | NETIF_FLAG_ETHARP + | NETIF_FLAG_ETHERNET + | NETIF_FLAG_IGMP + | NETIF_FLAG_MLD6 + | NETIF_FLAG_LINK_UP + | NETIF_FLAG_UP; + return ERR_OK; +} + void lwip_init_interface(void *tapref, const MAC &mac, const InetAddress &ip) { char ipbuf[INET6_ADDRSTRLEN]; @@ -429,52 +468,64 @@ void lwip_init_interface(void *tapref, const MAC &mac, const InetAddress &ip) VirtualTap *vtap = (VirtualTap*)tapref; struct netif *n = NULL; - if (vtap->netif) { - n = (struct netif*)vtap->netif; - } - else { - n = new struct netif; - } + bool isNewNetif = false; if (ip.isV4()) { + if (vtap->netif4) { + n = (struct netif*)vtap->netif4; + } + else { + n = new struct netif; + isNewNetif = true; + netifCount++; + } char nmbuf[INET6_ADDRSTRLEN]; - static ip4_addr_t ipaddr, netmask, gw; + static ip4_addr_t ip4, netmask, gw; IP4_ADDR(&gw,127,0,0,1); - ipaddr.addr = *((u32_t *)ip.rawIpData()); + ip4.addr = *((u32_t *)ip.rawIpData()); netmask.addr = *((u32_t *)ip.netmask().rawIpData()); LOCK_TCPIP_CORE(); - netif_add(n, &ipaddr, &netmask, &gw, tapref, netif_init, tcpip_input); + netif_add(n, &ip4, &netmask, &gw, (void*)vtap, netif_init, tcpip_input); + vtap->netif4 = (void*)n; postEvent(ZTS_EVENT_NETIF_UP, (void*)lwip_prepare_netif_status_msg(n)); UNLOCK_TCPIP_CORE(); -/* snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, nm=%s]",n, - macbuf, ip.toString(ipbuf), ip.netmask().toString(nmbuf)); -*/ - vtap->netif = (void*)n; + 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()) { - static ip6_addr_t ipaddr; - memcpy(&(ipaddr.addr), ip.rawIpData(), sizeof(ipaddr.addr)); - n->ip6_autoconfig_enabled = 1; - + if (vtap->netif6) { + n = (struct netif*)vtap->netif6; + } + else { + n = new struct netif; + isNewNetif = true; + netifCount++; + } + static ip6_addr_t ip6; + memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr)); LOCK_TCPIP_CORE(); - netif_ip6_addr_set(n, 1, &ipaddr); - netif_create_ip6_linklocal_address(n, 1); - netif_ip6_addr_set_state(n, 0, IP6_ADDR_TENTATIVE); - netif_ip6_addr_set_state(n, 1, IP6_ADDR_TENTATIVE); + if (isNewNetif) { + vtap->netif6 = (void*)n; + netif_add(n, NULL, NULL, NULL, (void*)vtap, netif_init6, ethernet_input); + n->ip6_autoconfig_enabled = 1; + vtap->_mac.copyTo(n->hwaddr, n->hwaddr_len); + netif_create_ip6_linklocal_address(n, 1); + netif_set_link_up(n); + netif_set_up(n); + netif_set_default(n); + } + netif_add_ip6_address(n,&ip6,NULL); n->output_ip6 = ethip6_output; - postEvent(ZTS_EVENT_NETIF_UP, (void*)lwip_prepare_netif_status_msg(n)); UNLOCK_TCPIP_CORE(); -/* + postEvent(ZTS_EVENT_NETIF_UP, (void*)lwip_prepare_netif_status_msg(n)); snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s]", n, - macbuf, ip.toString(ipbuf)); -*/ + DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n, + macbuf, ip.toString(ipbuf), vtap); } } diff --git a/src/lwipDriver.hpp b/src/lwipDriver.hpp index 4b7dffe..7467c7e 100644 --- a/src/lwipDriver.hpp +++ b/src/lwipDriver.hpp @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * 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. */ +/****/ /** * @file diff --git a/src/lwipRawDriver.cpp b/src/lwipRawDriver.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/lwipRawDriver.hpp b/src/lwipRawDriver.hpp deleted file mode 100644 index e69de29..0000000 From 2b60ab27ea2e73f332e6408983ac48dedc4a6568 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 15 Apr 2020 16:08:40 -0700 Subject: [PATCH 35/78] Add network and peer caching toggles to API, fix 6PLANE and RFC4193 address computation --- include/ZeroTier.h | 30 ++++++++++++++++++++++++++++ src/Controls.cpp | 19 ++++++++++++++++-- src/Options.h | 20 ------------------- src/Service.cpp | 50 ++++++++++++++++++++++------------------------ 4 files changed, 71 insertions(+), 48 deletions(-) diff --git a/include/ZeroTier.h b/include/ZeroTier.h index ee42aed..bf92169 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -466,6 +466,36 @@ struct zts_peer_list extern "C" { #endif +/** + * @brief Enable or disable whether the service will cache network details (enabled by default) + * + * This can potentially shorten (startup) times. This allows the service to nearly instantly + * inform the network stack of an address to use for this peer so that it can + * create an interface. This can be disabled for cases where one may not want network + * config details to be written to storage. This is especially useful for situations where + * address assignments do not change often. + * + * @usage Should be called before zts_start() if you intend on changing its state. + * + * @param enabled Whether or not this feature is enabled + */ +ZT_SOCKET_API void ZTCALL zts_set_network_caching(bool enabled); + +/** + * @brief Enable or disable whether the service will cache peer details (enabled by default) + * + * This can potentially shorten (connection) times. This allows the service to + * re-use previously discovered paths to a peer, this prevents the service from having + * to go through the entire transport-triggered link provisioning process. This is especially + * useful for situations where paths to peers do not change often. This is enabled by default + * and can be disabled for cases where one may not want peer details to be written to storage. + * + * @usage Should be called before zts_start() if you intend on changing its state. + * + * @param enabled Whether or not this feature is enabled + */ +ZT_SOCKET_API void ZTCALL zts_set_peer_caching(bool enabled); + /** * @brief Starts the ZeroTier service and notifies user application of events via callback * diff --git a/src/Controls.cpp b/src/Controls.cpp index c7c5ae9..cc466b3 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -79,6 +79,9 @@ bool _run_service = false; bool _run_callbacks = false; bool _run_lwip_tcpip = false; +bool _network_caching_enabled = true; +bool _peer_caching_enabled = true; + //bool _startupError = false; // Global reference to ZeroTier service @@ -501,6 +504,16 @@ int zts_deorbit(uint64_t moonWorldId) #ifdef SDK_JNI #endif +void zts_set_network_caching(bool enabled) +{ + _network_caching_enabled = enabled; +} + +void zts_set_peer_caching(bool enabled) +{ + _peer_caching_enabled = enabled; +} + int zts_start( const char *path, void (*callback)(struct zts_callback_msg*), int port) { @@ -720,7 +733,8 @@ int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, cons return ZTS_ERR_INVALID_ARG; } InetAddress _6planeAddr = InetAddress::makeIpv66plane(nwid,nodeId); - memcpy(addr, _6planeAddr.rawIpData(), sizeof(struct sockaddr_storage)); + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; + memcpy(in6->sin6_addr.s6_addr, _6planeAddr.rawIpData(), sizeof(struct in6_addr)); } int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) @@ -729,7 +743,8 @@ int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, con return ZTS_ERR_INVALID_ARG; } InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(nwid,nodeId); - memcpy(addr, _rfc4193Addr.rawIpData(), sizeof(struct sockaddr_storage)); + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; + memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr)); } ////////////////////////////////////////////////////////////////////////////// diff --git a/src/Options.h b/src/Options.h index f4ca86e..74708dd 100644 --- a/src/Options.h +++ b/src/Options.h @@ -77,24 +77,4 @@ */ #define ZTS_LWIP_MAX_RX_QUEUE_LEN 1024 -////////////////////////////////////////////////////////////////////////////// -// Service behaviour // -////////////////////////////////////////////////////////////////////////////// - -/** - * Whether the service will cache peer details (such as known paths). This will - * make startup and reachability time shorter but is generally only effective - * for networks with a somewhat static topology. In other words this would not be - * recommended for use on mobile devices. - */ -#define PEER_CACHING 0 - -/** - * Whether the service will cache network details. This will shorten startup - * times. This allows the service to nearly instantly inform the network stack - * of an address to use for this peer so that it can create an interface. This - * is only recommended for networks whose IP assignments do not change often. - */ -#define NETWORK_CACHING 1 - #endif \ No newline at end of file diff --git a/src/Service.cpp b/src/Service.cpp index 24f14da..b3bc011 100644 --- a/src/Service.cpp +++ b/src/Service.cpp @@ -90,6 +90,8 @@ namespace ZeroTier { typedef VirtualTap EthernetTap; } namespace ZeroTier { extern void postEvent(uint64_t id, int eventCode); +extern bool _network_caching_enabled; +extern bool _peer_caching_enabled; namespace { @@ -422,10 +424,8 @@ public: } } #endif - -#if NETWORK_CACHING // Join existing networks in networks.d - { + if (_network_caching_enabled) { std::vector networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str())); for(std::vector::iterator f(networksDotD.begin());f!=networksDotD.end();++f) { std::size_t dot = f->find_last_of('.'); @@ -433,8 +433,6 @@ public: _node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0); } } -#endif - // Main I/O loop _nextBackgroundTaskDeadline = 0; int64_t clockShouldBe = OSUtils::now(); @@ -773,13 +771,13 @@ public: *nuptr = (void *)0; delete n.tap; _nets.erase(nwid); -#if NETWORK_CACHING - if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) { - char nlcpath[256]; - OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid); - OSUtils::rm(nlcpath); + if (_network_caching_enabled) { + if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) { + char nlcpath[256]; + OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid); + OSUtils::rm(nlcpath); + } } -#endif } else { _nets.erase(nwid); } @@ -970,19 +968,19 @@ public: case ZT_STATE_OBJECT_PLANET: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; -#if NETWORK_CACHING case ZT_STATE_OBJECT_NETWORK_CONFIG: - OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str()); - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]); - secure = true; + if (_network_caching_enabled) { + OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str()); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]); + secure = true; + } break; -#endif -#if PEER_CACHING case ZT_STATE_OBJECT_PEER: - OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str()); - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]); + if (_peer_caching_enabled) { + OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str()); + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]); + } break; -#endif default: return; } @@ -1032,16 +1030,16 @@ public: case ZT_STATE_OBJECT_PLANET: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; -#if NETWORK_CACHING case ZT_STATE_OBJECT_NETWORK_CONFIG: - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]); + if (_network_caching_enabled) { + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]); + } break; -#endif -#if PEER_CACHING case ZT_STATE_OBJECT_PEER: - OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",_homePath.c_str(),(unsigned long long)id[0]); + if (_peer_caching_enabled) { + OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",_homePath.c_str(),(unsigned long long)id[0]); + } break; -#endif default: return -1; } From 41c8f019a0182ac84f7f9be6ae2e7d9dda06cd88 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 16 Apr 2020 12:26:50 -0700 Subject: [PATCH 36/78] Remove old WindowsCppApp example --- .../ExampleWindowsCppApp.sln | 31 ---- .../ExampleWindowsCppApp.cpp | Bin 6782 -> 0 bytes .../ExampleWindowsCppApp.vcxproj | 174 ------------------ .../ExampleWindowsCppApp.vcxproj.filters | 33 ---- .../ExampleWindowsCppApp/stdafx.cpp | Bin 616 -> 0 bytes .../ExampleWindowsCppApp/stdafx.h | Bin 642 -> 0 bytes .../ExampleWindowsCppApp/targetver.h | Bin 630 -> 0 bytes 7 files changed, 238 deletions(-) delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp.sln delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.cpp delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj.filters delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.cpp delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.h delete mode 100644 examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/targetver.h diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp.sln b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp.sln deleted file mode 100644 index 429a747..0000000 --- a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExampleWindowsCppApp", "ExampleWindowsCppApp\ExampleWindowsCppApp.vcxproj", "{D6BC8888-0498-4BDB-AFAF-21090A39082A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Debug|x64.ActiveCfg = Debug|x64 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Debug|x64.Build.0 = Debug|x64 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Debug|x86.ActiveCfg = Debug|Win32 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Debug|x86.Build.0 = Debug|Win32 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Release|x64.ActiveCfg = Release|x64 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Release|x64.Build.0 = Release|x64 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Release|x86.ActiveCfg = Release|Win32 - {D6BC8888-0498-4BDB-AFAF-21090A39082A}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {68E97C56-8BB2-44E4-8536-069924BC9707} - EndGlobalSection -EndGlobal diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.cpp b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.cpp deleted file mode 100644 index dcd37ba26d38187d0d5c682bca9f2612d366a926..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6782 zcmbuEeQy&-5XSf4RO)xQrUE&r32zmZc&P+w2vNg}uxUyuvK-rS+&V9f&xC;Z>TRFj z&W-oZJ|_WWk-OWSot=5+*_pZHzkjcXYvJ4Qjs9lgkI)Y%;V87iFy3XM8GZ@BYGzgc zo@p!(rbF|l{iLyZ%YM+&?XVL)z1O4qtE-Oa&^xW4klgv@r(_W{VXY8)V&NMdBwW~3FXS^3z z=DHt-UCrik^-$L=O7&y|NM_pQC`#{1kK1E%6KU&d9a4Ejkt@#!(N-);Gno2yL*Df=T`}8!kTe8P@f2_R?hvp0pS$G^#z~aOM z;?&oUwjMu>6=RJVPi||h>POW=G=P&M(}>y~b2P#w=N$|p24}jCqNJf$-F59VqhA+G z5E{m4J2v~#5-?>143OaOsjj0Ki`|I%M40y#yNSkrn0jU`$-<79-3m{_b49bI)ejZd z*ShZL?xlV_w={CE(9CY|kDdAvUpVMi4aJpX?F0X@*?0@P4e2!V}_Vn(OkBA-5IJ{h`+7;tyUJlJ~XN@?bR!4y1n~48UffyaO9j z4j60X=Q)H{m4WLq^DJjv2JMmlkw=VS`=ewQum#(6_+RrbI`=HcILl`$+mr%?B>=Q#dlw67I@I`QRD z6?!~HiTnlfT}h4#F9z}ixv|FP>ua3_&Q8xYmKEI#i%W`m)gSBy6-UT2zM*D0{8YAp z?y-IrjZ{Va^jg(M$M?~8_MSw&S-2lP!KNc|d0&h^PmZb#k1j8{SRa}=b!W1iI3%5J z0aY6B_u{Ucl16C9JpU}*R-d!7(AJ%}qA8!cis-G9?(-19o@7OxWK#%@Vl6n<4rN(b z3n$%nxK>7)&Vp)d*X!}Fyq=lrim{L($G`}@VEy?kOm|Nm$oX<~V8`N&S#hym`YEz% zt_YMhlaFAI)xmntmy2~BNxhG&mm@~?(#A$-nQ^CHa#`roA$}-ONm`9p^oMp*8xd&v|XI%B8dLMwXL7ITHh&2CmeRc8n-h@U1L-BMI_O zuXx(kJ+VTH447+terN2Yk~Z`%ku3UDSZ49RtInM}j}di-j6?MW8%Oeu-bLO{y;`)^ ztu&3Z8g7*|cvR;-@m0Az8&0G3P8i(Q`s9aeI4vx_5qSUuu-l$Mx;o;gZ%^pKn~w1B z3iaO9x5T6q@o}O#b}rja1>?`z8j%M)kPRUZAaemqSnl#v5Vuay9Cj`I7vUDD6w~4dCK4m?7k>jwDdJm=#)#0|) z(bDm@B)!-M_runiZXat)FK;Hc+R)%#mA7ktbSN8odt)EopMTfBPt`MT>poLA{ao+j z8`GRKf4+94AJ(%g-K6)#d75ti*}5ebKQJ2#-o=m zdL*=c-e!2?ZWmE+h{dpl_tb6uxW6gQDH^jG$aOskSEBa!#c5|{Lpu^*@(1?QrKeun z?^dSvq+Q6h|f{rK6KW`R1p-I}DS4jt=swxT_-$9EgVbJNx?CIO@A1d_H{ z@3*wtzCwJB_sJgL$+$=%amgW(=sd#^B#Oe64*!?L-*T1sx4FZO3D0`40H{}b~*xL_0{v1zz2Re?! zBCpZ4@|A>aN3OPtWD=h1QhiGZxMYfIstrheMDzDKO@yJ2i(*HQ}`c!s6Y__ diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj deleted file mode 100644 index 5581f98..0000000 --- a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj +++ /dev/null @@ -1,174 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {D6BC8888-0498-4BDB-AFAF-21090A39082A} - Win32Proj - ExampleWindowsCppApp - 10.0.16299.0 - - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - Use - Level3 - Disabled - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - C:\libzt\include;%(AdditionalIncludeDirectories) - - - Console - true - C:\libzt\bin\lib\Debug;%(AdditionalLibraryDirectories) - ws2_32.lib;iphlpapi.lib;Shlwapi.lib;zt-static.lib;%(AdditionalDependencies) - - - - - Use - Level3 - Disabled - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - - - - - Use - Level3 - MaxSpeed - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - C:\libzt\include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - C:\libzt\bin\lib\Release;%(AdditionalLibraryDirectories) - zt-static.lib;ws2_32.lib;shlwapi.lib;iphlpapi.lib;%(AdditionalDependencies) - - - - - Use - Level3 - MaxSpeed - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - C:\libzt\include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - zt-static.lib;ws2_32.lib;iphlpapi.lib;Shlwapi.lib;%(AdditionalDependencies) - C:\libzt\examples\cpp\ExampleWindowsCppApp\ExampleWindowsCppApp\Debug;%(AdditionalLibraryDirectories) - - - - - - - - - - Create - Create - Create - Create - - - - - - \ No newline at end of file diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj.filters b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj.filters deleted file mode 100644 index 64d4196..0000000 --- a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/ExampleWindowsCppApp.vcxproj.filters +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.cpp b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.cpp deleted file mode 100644 index d090f4677a40d5cbb340dc28006bc0b61edb71a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 616 zcmZ`%L2kk@5S%ko|6swnaPJL4D{dT$M6WJM6r&KM5LEK{wliyy096&O?Ra-*W_SJb zJ;MwRC$z}mQQ(3pb~y5Lcrj1J84d4p{uKzQ?9`%Ue9OCln%;_089gW7?0FWVqm>q| zKhHREG+h5?Jz+#8((+0T9tSEA)7J1aM0^o*Van#VPPy)g@92@Lil@(Bo75i3mE)OF z(eRRXu}hUVy$uHD`z}PH?eEZqQ(Gmfd8qDxo)#TdNQ*KG<~LNLz6HY)w`^kmp^FaV zhnbYTxkjfxZLr3YrYWh;f~H;55Tp9t*`Be|&~tk0)NKbevyOVs+-v6M{e&Feu_4PG cZ}{NLSlx6h%~KhX*r~$QlVGpl`c|a+7nyfzLI3~& diff --git a/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.h b/examples/cpp/ExampleWindowsCppApp/ExampleWindowsCppApp/stdafx.h deleted file mode 100644 index 94d4ed877dbfd8cd2c783931c444cad664143b10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 642 zcmaJ<%Z`FT5UjJw{sYnLM(@V#$-9^R0UQRwu#ECB^7Cp{4I>V}Q z8vEtLe;CsQ`f9DHxFP?Ev!}!l=AXA(tbaPXB)?e2)ZxZ=WBxB^dUob-#=8i1_Gje& z#NH7xP7*QWxk;u;fEfw0WxN;+co0fK2Vs4B9U*sm0{`t1wOo&Foy0~*EXI^K{ z&F{}p2USW{Xp2p>*G`#oJ!s%(q!H-M(Ty4fmG}kNtEQTB%)V1m=}BwwfWPvrT#@e@ zH0NG}74Ao{glS)#QXA|NYdIfY7hrMp+Ji@H`t9kzWx_SD6;~Ri;cvXH)6CO{-I1p_IJPQ#X=r zigZeSqQguJz35q;zt9^Q_C^^>*mmuXUCp&pw^fPoGX+dho4RCrySs7j@9_UipWkA5 OQDt4mH~x-^Z~X_?9czaG From d7be0b7052308ec64b1b50fe20b817545bfc2ad2 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 10:32:43 -0700 Subject: [PATCH 37/78] Remove outdated examples --- examples/cpp/earthtest.cpp | 39 - examples/cpp/ipv4simple/ipv4client.cpp | 96 - examples/cpp/ipv4simple/ipv4client_udp.cpp | 109 - examples/cpp/ipv4simple/ipv4server.cpp | 119 - examples/cpp/ipv4simple/ipv4server_udp.cpp | 107 - examples/cpp/ipv6adhoc/ipv6adhocclient.cpp | 114 - examples/cpp/ipv6adhoc/ipv6adhocserver.cpp | 127 - examples/cpp/ipv6simple/ipv6client.cpp | 96 - examples/cpp/ipv6simple/ipv6server.cpp | 106 - examples/cpp/sharedlib/ipv4client_shared.cpp | 215 -- examples/cpp/sharedlib/ipv4server_shared.cpp | 222 -- test/example.cpp | 280 -- test/selftest.cpp | 3411 ------------------ test/simple.cpp | 92 - 14 files changed, 5133 deletions(-) delete mode 100644 examples/cpp/earthtest.cpp delete mode 100644 examples/cpp/ipv4simple/ipv4client.cpp delete mode 100644 examples/cpp/ipv4simple/ipv4client_udp.cpp delete mode 100644 examples/cpp/ipv4simple/ipv4server.cpp delete mode 100644 examples/cpp/ipv4simple/ipv4server_udp.cpp delete mode 100644 examples/cpp/ipv6adhoc/ipv6adhocclient.cpp delete mode 100644 examples/cpp/ipv6adhoc/ipv6adhocserver.cpp delete mode 100644 examples/cpp/ipv6simple/ipv6client.cpp delete mode 100644 examples/cpp/ipv6simple/ipv6server.cpp delete mode 100644 examples/cpp/sharedlib/ipv4client_shared.cpp delete mode 100644 examples/cpp/sharedlib/ipv4server_shared.cpp delete mode 100644 test/example.cpp delete mode 100644 test/selftest.cpp delete mode 100644 test/simple.cpp diff --git a/examples/cpp/earthtest.cpp b/examples/cpp/earthtest.cpp deleted file mode 100644 index df51df9..0000000 --- a/examples/cpp/earthtest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include - -#include "libzt.h" - -int main(int argc, char **argv) -{ - uint64_t nwid = 0x8056c2e21c000001; - zts_startjoin(".", nwid); - fprintf(stderr, "%llx", (unsigned long long)zts_get_node_id()); - zts_leave(nwid); - zts_stop(); - return 0; -} \ No newline at end of file diff --git a/examples/cpp/ipv4simple/ipv4client.cpp b/examples/cpp/ipv4simple/ipv4client.cpp deleted file mode 100644 index 9d0100b..0000000 --- a/examples/cpp/ipv4simple/ipv4client.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -char *msg = (char*)"welcome to the machine"; - -int main(int argc, char **argv) -{ - if (argc != 5) { - printf("\nlibzt example client\n"); - printf("client [config_file_path] [nwid] [remote_addr] [remote_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - std::string remote_addr = argv[3]; - int remote_port = atoi(argv[4]); - int r=0, w=0, err=0, sockfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4; - in4.sin_port = htons(remote_port); - in4.sin_addr.s_addr = inet_addr(remote_addr.c_str()); - in4.sin_family = AF_INET; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_connect(sockfd, (const struct sockaddr *)&in4, sizeof(in4))) < 0) { - printf("error connecting to remote host (%d)\n", err); - } - - printf("sending to server...\n"); - w = zts_write(sockfd, msg, strlen(msg)); - - printf("reading from server...\n"); - r = zts_read(sockfd, rbuf, strlen(msg)); - - printf("Sent : %s\n", msg); - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv4simple/ipv4client_udp.cpp b/examples/cpp/ipv4simple/ipv4client_udp.cpp deleted file mode 100644 index 273c797..0000000 --- a/examples/cpp/ipv4simple/ipv4client_udp.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -char *msg = (char*)"welcome to the machine"; - -int main(int argc, char **argv) -{ - if (argc != 5) { - printf("\nlibzt example client\n"); - printf("client [config_file_path] [nwid] [remote_addr] [remote_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - std::string remote_addr = argv[3]; - int remote_port = atoi(argv[4]); - int r=0, w=0, err=0, sockfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4; - in4.sin_port = htons(remote_port); - in4.sin_addr.s_addr = INADDR_ANY; - in4.sin_family = AF_INET; - - struct sockaddr_in remote4; - remote4.sin_port = htons(remote_port); - remote4.sin_addr.s_addr = inet_addr(remote_addr.c_str()); - remote4.sin_family = AF_INET; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_bind(sockfd, (struct sockaddr *)&in4, sizeof(struct sockaddr_in)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - int flags = 0; - int len = strlen(msg); - - while(true) { - sleep(1); - if ((err = zts_sendto(sockfd, msg, len, flags, (const struct sockaddr *)&remote4, sizeof(remote4))) < 0) { - printf("error sending message to remote host (%d)\n", err); - } - printf("sent=%d\n", err); - } - /* - printf("reading from server...\n"); - r = zts_read(sockfd, rbuf, strlen(msg)); - - printf("Sent : %s\n", msg); - printf("Received : %s\n", rbuf); - */ - err = zts_close(sockfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv4simple/ipv4server.cpp b/examples/cpp/ipv4simple/ipv4server.cpp deleted file mode 100644 index 260e9e4..0000000 --- a/examples/cpp/ipv4simple/ipv4server.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif -#include "libzt.h" - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [nwid] [bind_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - int bind_port = atoi(argv[3]); - int w=0, r=0, err=0, sockfd, accfd; - char rbuf[1000]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4, acc_in4; - in4.sin_port = htons(bind_port); - in4.sin_addr.s_addr = INADDR_ANY; - in4.sin_family = AF_INET; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_bind(sockfd, (struct sockaddr *)&in4, sizeof(struct sockaddr_in)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - - if ((err = zts_listen(sockfd, 100)) < 0) { - printf("error placing socket in LISTENING state (%d)\n", err); - } - - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((accfd = zts_accept(sockfd, (struct sockaddr *)&acc_in4, &client_addrlen)) < 0) { - printf("error accepting connection (%d)\n", err); - } - - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in4, &peer_addrlen); - printf("accepted connection from %s : %d\n", inet_ntoa(acc_in4.sin_addr), ntohs(acc_in4.sin_port)); - - int tot = 0; - printf("reading from client...\n"); - while(true) { - memset(rbuf, 0, sizeof rbuf); - if (tot == 500000) - { - break; - } - r = zts_read(accfd, rbuf, sizeof rbuf); - printf("r=%d\n", r); - printf("Received : %s\n", rbuf); - tot+= r; - printf("tot=%d\n", tot); - } - printf("tot=%d\n", tot); - - printf("sending to client...\n"); - w = zts_write(accfd, rbuf, strlen(rbuf)); - printf("w=%d\n", r); - - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - err = zts_close(accfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv4simple/ipv4server_udp.cpp b/examples/cpp/ipv4simple/ipv4server_udp.cpp deleted file mode 100644 index 1a7f5eb..0000000 --- a/examples/cpp/ipv4simple/ipv4server_udp.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif -#include "libzt.h" - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [nwid] [bind_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - int bind_port = atoi(argv[3]); - int w=0, r=0, err=0, sockfd = 0, accfd = 0, flags = 0; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4, acc_in4; - in4.sin_port = htons(bind_port); - in4.sin_addr.s_addr = INADDR_ANY; - in4.sin_family = AF_INET; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - if ((err = zts_bind(sockfd, (struct sockaddr *)&in4, sizeof(struct sockaddr_in)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - -/* - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in4, &peer_addrlen); - DEBUG_INFO("accepted connection from %s : %d", inet_ntoa(acc_in4.sin_addr), ntohs(acc_in4.sin_port)); -*/ - - printf("reading from client...\n"); - socklen_t addrlen = sizeof(acc_in4); - memset(&acc_in4, 0, sizeof acc_in4); - - while(true) { - memset(&rbuf, 0, sizeof rbuf); - r = zts_recvfrom(accfd, rbuf, sizeof(rbuf), flags, (struct sockaddr *)&acc_in4, &addrlen); - if (r >= 0) { - char *ip = inet_ntoa(acc_in4.sin_addr); - printf("Received : r=%d, %s -- from: %s : %d\n", r, rbuf, ip, ntohs(acc_in4.sin_port)); - } - } - -/* - printf("sending to client...\n"); - w = zts_write(accfd, rbuf, strlen(rbuf)); - -*/ - - err = zts_close(sockfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv6adhoc/ipv6adhocclient.cpp b/examples/cpp/ipv6adhoc/ipv6adhocclient.cpp deleted file mode 100644 index ab1ee0e..0000000 --- a/examples/cpp/ipv6adhoc/ipv6adhocclient.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -char *msg = (char*)"welcome to the machine"; - -uint64_t generate_adhoc_nwid_from_port(int port) -{ - std::string padding; - if(port < 10) { - padding = "000"; - } else if(port < 100) { - padding = "00"; - } else if(port < 1000) { - padding = "0"; - } - // We will join ad-hoc network ffSSSSEEEE000000 - // Where SSSS = start port - // EEEE = end port - padding = padding + std::to_string(port); // SSSS - std::string nwidstr = "ff" + padding + padding + "000000"; // ff + SSSS + EEEE + 000000 - return strtoull(nwidstr.c_str(), NULL, 16); -} - -int main(int argc, char **argv) -{ - if (argc != 5) { - printf("\nlibzt example client\n"); - printf("client [config_file_path] [nwid] [remote_addr] [remote_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - std::string remote_addr = argv[3]; - int remote_port = atoi(argv[4]); - int r=0, w=0, err=0, sockfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in6 in6; - in6.sin6_port = htons(remote_port); - inet_pton(AF_INET6, remote_addr.c_str(), &(in6.sin6_addr)); - in6.sin6_family = AF_INET6; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = generate_adhoc_nwid_from_port(remote_port); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_connect(sockfd, (const struct sockaddr *)&in6, sizeof(in6))) < 0) { - printf("error connecting to remote host (%d)\n", err); - } - - printf("sending to server...\n"); - w = zts_write(sockfd, msg, strlen(msg)); - - printf("reading from server...\n"); - r = zts_read(sockfd, rbuf, strlen(msg)); - - printf("Sent : %s\n", msg); - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv6adhoc/ipv6adhocserver.cpp b/examples/cpp/ipv6adhoc/ipv6adhocserver.cpp deleted file mode 100644 index 512a568..0000000 --- a/examples/cpp/ipv6adhoc/ipv6adhocserver.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -uint64_t generate_adhoc_nwid_from_port(int port) -{ - std::string padding; - if(port < 10) { - padding = "000"; - } else if(port < 100) { - padding = "00"; - } else if(port < 1000) { - padding = "0"; - } - // We will join ad-hoc network ffSSSSEEEE000000 - // Where SSSS = start port - // EEEE = end port - padding = padding + std::to_string(port); // SSSS - std::string nwidstr = "ff" + padding + padding + "000000"; // ff + SSSS + EEEE + 000000 - return strtoull(nwidstr.c_str(), NULL, 16); -} - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [bind_port]\n"); - exit(0); - } - std::string path = argv[1]; - int bind_port = atoi(argv[2]); - uint64_t nwid = generate_adhoc_nwid_from_port(bind_port); - int w=0, r=0, err=0, sockfd, accfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in6 in6, acc_in6; - in6.sin6_port = htons(bind_port); - in6.sin6_family = AF_INET6; - in6.sin6_addr = in6addr_any; - - fprintf(stderr, "nwid=%llx\n", (unsigned long long)nwid); - exit(-1); - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - nwid = generate_adhoc_nwid_from_port(bind_port); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_bind(sockfd, (struct sockaddr *)&in6, sizeof(struct sockaddr_in6)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - - if ((err = zts_listen(sockfd, 100)) < 0) { - printf("error placing socket in LISTENING state (%d)\n", err); - } - - socklen_t client_addrlen = sizeof(sockaddr_in6); - if ((accfd = zts_accept(sockfd, (struct sockaddr *)&acc_in6, &client_addrlen)) < 0) { - printf("error accepting connection (%d)\n", err); - } - - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in6, &peer_addrlen); - //printf("accepted connection from %s : %d", inet_ntoa(acc_in6.sin6_addr), ntohs(acc_in6.sin6_port)); - - printf("reading from client...\n"); - r = zts_read(accfd, rbuf, sizeof rbuf); - - printf("sending to client...\n"); - w = zts_write(accfd, rbuf, strlen(rbuf)); - - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - err = zts_close(accfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv6simple/ipv6client.cpp b/examples/cpp/ipv6simple/ipv6client.cpp deleted file mode 100644 index 3bf3099..0000000 --- a/examples/cpp/ipv6simple/ipv6client.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -char *msg = (char*)"welcome to the machine"; - -int main(int argc, char **argv) -{ - if (argc != 5) { - printf("\nlibzt example client\n"); - printf("client [config_file_path] [nwid] [remote_addr] [remote_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - std::string remote_addr = argv[3]; - int remote_port = atoi(argv[4]); - int r=0, w=0, err=0, sockfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in6 in6; - in6.sin6_port = htons(remote_port); - inet_pton(AF_INET6, remote_addr.c_str(), &(in6.sin6_addr)); - in6.sin6_family = AF_INET6; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_connect(sockfd, (const struct sockaddr *)&in6, sizeof(in6))) < 0) { - printf("error connecting to remote host (%d)\n", err); - } - - printf("sending to server...\n"); - w = zts_write(sockfd, msg, strlen(msg)); - - printf("reading from server...\n"); - r = zts_read(sockfd, rbuf, strlen(msg)); - - printf("Sent : %s\n", msg); - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/ipv6simple/ipv6server.cpp b/examples/cpp/ipv6simple/ipv6server.cpp deleted file mode 100644 index 7062d4f..0000000 --- a/examples/cpp/ipv6simple/ipv6server.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [nwid] [bind_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - int bind_port = atoi(argv[3]); - int w=0, r=0, err=0, sockfd, accfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in6 in6, acc_in6; - in6.sin6_port = htons(bind_port); - in6.sin6_family = AF_INET6; - in6.sin6_addr = in6addr_any; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_bind(sockfd, (struct sockaddr *)&in6, sizeof(struct sockaddr_in6)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - - if ((err = zts_listen(sockfd, 100)) < 0) { - printf("error placing socket in LISTENING state (%d)\n", err); - } - - socklen_t client_addrlen = sizeof(sockaddr_in6); - if ((accfd = zts_accept(sockfd, (struct sockaddr *)&acc_in6, &client_addrlen)) < 0) { - printf("error accepting connection (%d)\n", err); - } - - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in6, &peer_addrlen); - //DEBUG_INFO("accepted connection from %s : %d", inet_ntoa(acc_in6.sin6_addr), ntohs(acc_in6.sin6_port)); - - printf("reading from client...\n"); - r = zts_read(accfd, rbuf, sizeof rbuf); - - printf("sending to client...\n"); - w = zts_write(accfd, rbuf, strlen(rbuf)); - - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - err = zts_close(accfd); - - return err; -} \ No newline at end of file diff --git a/examples/cpp/sharedlib/ipv4client_shared.cpp b/examples/cpp/sharedlib/ipv4client_shared.cpp deleted file mode 100644 index 83a0782..0000000 --- a/examples/cpp/sharedlib/ipv4client_shared.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#if !defined(_MSC_VER) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libzt.h" - -// function pointers which will have values assigned once the dynamic library is loaded - -int (*_zts_set_service_port)(int portno); -int (*_zts_start)(const char *path, bool blocking); -int (*_zts_startjoin)(const char*, uint64_t); -void (*_zts_stop)(void); -int (*_zts_core_running)(void); -int (*_zts_stack_running)(void); -int (*_zts_ready)(void); -int (*_zts_join)(const uint64_t); -int (*_zts_leave)(const uint64_t); -void (*_zts_get_path)(char *homePath, const size_t len); -uint64_t (*_zts_get_node_id)(void); -int (*_zts_has_address)(const uint64_t); -int (*_zts_get_num_assigned_addresses)(const uint64_t nwid); -int (*_zts_get_address_at_index)(const uint64_t nwid, const int index, struct sockaddr_storage *addr); -int (*_zts_get_address)(const uint64_t nwid, struct sockaddr_storage *addr, const int address_family); -void (*_zts_get_6plane_addr)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); -void (*_zts_get_rfc4193_addr)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); -unsigned long (*_zts_get_peer_count)(void); -int (*_zts_get_peer_address)(char *peer, const uint64_t nodeId); -int (*_zts_socket)(int socket_family, int socket_type, int protocol); -int (*_zts_connect)(int fd, const struct sockaddr *addr, socklen_t addrlen); -int (*_zts_bind)(int fd, const struct sockaddr *addr, socklen_t addrlen); -int (*_zts_listen)(int fd, int backlog); -int (*_zts_accept)(int fd, struct sockaddr *addr, socklen_t *addrlen); -#if defined(__linux__) -int (*_zts_accept4)(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags); -#endif -int (*_zts_setsockopt)(int fd, int level, int optname, const void *optval, socklen_t optlen); -int (*_zts_getsockopt)(int fd, int level, int optname, void *optval, socklen_t *optlen); -int (*_zts_getsockname)(int fd, struct sockaddr *addr, socklen_t *addrlen); -int (*_zts_getpeername)(int fd, struct sockaddr *addr, socklen_t *addrlen); -int (*_zts_gethostname)(char *name, size_t len); -int (*_zts_sethostname)(const char *name, size_t len); -struct hostent *(*_zts_gethostbyname)(const char *name); -int (*_zts_close)(int fd); -int(*_zts_fcntl)(int fd, int cmd, int flags); -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_sendto)(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); -ssize_t (*_zts_sendmsg)(int fd, const struct msghdr *msg, int flags); -ssize_t (*_zts_recv)(int fd, void *buf, size_t len, int flags); -ssize_t (*_zts_recvfrom)(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); -ssize_t (*_zts_recvmsg)(int fd, struct msghdr *msg,int flags); -int (*_zts_read)(int fd, void *buf, size_t len); -int (*_zts_write)(int fd, const void *buf, size_t len); -int (*_zts_shutdown)(int fd, int how); -int (*_zts_add_dns_nameserver)(struct sockaddr *addr); -int (*_zts_del_dns_nameserver)(struct sockaddr *addr); - - -void load_library_symbols(char *library_path) -{ - void *libHandle = dlopen(library_path, RTLD_LAZY); - if (libHandle == NULL) { - DEBUG_ERROR("unable to load dynamic libs: %s", dlerror()); - exit(-1); - } - - // Load symbols from library (call these directly) - - _zts_set_service_port = (int(*)(int portno))dlsym(libHandle, "zts_set_service_port"); - _zts_start = (int(*)(const char *path, bool blocking))dlsym(libHandle, "zts_start"); - _zts_startjoin = (int(*)(const char*, uint64_t))dlsym(libHandle, "zts_startjoin"); - _zts_stop = (void(*)(void))dlsym(libHandle, "zts_stop"); - _zts_core_running = (int(*)(void))dlsym(libHandle, "zts_core_running"); - _zts_stack_running = (int(*)(void))dlsym(libHandle, "zts_stack_running"); - _zts_ready = (int(*)(void))dlsym(libHandle, "zts_ready"); - _zts_join = (int(*)(const uint64_t))dlsym(libHandle, "zts_join"); - _zts_leave = (int(*)(const uint64_t))dlsym(libHandle, "zts_leave"); - _zts_get_path = (void(*)(char *homePath, const size_t len))dlsym(libHandle, "zts_get_path"); - _zts_get_node_id = (uint64_t(*)(void))dlsym(libHandle, "zts_get_node_id"); - _zts_has_address = (int(*)(const uint64_t))dlsym(libHandle, "zts_has_address"); - _zts_get_num_assigned_addresses = (int(*)(const uint64_t nwid))dlsym(libHandle, "zts_get_num_assigned_addresses"); - _zts_get_address_at_index = (int(*)(const uint64_t nwid, const int index, struct sockaddr_storage *addr))dlsym(libHandle, "zts_get_address_at_index"); - _zts_get_address = (int(*)(const uint64_t nwid, struct sockaddr_storage *addr, const int address_family))dlsym(libHandle, "zts_get_address"); - _zts_get_6plane_addr = (void(*)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId))dlsym(libHandle, "zts_get_6plane_addr"); - _zts_get_rfc4193_addr = (void(*)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId))dlsym(libHandle, "zts_get_rfc4193_addr"); - _zts_get_peer_count = (unsigned long(*)(void))dlsym(libHandle, "zts_get_peer_count"); - _zts_get_peer_address = (int(*)(char *peer, const uint64_t nodeId))dlsym(libHandle, "zts_get_peer_address"); - _zts_socket = (int(*)(int socket_family, int socket_type, int protocol))dlsym(libHandle, "zts_socket"); - _zts_connect = (int(*)(int fd, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_connect"); - _zts_bind = (int(*)(int fd, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_bind"); - _zts_listen = (int(*)(int fd, int backlog))dlsym(libHandle, "zts_listen"); - _zts_accept = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_accept"); - #if defined(__linux__) - _zts_accept4 = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags))dlsym(libHandle, "zts_accept4"); - #endif - _zts_setsockopt = (int(*)(int fd, int level, int optname, const void *optval, socklen_t optlen))dlsym(libHandle, "zts_setsockopt"); - _zts_getsockopt = (int(*)(int fd, int level, int optname, void *optval, socklen_t *optlen))dlsym(libHandle, "zts_getsockopt"); - _zts_getsockname = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_getsockname"); - _zts_getpeername = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_getpeername"); - _zts_gethostname = (int(*)(char *name, size_t len))dlsym(libHandle, "zts_gethostname"); - _zts_sethostname = (int(*)(const char *name, size_t len))dlsym(libHandle, "zts_sethostname"); - _zts_gethostbyname = (struct hostent*(*)(const char *name))dlsym(libHandle, "zts_gethostbyname"); - _zts_close = (int(*)(int fd))dlsym(libHandle, "zts_close"); - _zts_fcntl = (int(*)(int fd, int cmd, int flags))dlsym(libHandle, "zts_fcntl"); - _zts_ioctl = (int(*)(int fd, unsigned long request, void *argp))dlsym(libHandle, "zts_ioctl"); - _zts_send = (ssize_t(*)(int fd, const void *buf, size_t len, int flags))dlsym(libHandle, "zts_send"); - _zts_sendto = (ssize_t(*)(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_sendto"); - _zts_sendmsg = (ssize_t(*)(int fd, const struct msghdr *msg, int flags))dlsym(libHandle, "zts_sendmsg"); - _zts_recv = (ssize_t(*)(int fd, void *buf, size_t len, int flags))dlsym(libHandle, "zts_recv"); - _zts_recvfrom = (ssize_t(*)(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_recvfrom"); - _zts_recvmsg = (ssize_t(*)(int fd, struct msghdr *msg,int flags))dlsym(libHandle, "zts_recvmsg"); - _zts_read = (int(*)(int fd, void *buf, size_t len))dlsym(libHandle, "zts_read"); - _zts_write = (int(*)(int fd, const void *buf, size_t len))dlsym(libHandle, "zts_write"); - _zts_shutdown = (int(*)(int fd, int how))dlsym(libHandle, "zts_shutdown"); - _zts_add_dns_nameserver = (int(*)(struct sockaddr *addr))dlsym(libHandle, "zts_add_dns_nameserver"); -} - -char *msg = (char*)"welcome to the machine"; - -int main(int argc, char **argv) -{ - if (argc != 5) { - printf("\nlibzt example client\n"); - printf("client [config_file_path] [nwid] [remote_addr] [remote_port]\n"); - exit(0); - } - -#ifdef __linux__ - char *library_path = (char*)"./libzt.so"; -#endif -#ifdef __APPLE__ - char *library_path = (char*)"./libzt.dylib"; -#endif - load_library_symbols(library_path); - - std::string path = argv[1]; - std::string nwidstr = argv[2]; - std::string remote_addr = argv[3]; - int remote_port = atoi(argv[4]); - int r=0, w=0, err=0, sockfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4; - in4.sin_port = htons(remote_port); - in4.sin_addr.s_addr = inet_addr(remote_addr.c_str()); - in4.sin_family = AF_INET; - - // - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - _zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = _zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = _zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = _zts_connect(sockfd, (const struct sockaddr *)&in4, sizeof(in4))) < 0) { - printf("error connecting to remote host (%d)\n", err); - } - - printf("sending to server...\n"); - w = _zts_write(sockfd, msg, strlen(msg)); - - printf("reading from server...\n"); - r = _zts_read(sockfd, rbuf, strlen(msg)); - - printf("Sent : %s\n", msg); - printf("Received : %s\n", rbuf); - - err = _zts_close(sockfd); - - return err; -} - -#endif // _MSC_VER \ No newline at end of file diff --git a/examples/cpp/sharedlib/ipv4server_shared.cpp b/examples/cpp/sharedlib/ipv4server_shared.cpp deleted file mode 100644 index e5262cb..0000000 --- a/examples/cpp/sharedlib/ipv4server_shared.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#if !defined(_MSC_VER) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libzt.h" - -// function pointers which will have values assigned once the dynamic library is loaded - -int (*_zts_set_service_port)(int portno); -int (*_zts_start)(const char *path, bool blocking); -int (*_zts_startjoin)(const char*, uint64_t); -void (*_zts_stop)(void); -int (*_zts_core_running)(void); -int (*_zts_stack_running)(void); -int (*_zts_ready)(void); -int (*_zts_join)(const uint64_t); -int (*_zts_leave)(const uint64_t); -void (*_zts_get_path)(char *homePath, const size_t len); -uint64_t (*_zts_get_node_id)(void); -int (*_zts_has_address)(const uint64_t); -int (*_zts_get_num_assigned_addresses)(const uint64_t nwid); -int (*_zts_get_address_at_index)(const uint64_t nwid, const int index, struct sockaddr_storage *addr); -int (*_zts_get_address)(const uint64_t nwid, struct sockaddr_storage *addr, const int address_family); -void (*_zts_get_6plane_addr)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); -void (*_zts_get_rfc4193_addr)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); -unsigned long (*_zts_get_peer_count)(void); -int (*_zts_get_peer_address)(char *peer, const uint64_t nodeId); -int (*_zts_socket)(int socket_family, int socket_type, int protocol); -int (*_zts_connect)(int fd, const struct sockaddr *addr, socklen_t addrlen); -int (*_zts_bind)(int fd, const struct sockaddr *addr, socklen_t addrlen); -int (*_zts_listen)(int fd, int backlog); -int (*_zts_accept)(int fd, struct sockaddr *addr, socklen_t *addrlen); -#if defined(__linux__) -int (*_zts_accept4)(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags); -#endif -int (*_zts_setsockopt)(int fd, int level, int optname, const void *optval, socklen_t optlen); -int (*_zts_getsockopt)(int fd, int level, int optname, void *optval, socklen_t *optlen); -int (*_zts_getsockname)(int fd, struct sockaddr *addr, socklen_t *addrlen); -int (*_zts_getpeername)(int fd, struct sockaddr *addr, socklen_t *addrlen); -int (*_zts_gethostname)(char *name, size_t len); -int (*_zts_sethostname)(const char *name, size_t len); -struct hostent *(*_zts_gethostbyname)(const char *name); -int (*_zts_close)(int fd); -int(*_zts_fcntl)(int fd, int cmd, int flags); -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_sendto)(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); -ssize_t (*_zts_sendmsg)(int fd, const struct msghdr *msg, int flags); -ssize_t (*_zts_recv)(int fd, void *buf, size_t len, int flags); -ssize_t (*_zts_recvfrom)(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); -ssize_t (*_zts_recvmsg)(int fd, struct msghdr *msg,int flags); -int (*_zts_read)(int fd, void *buf, size_t len); -int (*_zts_write)(int fd, const void *buf, size_t len); -int (*_zts_shutdown)(int fd, int how); -int (*_zts_add_dns_nameserver)(struct sockaddr *addr); -int (*_zts_del_dns_nameserver)(struct sockaddr *addr); - - -void load_library_symbols(char *library_path) -{ - void *libHandle = dlopen(library_path, RTLD_LAZY); - if (libHandle == NULL) { - DEBUG_ERROR("unable to load dynamic libs: %s", dlerror()); - exit(-1); - } - - // Load symbols from library (call these directly) - - _zts_set_service_port = (int(*)(int portno))dlsym(libHandle, "zts_set_service_port"); - _zts_start = (int(*)(const char *path, bool blocking))dlsym(libHandle, "zts_start"); - _zts_startjoin = (int(*)(const char*, uint64_t))dlsym(libHandle, "zts_startjoin"); - _zts_stop = (void(*)(void))dlsym(libHandle, "zts_stop"); - _zts_core_running = (int(*)(void))dlsym(libHandle, "zts_core_running"); - _zts_stack_running = (int(*)(void))dlsym(libHandle, "zts_stack_running"); - _zts_ready = (int(*)(void))dlsym(libHandle, "zts_ready"); - _zts_join = (int(*)(const uint64_t))dlsym(libHandle, "zts_join"); - _zts_leave = (int(*)(const uint64_t))dlsym(libHandle, "zts_leave"); - _zts_get_path = (void(*)(char *homePath, const size_t len))dlsym(libHandle, "zts_get_path"); - _zts_get_node_id = (uint64_t(*)(void))dlsym(libHandle, "zts_get_node_id"); - _zts_has_address = (int(*)(const uint64_t))dlsym(libHandle, "zts_has_address"); - _zts_get_num_assigned_addresses = (int(*)(const uint64_t nwid))dlsym(libHandle, "zts_get_num_assigned_addresses"); - _zts_get_address_at_index = (int(*)(const uint64_t nwid, const int index, struct sockaddr_storage *addr))dlsym(libHandle, "zts_get_address_at_index"); - _zts_get_address = (int(*)(const uint64_t nwid, struct sockaddr_storage *addr, const int address_family))dlsym(libHandle, "zts_get_address"); - _zts_get_6plane_addr = (void(*)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId))dlsym(libHandle, "zts_get_6plane_addr"); - _zts_get_rfc4193_addr = (void(*)(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId))dlsym(libHandle, "zts_get_rfc4193_addr"); - _zts_get_peer_count = (unsigned long(*)(void))dlsym(libHandle, "zts_get_peer_count"); - _zts_get_peer_address = (int(*)(char *peer, const uint64_t nodeId))dlsym(libHandle, "zts_get_peer_address"); - _zts_socket = (int(*)(int socket_family, int socket_type, int protocol))dlsym(libHandle, "zts_socket"); - _zts_connect = (int(*)(int fd, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_connect"); - _zts_bind = (int(*)(int fd, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_bind"); - _zts_listen = (int(*)(int fd, int backlog))dlsym(libHandle, "zts_listen"); - _zts_accept = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_accept"); - #if defined(__linux__) - _zts_accept4 = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags))dlsym(libHandle, "zts_accept4"); - #endif - _zts_setsockopt = (int(*)(int fd, int level, int optname, const void *optval, socklen_t optlen))dlsym(libHandle, "zts_setsockopt"); - _zts_getsockopt = (int(*)(int fd, int level, int optname, void *optval, socklen_t *optlen))dlsym(libHandle, "zts_getsockopt"); - _zts_getsockname = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_getsockname"); - _zts_getpeername = (int(*)(int fd, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_getpeername"); - _zts_gethostname = (int(*)(char *name, size_t len))dlsym(libHandle, "zts_gethostname"); - _zts_sethostname = (int(*)(const char *name, size_t len))dlsym(libHandle, "zts_sethostname"); - _zts_gethostbyname = (struct hostent*(*)(const char *name))dlsym(libHandle, "zts_gethostbyname"); - _zts_close = (int(*)(int fd))dlsym(libHandle, "zts_close"); - _zts_fcntl = (int(*)(int fd, int cmd, int flags))dlsym(libHandle, "zts_fcntl"); - _zts_ioctl = (int(*)(int fd, unsigned long request, void *argp))dlsym(libHandle, "zts_ioctl"); - _zts_send = (ssize_t(*)(int fd, const void *buf, size_t len, int flags))dlsym(libHandle, "zts_send"); - _zts_sendto = (ssize_t(*)(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen))dlsym(libHandle, "zts_sendto"); - _zts_sendmsg = (ssize_t(*)(int fd, const struct msghdr *msg, int flags))dlsym(libHandle, "zts_sendmsg"); - _zts_recv = (ssize_t(*)(int fd, void *buf, size_t len, int flags))dlsym(libHandle, "zts_recv"); - _zts_recvfrom = (ssize_t(*)(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen))dlsym(libHandle, "zts_recvfrom"); - _zts_recvmsg = (ssize_t(*)(int fd, struct msghdr *msg,int flags))dlsym(libHandle, "zts_recvmsg"); - _zts_read = (int(*)(int fd, void *buf, size_t len))dlsym(libHandle, "zts_read"); - _zts_write = (int(*)(int fd, const void *buf, size_t len))dlsym(libHandle, "zts_write"); - _zts_shutdown = (int(*)(int fd, int how))dlsym(libHandle, "zts_shutdown"); - _zts_add_dns_nameserver = (int(*)(struct sockaddr *addr))dlsym(libHandle, "zts_add_dns_nameserver"); -} - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [nwid] [bind_port]\n"); - exit(0); - } - -#ifdef __linux__ - char *library_path = (char*)"./libzt.so"; -#endif -#ifdef __APPLE__ - char *library_path = (char*)"./libzt.dylib"; -#endif - load_library_symbols(library_path); - - std::string path = argv[1]; - std::string nwidstr = argv[2]; - int bind_port = atoi(argv[3]); - int w=0, r=0, err=0, sockfd, accfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in in4, acc_in4; - in4.sin_port = htons(bind_port); - in4.sin_addr.s_addr = INADDR_ANY; - in4.sin_family = AF_INET; - - // - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - _zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = _zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = _zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - if ((err = _zts_bind(sockfd, (struct sockaddr *)&in4, sizeof(struct sockaddr_in)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - if ((err = _zts_listen(sockfd, 100)) < 0) { - printf("error placing socket in LISTENING state (%d)\n", err); - } - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((accfd = _zts_accept(sockfd, (struct sockaddr *)&acc_in4, &client_addrlen)) < 0) { - printf("error accepting connection (%d)\n", err); - } - - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - _zts_getpeername(accfd, (struct sockaddr*)&acc_in4, &peer_addrlen); - DEBUG_INFO("accepted connection from %s : %d\n", inet_ntoa(acc_in4.sin_addr), ntohs(acc_in4.sin_port)); - - printf("reading from client...\n"); - r = _zts_read(accfd, rbuf, sizeof rbuf); - - printf("sending to client...\n"); - w = _zts_write(accfd, rbuf, strlen(rbuf)); - - printf("Received : %s\n", rbuf); - - err = _zts_close(sockfd); - err = _zts_close(accfd); - - return err; -} - -#endif // _MSC_VER diff --git a/test/example.cpp b/test/example.cpp deleted file mode 100644 index 11ff33c..0000000 --- a/test/example.cpp +++ /dev/null @@ -1,280 +0,0 @@ -#include -#include -#include -#include - -#include "ZeroTier.h" - -bool node_ready = false; -bool network_ready = false; - -// Example callbacks -void myZeroTierEventCallback(struct zts_callback_msg *msg) -{ - // - // Node events - // - if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { - printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address); - node_ready = true; - // ZeroTier service is running and online - } - if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { - printf("ZTS_EVENT_NODE_OFFLINE\n"); - node_ready = false; - // ZeroTier service is running and online - } - if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { - printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); - // ZeroTier service has stopped - } - - // - // Virtual network events - // - if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { - printf("ZTS_EVENT_NETWORK_NOT_FOUND --- network=%llx\n", msg->network->nwid); - // Is your nwid incorrect? - } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- network=%llx\n", msg->network->nwid); - // Node is requesting network config details from controller, please wait - } - if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) { - printf("ZTS_EVENT_NETWORK_ACCESS_DENIED --- network=%llx\n", msg->network->nwid); - // This node is not authorized to join nwid - } - if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { - printf("ZTS_EVENT_NETWORK_READY_IP4 --- network=%llx\n", msg->network->nwid); - network_ready = true; - // IPv4 traffic can now be processed for nwid - } - if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) { - printf("ZTS_EVENT_NETWORK_READY_IP6 --- network=%llx\n", msg->network->nwid); - network_ready = true; - } - if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { - printf("ZTS_EVENT_NETWORK_DOWN --- network=%llx\n", msg->network->nwid); - // Can happen if another thread called zts_leave() - } - - // - // Network stack events - // - if (msg->eventCode == ZTS_EVENT_NETIF_UP) { - printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", - msg->netif->nwid, - msg->netif->mac, - msg->netif->mtu); - network_ready = true; - } - if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { - printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", - msg->netif->nwid, - msg->netif->mac); - - network_ready = true; - } - - // - // Address events - // - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[INET_ADDRSTRLEN]; - struct sockaddr_in *in4 = (struct sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- addr=%s (on network=%llx)\n", - ipstr, msg->addr->nwid); - } - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP6 --- addr=%s (on network=%llx)\n", - ipstr, msg->addr->nwid); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[INET_ADDRSTRLEN]; - struct sockaddr_in *in4 = (struct sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- addr=%s (on network=%llx)\n", - ipstr, msg->addr->nwid); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- addr=%s (on network=%llx)\n", - ipstr, msg->addr->nwid); - } - - // - // Peer events - // - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); - // A direct path is known for nodeId - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); - // No direct path is known for nodeId - } -} - -void printPeerDetails(struct zts_peer_details *pd) -{ - printf("\npeer=%llx, latency=%d, version=%d.%d.%d, pathCount=%d\n", - pd->address, - pd->latency, - pd->versionMajor, - pd->versionMinor, - pd->versionRev, - pd->pathCount); - // Print all known paths for each peer - for (int j=0; jpathCount; j++) { - char ipstr[INET6_ADDRSTRLEN]; - int port; - struct sockaddr *sa = (struct sockaddr *)&(pd->paths[j].address); - if (sa->sa_family == AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in*)sa; - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - port = ntohs(in4->sin_port); - } - if (sa->sa_family == AF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)sa; - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - } - printf("\tpath[%d]=%s, port=%d\n", j, ipstr, port); - } -} - -void getSinglePeerDetails(uint64_t peerId) -{ - struct zts_peer_details pd; - int err = zts_get_peer(&pd, peerId); - - if (err == ZTS_ERR_OK) { - printf("(%d) call succeeded\n", err); - } if (err == ZTS_ERR_INVALID_ARG) { - printf("(%d) invalid argument\n", err); - return; - } if (err == ZTS_ERR_SERVICE) { - printf("(%d) error: service is unavailable\n", err); - return; - } if (err == ZTS_ERR_INVALID_OP) { - printf("(%d) error: invalid API operation\n", err); - return; - } if (err == ZTS_ERR_NO_RESULT) { - printf("(%d) error: object or result not found\n", err); - return; - } - if (err == 0) { // ZTS_ERR_OK - printPeerDetails(&pd); - } -} - -// Similar to "zerotier-cli listpeers" -void getAllPeerDetails() -{ - struct zts_peer_details pd[128]; - /* This number should be large enough to handle the - expected number of peers. This call can also get - expensive for large numbers of peers. Consider using - get_peer(struct zts_peer_details *pds, uint64_t peerId) - instead */ - int num = 128; - int err; - if ((err = zts_get_peers(pd, &num)) < 0) { - printf("error (%d)\n", err); - return; - } - if (num) { - printf("num=%d\n", num); - for (int i=0; i. - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libzt.h" - -#if defined(__SELFTEST__) -#include "Utils.hpp" -#endif - -#if defined(__linux__) || defined(__APPLE__) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - - -#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 -#else - #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL -#endif - -void micro_sleep(unsigned long us) -{ - std::this_thread::sleep_for(std::chrono::microseconds(us)); -} - -#if defined(_WIN32) -#include -#include -#include -#include -#include -#include - -void sleep(unsigned long ms) -{ - Sleep(ms); -} - -struct timezone -{ - int tz_minuteswest; - int tz_dsttime; -}; - - -int gettimeofday(struct timeval *tv, struct timezone *tz) -{ - FILETIME ft; - unsigned __int64 tmpres = 0; - static int tzflag; - - if (NULL != tv) - { - GetSystemTimeAsFileTime(&ft); - - tmpres |= ft.dwHighDateTime; - tmpres <<= 32; - tmpres |= ft.dwLowDateTime; - - /*converting file time to unix epoch*/ - tmpres -= DELTA_EPOCH_IN_MICROSECS; - tmpres /= 10; /*convert into microseconds*/ - tv->tv_sec = (long)(tmpres / 1000000UL); - tv->tv_usec = (long)(tmpres % 1000000UL); - } - - if (NULL != tz) - { - if (!tzflag) - { - _tzset(); - tzflag++; - } - tz->tz_minuteswest = _timezone / 60; - tz->tz_dsttime = _daylight; - } - - return 0; -} -#endif - - -#define EXIT_ON_FAIL false - -#define TEST_PASSED 1 -#define TEST_FAILED 0 - -#define ECHO_INTERVAL 1000000 // microseconds -#define SLAM_INTERVAL 500000 // microseconds - -#define ARTIFICIAL_SOCKET_LINGER 1 - -#define STR_SIZE 32 - -#define TEST_OP_N_BYTES 10 -#define TEST_OP_N_SECONDS 11 -#define TEST_OP_N_TIMES 12 - -#define TEST_MODE_CLIENT 20 -#define TEST_MODE_SERVER 21 - -#define TEST_TYPE_SIMPLE 30 -#define TEST_TYPE_SUSTAINED 31 -#define TEST_TYPE_PERF 32 -#define TEST_TYPE_PERF_TO_ECHO 33 - -#define MIN_PORT 5000 -#define MAX_PORT 50000 - -#define TCP_UNIT_TEST_SIG_4 struct sockaddr_in *addr, int op, int cnt, char *details, bool *passed -#define UDP_UNIT_TEST_SIG_4 struct sockaddr_in *local_addr, struct sockaddr_in *remote_addr, int op, int cnt, char *details, bool *passed - -#define TCP_UNIT_TEST_SIG_6 struct sockaddr_in6 *addr, int op, int cnt, char *details, bool *passed -#define UDP_UNIT_TEST_SIG_6 struct sockaddr_in6 *local_addr, struct sockaddr_in6 *remote_addr, int op, int cnt, char *details, bool *passed - -#define ECHOTEST_MODE_RX 333 -#define ECHOTEST_MODE_TX 666 - -#define MAX_RX_BUF_SZ 2048 -#define MAX_TX_BUF_SZ 2048 - -#define ONE_MEGABYTE 1024 * 1024 - -#define DETAILS_STR_LEN 128 - -// If running a self test, use libzt calls -#if defined(__SELFTEST__) -#define _SOCKET zts_socket -#define _BIND zts_bind -#define _LISTEN zts_listen -#define _ACCEPT zts_accept -#define _CONNECT zts_connect -#define _READ zts_read -#define _WRITE zts_write -#define _RECV zts_recv -#define _SEND zts_send -#define _RECVFROM zts_recvfrom -#define _SENDTO zts_sendto -#define _RECVMSG zts_recvmsg -#define _SENDMSG zts_sendmsg -#define _SETSOCKOPT zts_setsockopt -#define _GETSOCKOPT zts_getsockopt -#define _IOCTL zts_ioctl -#define _FCNTL zts_fcntl -#define _SELECT zts_select -#define _CLOSE zts_close -#define _GETPEERNAME zts_getpeername -#endif - -// If running a native instance to test against, use system calls -#if defined(__NATIVETEST__) -inline unsigned int gettid() -{ -#ifdef _WIN32 - return GetCurrentThreadId(); -#elif defined(__unix__) - return static_cast(::syscall(__NR_gettid)); -#elif defined(__APPLE__) - uint64_t tid64; - pthread_threadid_np(NULL, &tid64); - return static_cast(tid64); -#endif -} - -#define _SOCKET socket -#define _BIND bind -#define _LISTEN listen -#define _ACCEPT accept -#define _CONNECT connect -#define _RECV recvmsg -#define _SEND send -#define _RECVFROM recvfrom -#define _SENDTO sendto -#define _RECVMSG recvmsg -#define _SENDMSG sendmsg -#define _SETSOCKOPT setsockopt -#define _GETSOCKOPT getsockopt -#define _IOCTL ioctl -#define _FCNTL fcntl -#define _SELECT select - -#define _READ read -#define _WRITE write - -//#define _RECV recv -//#define _SEND send - -#if defined(_WIN32) -#define _CLOSE closesocket -#else -#define _CLOSE close -#endif - -#define _GETPEERNAME getpeername -#endif - -std::map testConf; - -/* Tests in this file: - - Basic RX/TX connect()/accept() Functionality: - - [ ?] slam - perform thousands of the same call per second - [ ] random - act like a monkey, press all the buttons - [OK] simple client ipv4 - connect, send one message and wait for an echo - [OK] simple server ipv4 - accept, read one message and echo it back - [OK] simple client ipv6 - connect, send one message and wait for an echo - [OK] simple server ipv6 - accept, read one message and echo it back - [OK] sustained client ipv4 - connect and rx/tx many messages, VERIFIES data integrity - [OK] sustained server ipv4 - accept and echo messages, VERIFIES data integrity - [OK] sustained client ipv6 - connect and rx/tx many messages, VERIFIES data integrity - [OK] sustained server ipv6 - accept and echo messages, VERIFIES data integrity - [OK] comprehensive client ipv4 - test all ipv4/6 client simple/sustained modes - [OK] comprehensive server ipv6 - test all ipv4/6 server simple/sustained modes - [ ?] SOCK_RAW (VL2) ipv4 - See test/layer2.cpp - [ ?] SOCK_RAW (VL2) ipv6 - See test/layer2.cpp - - Performance: - (See libzt.h, compile libzt with appropriate ZT_TCP_TX_BUF_SZ, ZT_TCP_RX_BUF_SZ, ZT_UDP_TX_BUF_SZ, and ZT_UDO_RX_BUF_SZ for your test) - - [OK] Throughput - Test maximum RX/TX speeds - [ ] Memory Usage - Test memory consumption profile - [ ] CPU Usage - Test processor usage - [ ] - - Correctness: - - [ ] Block/Non-block - Test that blocking and non-blocking behaviour is consistent - [ ] Release of resources - Test that all destructor methods/blocks function properly - [OK] Multi-network handling - Test internal Tap multiplexing works for multiple networks - [ ] Address handling - Test that addresses are copied/parsed/returned properly - -*/ - - - - - -/****************************************************************************/ -/* Helper Functions */ -/****************************************************************************/ - -/** Returns true on success, or false if there was an error */ -bool SetSocketBlockingEnabled(int fd, bool blocking) -{ - if (fd < 0) return false; -#ifdef _WIN32 - unsigned long mode = blocking ? 0 : 1; - return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false; -#else - int flags = _FCNTL(fd, F_GETFL, 0); - if (flags == -1) return false; - flags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); - return (_FCNTL(fd, F_SETFL, flags) == 0) ? true : false; -#endif -} - -void displayResults(int *results, int size) -{ - int success = 0, failure = 0; - for (int i=0; i get_now_ts()) { - sleep(1); - } -} -void wait_until_tplus_s(long int original_time, int tplus_s) -{ - int current_time_offset = (get_now_ts() - original_time) / 1000; - fprintf(stderr, "\n\n--- WAITING FOR T+%d --- (current: T+%d)\n\n", tplus_s, current_time_offset); - if (current_time_offset > tplus_s) { - DEBUG_ERROR("--- ABORTING TEST: Tests are out of sync and might not yield valid results. ---"); - //exit(0); - } - if (current_time_offset == tplus_s) { - DEBUG_ERROR("--- WARNING: Tests might be out of sync and might not yield valid results. ---"); - } - wait_until_tplus(original_time, tplus_s * 1000); -} - -int rand_in_range(int min, int max) -{ -#if defined(__SELFTEST__) - unsigned int seed; - ZeroTier::Utils::getSecureRandom((void*)&seed,sizeof(seed)); - srand(seed); -#else - srand((unsigned int)time(NULL)); -#endif - return min + rand() % static_cast(max - min + 1); -} - -void generate_random_data(void *buf, size_t n, int min, int max) -{ - char *b = (char*)buf; - for (size_t i=0; isin_addr.s_addr = inet_addr(ipstr.c_str()); - in4->sin_family = AF_INET; - in4->sin_port = htons(port); - } if (ipv == 6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)saddr; - inet_pton(AF_INET6, ipstr.c_str(), &(in6->sin6_addr)); - in6->sin6_flowinfo = 0; - in6->sin6_family = AF_INET6; - in6->sin6_port = htons(port); - } -} - -void RECORD_RESULTS(bool passed, char *details, std::vector *results) -{ - char *ok_str = (char*)"[ OK ]"; - char *fail_str = (char*)"[ FAIL ]"; - if (passed == TEST_PASSED) { - DEBUG_TEST("%s", ok_str); - results->push_back(std::string(ok_str) + " " + std::string(details)); - } else { - DEBUG_ERROR("%s", fail_str); - results->push_back(std::string(fail_str) + " " + std::string(details)); - } if (EXIT_ON_FAIL && !passed) { - fprintf(stderr, "%s\n", results->at(results->size()-1).c_str()); - exit(0); - } - memset(details, 0, DETAILS_STR_LEN); -} - -void wait_until_everyone_is_ready(struct sockaddr *local_addr, struct sockaddr *remote_addr, int start_port) -{ - /* try to connect to (and listen for) other selftest instances on the network. - When one is found, send some sort of synchronization message and allow test to - begin */ - int err; - bool connected = false; - int accepted_fd; - // listen socket setup - int listen_fd; - if ((listen_fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket"); - exit(0); - } if ((err = _BIND(listen_fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { - perror("bind"); - exit(0); - } if ((err = _LISTEN(listen_fd, 0)) < 0) { - perror("listen"); - exit(0); - } - SetSocketBlockingEnabled(listen_fd, true); - // connect socket setup - int conn_fd; - if ((conn_fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket"); - exit(0); - } - SetSocketBlockingEnabled(conn_fd, true); - while(connected == false) { - if ((err = _CONNECT(conn_fd, (const struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { - if (errno == EISCONN) { - DEBUG_TEST("connected"); - connected = true; - } - } - else { - connected = true; - } - if (connected == false) { - struct sockaddr_in client; - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((accepted_fd = _ACCEPT(listen_fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { - //DEBUG_TEST("errno = %d", errno); - } - else { - DEBUG_TEST("connected"); - connected = true; - } - } - sleep(1); - } - _CLOSE(listen_fd); - _CLOSE(conn_fd); - _CLOSE(accepted_fd); -} - - -/****************************************************************************/ -/* POLL/_SELECT */ -/****************************************************************************/ - -void tcp_select_server(TCP_UNIT_TEST_SIG_4) -{ - std::string testname = "tcp_select_server"; - std::string msg = "tcp_select"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "accept connection, create poll/select loop, read and write strings back and forth\n"); - int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - if ((err = _LISTEN(fd, 100)) < 0) { - printf("error placing socket in _LISTENING state (%d)", err); - perror("listen"); - *passed = false; - return; - } - struct sockaddr_in client; - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { - perror("accept"); - *passed = false; - return; - } - - DEBUG_TEST("accepted connection fd=%d", client_fd); - - fd_set read_set, write_set; - memset(&read_set, 0, sizeof(read_set)); - memset(&write_set, 0, sizeof(write_set)); - - uint32_t msecs = 5; - struct timeval tv; - tv.tv_sec = msecs / 1000; - tv.tv_usec = (msecs % 1000) * 1000; - int ret = 0; - - FD_SET(client_fd, &read_set); - FD_SET(client_fd, &write_set); - - int tot = 1000, rx_num = 0, tx_num = 0; - - while(rx_num < tot && tx_num < tot) { - - FD_ZERO(&read_set); - FD_ZERO(&write_set); - - FD_SET(client_fd, &read_set); - FD_SET(client_fd, &write_set); - - ret = _SELECT(client_fd + 1, &read_set, &write_set, NULL, &tv); - - if (ret > 0) { - for (int fd_i=0; fd_i 0) { - DEBUG_TEST("socket activity"); - for (int fd_i=0; fd_i %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - -#if defined(_WIN32) - w = _SEND(fd, msg.c_str(), len, 0); -#else - w = _WRITE(fd, msg.c_str(), len); -#endif -#if defined(_WIN32) - r = _RECV(fd, rbuf, len, 0); -#else - r = _READ(fd, rbuf, len); -#endif - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - - -void tcp_server_4(TCP_UNIT_TEST_SIG_4) -{ - std::string testname = "tcp_server_4"; - std::string msg = "tcp_cs_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "accept connection with IPv4 address, read string, write string, compare.\n"); - int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - if ((err = _LISTEN(fd, 100)) < 0) { - printf("error placing socket in _LISTENING state (%d)", err); - perror("listen"); - *passed = false; - return; - } - struct sockaddr_in client; - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { - perror("accept"); - *passed = false; - return; - } - DEBUG_TEST("accepted connection from %s, on port %d", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); - // TODO: Put this test in the general API section - struct sockaddr_storage peer_addr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; - socklen_t peer_addrlen = sizeof(peer_addr); - - if ((err = _GETPEERNAME(client_fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { - perror("getpeername"); - *passed = false; - return; - } - DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); -#if defined(_WIN32) - r = _RECV(client_fd, rbuf, len, 0); -#else - r = _READ(client_fd, rbuf, len); -#endif - -#if defined(_WIN32) - w = _SEND(client_fd, rbuf, len, 0); -#else - w = _WRITE(client_fd, rbuf, len); -#endif - DEBUG_TEST("Received : %s, r=%d, w=%d", rbuf, r, w); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - err = _CLOSE(client_fd); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - - -void tcp_client_6(TCP_UNIT_TEST_SIG_6) -{ - std::string testname = "tcp_client_6"; - std::string msg = "tcp_cs_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "connect to remote host with IPv6 address, write string, read string, compare.\n"); - int r, w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { - DEBUG_ERROR("error connecting to remote host (%d)", err); - perror("connect"); - *passed = false; - return; - } - // TODO: Put this test in the general API section - struct sockaddr_storage peer_addr; - struct sockaddr_in6 *p6 = (struct sockaddr_in6*)&peer_addr; - socklen_t peer_addrlen = sizeof(peer_addr); - if ((err = _GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { - perror("getpeername"); - *passed = false; - return; - } - char peer_addrstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(p6->sin6_addr), peer_addrstr, INET6_ADDRSTRLEN); - DEBUG_TEST("getpeername() => %s : %d", peer_addrstr, ntohs(p6->sin6_port)); - -#if defined(_WIN32) - w = _SEND(fd, msg.c_str(), len, 0); -#else - w = _WRITE(fd, msg.c_str(), len); -#endif -#if defined(_WIN32) - r = _RECV(fd, rbuf, len, 0); -#else - r = _READ(fd, rbuf, len); -#endif - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - -void tcp_server_6(TCP_UNIT_TEST_SIG_6) -{ - std::string testname = "tcp_server_6"; - std::string msg = "tcp_cs_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "accept connection with IPv6 address, read string, write string, compare.\n"); - int w=0, r=0, fd, client_fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in6)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - if ((err = _LISTEN(fd, 100)) < 0) { - DEBUG_ERROR("error placing socket in _LISTENING state (%d)", err); - perror("listen"); - *passed = false; - return; - } - struct sockaddr_in6 client; - socklen_t client_addrlen = sizeof(sockaddr_in6); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { - perror("accept"); - *passed = false; - return; - } - char ipstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &client.sin6_addr, ipstr, sizeof ipstr); - DEBUG_TEST("accepted connection from %s, on port %d", ipstr, ntohs(client.sin6_port)); - // TODO: Put this test in the general API section - struct sockaddr_storage peer_addr; - struct sockaddr_in6 *p6 = (struct sockaddr_in6*)&peer_addr; - socklen_t peer_addrlen = sizeof(peer_addr); - if ((err = _GETPEERNAME(client_fd, (struct sockaddr*)&peer_addr, &peer_addrlen)) < 0) { - perror("getpeername"); - *passed = false; - return; - } - char peer_addrstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(p6->sin6_addr), peer_addrstr, INET6_ADDRSTRLEN); - DEBUG_TEST("getpeername() => %s : %d", peer_addrstr, ntohs(p6->sin6_port)); -#if defined(_WIN32) - r = _RECV(client_fd, rbuf, sizeof rbuf, 0); -#else - r = _READ(client_fd, rbuf, sizeof rbuf); -#endif -#if defined(_WIN32) - w = _SEND(client_fd, rbuf, len, 0); -#else - w = _WRITE(client_fd, rbuf, len); -#endif - DEBUG_TEST("Received : %s", rbuf); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - err = _CLOSE(client_fd); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - - -// UDP - -void udp_client_4(UDP_UNIT_TEST_SIG_4) -{ - std::string testname = "udp_client_4"; - std::string msg = "udp_cs_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv4 address, send string until response is seen. compare.\n"); - int r, w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - SetSocketBlockingEnabled(fd, true); - DEBUG_TEST("sending UDP packets until I get a single response..."); - if ((err = _BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - struct sockaddr_storage saddr; - while (true) { - sleep(1); - // tx - if ((w = _SENDTO(fd, msg.c_str(), strlen(msg.c_str()), 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { - DEBUG_ERROR("error sending packet, err=%d", errno); - } - memset(rbuf, 0, sizeof(rbuf)); - int serverlen = sizeof(struct sockaddr_storage); - // rx - r = _RECVFROM(fd, rbuf, STR_SIZE, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); - if (r == (int)strlen(msg.c_str())) { - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); - return; - } - } -} - - - - - -void udp_server_4(UDP_UNIT_TEST_SIG_4) -{ - std::string testname = "udp_server_4"; - std::string msg = "udp_cs_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv4 address, read single string, send many responses. compare.\n"); - int r, w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - // rx - DEBUG_TEST("waiting for UDP packet..."); - struct sockaddr_storage saddr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&saddr; - int serverlen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if ((r = _RECVFROM(fd, rbuf, STR_SIZE, 0, (struct sockaddr *)in4, (socklen_t *)&serverlen)) < 0) { - perror("recvfrom"); - *passed = false; - return; - } - char addrstr[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &(in4->sin_addr), addrstr, INET_ADDRSTRLEN); - // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see - DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin_addr), ntohs(remote_addr->sin_port)); - // tx - long int tx_ti = get_now_ts(); - while (true) { - sleep(1); - if ((w = _SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { - DEBUG_ERROR("error sending packet, err=%d", errno); - } - if (get_now_ts() >= tx_ti + 10000) { - break; - } - } - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - -int zts_bind_test(int fd, const struct sockaddr *addr, socklen_t addrlen) -{ - // int err = -1; - DEBUG_EXTRA("fd=%d", fd); - DEBUG_INFO("addrp=%p", addr); - DEBUG_INFO("addrlen=%d", addrlen); - DEBUG_INFO("sa_family======%d", addr->sa_family); - struct sockaddr_storage ss; - memcpy(&ss, addr, addrlen); - DEBUG_INFO("ss->sa_family=%d", ss.ss_family); - //fix_addr_socket_family((struct sockaddr*)&ss); - //ss.ss_family=AF_INET6; - //DEBUG_INFO("ss->sa_family=%d", ss.ss_family); - //err = lwip_bind(fd, (struct sockaddr*)&ss, addrlen); - exit(0); -} - -void udp_client_6(UDP_UNIT_TEST_SIG_6) -{ - std::string testname = "udp_client_6"; - std::string msg = "udp_cs_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv6 address, send string until response is seen. compare.\n"); - int r, w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - - if ((fd = _SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - SetSocketBlockingEnabled(fd, true); - DEBUG_TEST("[1] binding and sending UDP packets until I get a single response..."); - if ((err = _BIND(fd, (struct sockaddr*)local_addr, sizeof(struct sockaddr_in6)) < 0)) { - DEBUG_ERROR("error binding to interface (err=%d, errno=%d)", err, errno); - perror("bind"); - *passed = false; - return; - } - // start sending UDP packets in the hopes that at least one will be picked up by the server - struct sockaddr_storage saddr; - while (true) { - // tx - if ((w = _SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { - DEBUG_ERROR("error sending packet, err=%d", errno); - } - micro_sleep(100000); - memset(rbuf, 0, sizeof(rbuf)); - int serverlen = sizeof(struct sockaddr_storage); - // rx - r = _RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen); - if (r == len) { - DEBUG_TEST("[2] complete"); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - DEBUG_TEST("%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); - return; - } - } -} - - - -void udp_server_6(UDP_UNIT_TEST_SIG_6) -{ - std::string testname = "udp_server_6"; - std::string msg = "udp_cs_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv6 address, read single string, send many responses. compare.\n"); - int r, w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - - if ((fd = _SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - // rx - DEBUG_TEST("[1/3] waiting for UDP packet to start test..."); - struct sockaddr_storage saddr; - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&saddr; - int serverlen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if ((r = _RECVFROM(fd, rbuf, len, 0, (struct sockaddr *)&saddr, (socklen_t *)&serverlen)) < 0) { - perror("recvfrom"); - *passed = false; - return; - } - char addrstr[INET6_ADDRSTRLEN], remote_addrstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &(in6->sin6_addr), addrstr, INET6_ADDRSTRLEN); - inet_ntop(AF_INET6, &(remote_addr->sin6_addr), remote_addrstr, INET6_ADDRSTRLEN); - DEBUG_TEST("[2/3] received DGRAM from %s : %d", addrstr, ntohs(in6->sin6_port)); - DEBUG_TEST("[2/3] sending DGRAM(s) to %s : %d", remote_addrstr, ntohs(remote_addr->sin6_port)); - // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see - // tx - long int tx_ti = get_now_ts(); - while (true) { - micro_sleep(100000); - //DEBUG_TEST("sending UDP packet"); - if ((w = _SENDTO(fd, msg.c_str(), len, 0, (struct sockaddr *)remote_addr, sizeof(*remote_addr))) < 0) { - DEBUG_ERROR("error sending packet, err=%d", errno); - } - if (get_now_ts() >= tx_ti + 10000) { - // DEBUG_TEST("[3/4] get_now_ts()-tx_ti=%d", get_now_ts()-tx_ti); - break; - } - } - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - DEBUG_TEST("[3/3] complete, %s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - sprintf(details, "%s, err=%d, r=%d, w=%d", testname.c_str(), err, r, w); - DEBUG_TEST("Sent : %s", msg.c_str()); - DEBUG_TEST("Received : %s", rbuf); - *passed = (w == len && r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - - - -/****************************************************************************/ -/* SUSTAINED */ -/****************************************************************************/ - - - - - -void tcp_client_sustained_4(TCP_UNIT_TEST_SIG_4) -{ - std::string testname = "tcp_client_sustained_4"; - std::string msg = "tcp_sustained_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "connect to remote host with IPv4 address, exchange a sequence of packets, check order.\n"); - int n=0, w=0, r=0, fd, err; - char *rxbuf = (char*)malloc(cnt*sizeof(char)); - char *txbuf = (char*)malloc(cnt*sizeof(char)); - generate_random_data(txbuf, cnt, 0, 9); - - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { - DEBUG_ERROR("error connecting to remote host (%d)", err); - perror("connect"); - *passed = false; - return; - } - if (op == TEST_OP_N_BYTES) { - int wrem = cnt, rrem = cnt; - // TX - long int tx_ti = get_now_ts(); - while (wrem) { - int next_write = std::min(4096, wrem); -#if !defined(_WIN32) - signal(SIGPIPE, SIG_IGN); -#endif - DEBUG_TEST("writing..."); -#if defined(_WIN32) - n = _SEND(fd, &txbuf[w], next_write, 0); -#else - n = _WRITE(fd, &txbuf[w], next_write); -#endif - DEBUG_TEST("wrote=%d", n); - if (n > 0) { - w += n; - wrem -= n; - err = n; - DEBUG_TEST("wrote=%d, w=%d, wrem=%d", n, w, wrem); - } - } - long int tx_tf = get_now_ts(); - DEBUG_TEST("wrote=%d, reading next...", w); - // RX - long int rx_ti = 0; - while (rrem) { -#if defined(_WIN32) - n = _RECV(fd, &rxbuf[r], rrem, 0); -#else - n = _READ(fd, &rxbuf[r], rrem); -#endif - if (rx_ti == 0) { // wait for first message - rx_ti = get_now_ts(); - } - if (n > 0) { - r += n; - rrem -= n; - err = n; - } - } - long int rx_tf = get_now_ts(); - DEBUG_TEST("read=%d", r); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - // Compare RX and TX buffer and detect mismatches - bool match = true; - for (int i=0; i=0); - } - free(rxbuf); - free(txbuf); -} - - - - - -void tcp_client_sustained_6(TCP_UNIT_TEST_SIG_6) -{ - std::string testname = "tcp_client_sustained_6"; - std::string msg = "tcp_sustained_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "connect to remote host with IPv6 address, exchange a sequence of packets, check order.\n"); - int n=0, w=0, r=0, fd, err; - char *rxbuf = (char*)malloc(cnt*sizeof(char)); - char *txbuf = (char*)malloc(cnt*sizeof(char)); - generate_random_data(txbuf, cnt, 0, 9); - if ((fd = _SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0){ - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { - DEBUG_ERROR("error connecting to remote host (%d)", err); - perror("connect"); - *passed = false; - return; - } - - if (op == TEST_OP_N_BYTES) { - int wrem = cnt, rrem = cnt; - // TX - long int tx_ti = get_now_ts(); - while (wrem) { - int next_write = std::min(4096, wrem); -#if defined(_WIN32) - n = _SEND(fd, &txbuf[w], next_write, 0); -#else - n = _WRITE(fd, &txbuf[w], next_write); -#endif - if (n > 0) { - w += n; - wrem -= n; - err = n; - } - } - long int tx_tf = get_now_ts(); - DEBUG_TEST("wrote=%d", w); - // RX - long int rx_ti = 0; - while (rrem) { -#if defined(_WIN32) - n = _RECV(fd, &rxbuf[r], rrem, 0); -#else - n = _READ(fd, &rxbuf[r], rrem); -#endif - if (rx_ti == 0) { // wait for first message - rx_ti = get_now_ts(); - } - if (n > 0) { - r += n; - rrem -= n; - err = n; - } - } - long int rx_tf = get_now_ts(); - DEBUG_TEST("read=%d", r); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - // Compare RX and TX buffer and detect mismatches - bool match = true; - for (int i=0; i=0); - } - free(rxbuf); - free(txbuf); -} - - - - - -void tcp_server_sustained_4(TCP_UNIT_TEST_SIG_4) -{ - std::string testname = "tcp_server_sustained_4"; - std::string msg = "tcp_sustained_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "accept connection from host with IPv4 address, exchange a sequence of packets, check order.\n"); - int n=0, w=0, r=0, fd, client_fd, err; - char *rxbuf = (char*)malloc(cnt*sizeof(char)); - memset(rxbuf, 0, cnt); - - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(*addr)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - if ((err = _LISTEN(fd, 1)) < 0) { - DEBUG_ERROR("error placing socket in _LISTENING state (%d)", err); - perror("listen"); - *passed = false; - return; - } - struct sockaddr_storage client; - struct sockaddr_in *in4 = (struct sockaddr_in*)&client; - socklen_t client_addrlen = sizeof(sockaddr_storage); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)in4, &client_addrlen)) < 0) { - fprintf(stderr,"error accepting connection (%d)\n", err); - perror("accept"); - } - DEBUG_TEST("accepted connection from %s, on port %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - if (op == TEST_OP_N_BYTES) { - int wrem = cnt, rrem = cnt; - long int rx_ti = 0; - while (rrem) { -#if defined(_WIN32) - n = _RECV(client_fd, &rxbuf[r], rrem, 0); -#else - n = _READ(client_fd, &rxbuf[r], rrem); -#endif - if (n > 0) { - if (rx_ti == 0) { // wait for first message - rx_ti = get_now_ts(); - } - r += n; - rrem -= n; - err = n; - DEBUG_TEST("read=%d, r=%d, rrem=%d", n, r, rrem); - } - } - long int rx_tf = get_now_ts(); - DEBUG_TEST("read=%d, writing next...", r); - long int tx_ti = get_now_ts(); - while (wrem) { - int next_write = std::min(1024, wrem); -#if defined(_WIN32) - n = _SEND(client_fd, &rxbuf[w], next_write, 0); -#else - n = _WRITE(client_fd, &rxbuf[w], next_write); -#endif - if (n > 0) { - w += n; - wrem -= n; - err = n; - } - } - long int tx_tf = get_now_ts(); - DEBUG_TEST("wrote=%d", w); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - err = _CLOSE(client_fd); - // Compute time deltas and transfer rates - float tx_dt = (tx_tf - tx_ti) / (float)1000; - float rx_dt = (rx_tf - rx_ti) / (float)1000; - float tx_rate = (float)cnt / (float)tx_dt; - float rx_rate = (float)cnt / (float)rx_dt; - sprintf(details, "%s, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - testname.c_str(), cnt, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); - *passed = (r == cnt && w == cnt && err>=0); - } - free(rxbuf); -} - - - - - -void tcp_server_sustained_6(TCP_UNIT_TEST_SIG_6) -{ - std::string testname = "tcp_server_sustained_6"; - std::string msg = "tcp_sustained_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "accept connection from host with IPv6 address, exchange a sequence of packets, check order.\n"); - int n=0, w=0, r=0, fd, client_fd, err; - char *rxbuf = (char*)malloc(cnt*sizeof(char)); - memset(rxbuf, 0, cnt); - - if ((fd = _SOCKET(AF_INET6, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - if ((err = _BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(struct sockaddr_in6)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - if ((err = _LISTEN(fd, 1)) < 0) { - DEBUG_ERROR("error placing socket in _LISTENING state (%d)", err); - perror("listen"); - *passed = false; - return; - } - struct sockaddr_in6 client; - socklen_t client_addrlen = sizeof(sockaddr_in6); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) { - fprintf(stderr,"error accepting connection (%d)\n", err); - perror("accept"); - *passed = false; - return; - } - char ipstr[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &client.sin6_addr, ipstr, sizeof ipstr); - DEBUG_TEST("accepted connection from %s, on port %d", ipstr, ntohs(client.sin6_port)); - - if (op == TEST_OP_N_BYTES) { - int wrem = cnt, rrem = cnt; - long int rx_ti = 0; - while (rrem) { -#if defined(_WIN32) - n = _RECV(client_fd, &rxbuf[r], rrem, 0); -#else - n = _READ(client_fd, &rxbuf[r], rrem); -#endif - if (n > 0) { - if (rx_ti == 0) { // wait for first message - rx_ti = get_now_ts(); - } - r += n; - rrem -= n; - err = n; - } - } - long int rx_tf = get_now_ts(); - DEBUG_TEST("read=%d", r); - long int tx_ti = get_now_ts(); - while (wrem) { - int next_write = std::min(1024, wrem); -#if defined(_WIN32) - n = _SEND(client_fd, &rxbuf[w], next_write, 0); -#else - n = _WRITE(client_fd, &rxbuf[w], next_write); -#endif - if (n > 0) { - w += n; - wrem -= n; - err = n; - } - } - long int tx_tf = get_now_ts(); - DEBUG_TEST("wrote=%d", w); - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - err = _CLOSE(client_fd); - // Compute time deltas and transfer rates - float tx_dt = (tx_tf - tx_ti) / (float)1000; - float rx_dt = (rx_tf - rx_ti) / (float)1000; - float tx_rate = (float)cnt / (float)tx_dt; - float rx_rate = (float)cnt / (float)rx_dt; - sprintf(details, "%s, n=%d, tx_dt=%.2f, rx_dt=%.2f, r=%d, w=%d, tx_rate=%.2f MB/s, rx_rate=%.2f MB/s", - testname.c_str(), cnt, tx_dt, rx_dt, r, w, (tx_rate / float(ONE_MEGABYTE) ), (rx_rate / float(ONE_MEGABYTE) )); - - *passed = (r == cnt && w == cnt && err>=0); - } - free(rxbuf); -} - - - - - -void udp_client_sustained_4(UDP_UNIT_TEST_SIG_4) -{ - std::string testname = "udp_client_sustained_4"; - std::string msg = "udp_sustained_4"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv4 address, TX n-datagrams\n"); - int w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - SetSocketBlockingEnabled(fd, true); - DEBUG_TEST("sending UDP packets until I get a single response..."); - if ((err = _BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - int num_to_send = 10; - for (int i=0; isin_addr), addrstr, INET_ADDRSTRLEN); - // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see - DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin_addr), ntohs(remote_addr->sin_port)); - } - sleep(ARTIFICIAL_SOCKET_LINGER); - //err = _CLOSE(fd); - DEBUG_TEST("%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); - sprintf(details, "%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); - DEBUG_TEST("Received : %s", rbuf); - *passed = (r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - - - - - -void udp_client_sustained_6(UDP_UNIT_TEST_SIG_6) -{ - std::string testname = "udp_client_sustained_6"; - std::string msg = "udp_sustained_6"; - fprintf(stderr, "\n\n%s (ts=%lu)\n", testname.c_str(), get_now_ts()); - fprintf(stderr, "bind to interface with IPv6 address, TX n-datagrams\n"); - int w, fd, err, len = strlen(msg.c_str()); - char rbuf[STR_SIZE]; - memset(rbuf, 0, sizeof rbuf); - if ((fd = _SOCKET(AF_INET6, SOCK_DGRAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - exit(0); - perror("socket"); - *passed = false; - return; - } - SetSocketBlockingEnabled(fd, true); - DEBUG_TEST("sending UDP packets until I get a single response..."); - if ((err = _BIND(fd, (struct sockaddr *)local_addr, sizeof(struct sockaddr_in6)) < 0)) { - DEBUG_ERROR("error binding to interface (%d)", err); - perror("bind"); - *passed = false; - return; - } - int num_to_send = 10; - for (int i=0; isin6_addr), addrstr, INET6_ADDRSTRLEN); - // once we receive a UDP packet, spend 10 seconds sending responses in the hopes that the client will see - //DEBUG_TEST("received DGRAM from %s : %d", inet_ntoa(in6->sin6_addr), ntohs(in6->sin6_port)); - //DEBUG_TEST("sending DGRAM(s) to %s : %d", inet_ntoa(remote_addr->sin6_addr), ntohs(remote_addr->sin6_port)); - } - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - DEBUG_TEST("%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); - sprintf(details, "%s, n=%d, err=%d, r=%d", testname.c_str(), cnt, err, r); - DEBUG_TEST("Received : %s", rbuf); - *passed = (r == len && !err) && !strcmp(rbuf, msg.c_str()); -} - -/****************************************************************************/ -/* PERFORMANCE (between library instances) */ -/****************************************************************************/ - -// Maintain transfer for cnt OR cnt -void tcp_client_perf_4(TCP_UNIT_TEST_SIG_4) -{ - fprintf(stderr, "\n\n\ntcp_client_perf_4\n"); - /* - int w=0, fd, err; - int total_test_sz = cnt; - int arbitrary_chunk_sz_max = MAX_RX_BUF_SZ; - int arbitrary_chunk_sz_min = 512; - - char rbuf[arbitrary_chunk_sz_max]; - - for (int i=arbitrary_chunk_sz_min; (i*2) < arbitrary_chunk_sz_max; i*=2) { - - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) - DEBUG_ERROR("error creating ZeroTier socket"); - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(addr))) < 0) - DEBUG_ERROR("error connecting to remote host (%d)", err); - - DEBUG_TEST("[TX] Testing (%d) byte chunks: ", i); - - int chunk_sz = i; - long int start_time = get_now_ts(); - w = 0; - - // TX - while (w < total_test_sz) - w += _WRITE(fd, rbuf, chunk_sz); - - long int end_time = get_now_ts(); - float ts_delta = (end_time - start_time) / (float)1000; - float rate = (float)total_test_sz / (float)ts_delta; - sprintf(details, "tot=%d, dt=%.2f, rate=%.2f MB/s", w, ts_delta, (rate / float(ONE_MEGABYTE) )); - _CLOSE(fd); - } - *passed = (w == total_test_sz && !err) ? TEST_PASSED : TEST_FAILED; - */ -} - -// Maintain transfer for cnt OR cnt -void tcp_server_perf_4(TCP_UNIT_TEST_SIG_4) -{ - fprintf(stderr, "\n\n\ntcp_server_perf_4\n"); - /* - int r=0, fd, client_fd, err; - int total_test_sz = cnt; - int arbitrary_chunk_sz_max = MAX_RX_BUF_SZ; - int arbitrary_chunk_sz_min = 512; - - char rbuf[arbitrary_chunk_sz_max]; - - for (int i=arbitrary_chunk_sz_min; (i*2) < arbitrary_chunk_sz_max; i*=2) { - DEBUG_ERROR("TESTING chunk size = %d", i); - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) - DEBUG_ERROR("error creating ZeroTier socket"); - if ((err = _BIND(fd, (struct sockaddr *)addr, (socklen_t)sizeof(struct sockaddr_in)) < 0)) - DEBUG_ERROR("error binding to interface (%d)", err); - if ((err = _LISTEN(fd, 1)) < 0) - DEBUG_ERROR("error placing socket in _LISTENING state (%d)", err); - if ((client_fd = _ACCEPT(fd, (struct sockaddr *)&addr, (socklen_t *)sizeof(addr))) < 0) - DEBUG_ERROR("error accepting connection (%d)", err); - - DEBUG_TEST("[RX] Testing (%d) byte chunks: ", i); - - int chunk_sz = i; - long int start_time = get_now_ts(); - r = 0; - - // RX - while (r < total_test_sz) - r += _READ(client_fd, rbuf, chunk_sz); - - long int end_time = get_now_ts(); - - float ts_delta = (end_time - start_time) / (float)1000; - float rate = (float)total_test_sz / (float)ts_delta; - - sprintf(details, "tot=%d, dt=%.2f, rate=%.2f MB/s", r, ts_delta, (rate / float(ONE_MEGABYTE) )); - - _CLOSE(fd); - _CLOSE(client_fd); - } - *passed = (r == total_test_sz && !err) ? TEST_PASSED : TEST_FAILED; - */ -} - - - - - -/****************************************************************************/ -/* PERFORMANCE (between library and native) */ -/****************************************************************************/ - -void tcp_perf_tx_echo_4(TCP_UNIT_TEST_SIG_4) -{ - std::string msg = "tcp_perf_tx_echo_4"; - fprintf(stderr, "\n\n%s\n\n", msg.c_str()); - - int err = 0; - int tot = 0; - int w = 0; - int fd, mode; - - char pbuf[64]; // test parameter buffer - char tbuf[MAX_TX_BUF_SZ]; - - mode = ECHOTEST_MODE_TX; - - // connect to remote echotest host - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - return; - } - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { - DEBUG_ERROR("error connecting to remote host (%d)", err); - return; - } - - DEBUG_TEST("copying test parameters to buffer"); - memset(pbuf, 0, sizeof pbuf); - memcpy(pbuf, &mode, sizeof mode); - memcpy(pbuf + sizeof mode, &cnt, sizeof cnt); - - DEBUG_TEST("sending test parameters to echotest"); -#if defined(_WIN32) - if ((w = _SEND(fd, pbuf, sizeof pbuf, 0)) < 0) { -#else - if ((w = _WRITE(fd, pbuf, sizeof pbuf)) < 0) { -#endif - DEBUG_ERROR("error while sending test parameters to echotest (err=%d)", w); - return; - } - - // begin - DEBUG_TEST("beginning test, sending test byte stream..."); - while (tot < cnt) { -#if defined(_WIN32) - if ((w = _SEND(fd, tbuf, sizeof tbuf, 0)) < 0) { -#else - if ((w = _WRITE(fd, tbuf, sizeof tbuf)) < 0) { -#endif - DEBUG_ERROR("error while sending test byte stream to echotest (err=%d)", w); - return; - } - tot += w; - DEBUG_TEST("tot=%d, sent=%d", tot, w); - } - // read results - memset(pbuf, 0, sizeof pbuf); - DEBUG_TEST("reading test results from echotest"); -#if defined(_WIN32) - if ((w = _RECV(fd, pbuf, sizeof tbuf, 0)) < 0) { -#else - if ((w = _READ(fd, pbuf, sizeof tbuf)) < 0) { -#endif - DEBUG_ERROR("error while reading results from echotest (err=%d)", w); - return; - } - - DEBUG_TEST("reading test results"); - long int start_time = 0, end_time = 0; - memcpy(&start_time, pbuf, sizeof start_time); - memcpy(&end_time, pbuf + sizeof start_time, sizeof end_time); - - float ts_delta = (end_time - start_time) / (float)1000; - float rate = (float)tot / (float)ts_delta; - sprintf(details, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); - - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - *passed = (tot == cnt && !err) ? TEST_PASSED : TEST_FAILED; -} - - -void tcp_perf_rx_echo_4(TCP_UNIT_TEST_SIG_4) -{ - std::string msg = "tcp_perf_rx_echo_4"; - fprintf(stderr, "\n\n%s\n\n", msg.c_str()); - - int err = 0; - int mode = 0; - int tot = 0; - int r = 0; - - char pbuf[64]; // test parameter buffer - char tbuf[MAX_TX_BUF_SZ]; - int fd; - - mode = ECHOTEST_MODE_RX; - - // connect to remote echotest host - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) { - DEBUG_ERROR("error creating ZeroTier socket"); - return; - } - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) { - DEBUG_ERROR("error connecting to remote host (%d)", err); - return; - } - - DEBUG_TEST("copying test parameters to buffer"); - memset(pbuf, 0, sizeof pbuf); - memcpy(pbuf, &mode, sizeof mode); - memcpy(pbuf + sizeof mode, &cnt, sizeof cnt); - - DEBUG_TEST("sending test parameters to echotest"); -#if defined(_WIN32) - if ((r = _SEND(fd, pbuf, sizeof pbuf, 0)) < 0) { -#else - if ((r = _WRITE(fd, pbuf, sizeof pbuf)) < 0) { -#endif - DEBUG_ERROR("error while sending test parameters to echotest (err=%d)", r); - return; - } - - // begin - DEBUG_TEST("beginning test, as soon as bytes are read we will start keeping time..."); -#if defined(_WIN32) - if ((r = _RECV(fd, tbuf, sizeof tbuf, 0)) < 0) { -#else - if ((r = _READ(fd, tbuf, sizeof tbuf)) < 0) { -#endif - DEBUG_ERROR("there was an error reading the test stream. aborting (err=%d, errno=%s)", r, strerror(errno)); - return; - } - - tot += r; - - long int start_time = get_now_ts(); - DEBUG_TEST("Received first set of bytes in test stream. now keeping time"); - - while (tot < cnt) { -#if defined(_WIN32) - if ((r = _RECV(fd, tbuf, sizeof tbuf, 0)) < 0) { -#else - if ((r = _READ(fd, tbuf, sizeof tbuf)) < 0) { -#endif - DEBUG_ERROR("there was an error reading the test stream. aborting (err=%d)", r); - return; - } - tot += r; - DEBUG_TEST("r=%d, tot=%d", r, tot); - } - long int end_time = get_now_ts(); - float ts_delta = (end_time - start_time) / (float)1000; - float rate = (float)tot / (float)ts_delta; - sprintf(details, "%s, tot=%d, dt=%.2f, rate=%.2f MB/s", msg.c_str(), tot, ts_delta, (rate / float(ONE_MEGABYTE) )); - - sleep(ARTIFICIAL_SOCKET_LINGER); - err = _CLOSE(fd); - *passed = (tot == cnt && !err) ? TEST_PASSED : TEST_FAILED; -} - -/****************************************************************************/ -/* OBSCURE API CALL TESTS */ -/****************************************************************************/ - -int obscure_api_test(bool *passed) -{ - int err = -1; - fprintf(stderr, "\n\nobscure API test\n\n"); - - /* - // --- - // getpeername() - int fd, client_fd; - - // after accept() - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) - DEBUG_ERROR("error creating ZeroTier socket"); - if ((err = _BIND(fd, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) < 0)) - DEBUG_ERROR("error binding to interface (%d)", err); - if ((err = _LISTEN(fd, 100)) < 0) - printf("error placing socket in _LISTENING state (%d)", err); - // accept - struct sockaddr_in client; - socklen_t client_addrlen = sizeof(sockaddr_in); - if ((client_fd = accept(fd, (struct sockaddr *)&client, &client_addrlen)) < 0) - fprintf(stderr,"error accepting connection (%d)\n", err); - fprintf(stderr, "accepted connection from %s, on port %d", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); - // getpeername - struct sockaddr_storage peer_addr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; - socklen_t peer_addrlen = sizeof(peer_addr); - _GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen); - DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - // compate getpeername() result to address returned by accept() - - // after connect - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) < 0) - DEBUG_ERROR("error creating ZeroTier socket"); - if ((err = _CONNECT(fd, (const struct sockaddr *)addr, sizeof(*addr))) < 0) - DEBUG_ERROR("error connecting to remote host (%d)", err); - // TODO: Put this test in the general API section - struct sockaddr_storage peer_addr; - struct sockaddr_in *in4 = (struct sockaddr_in*)&peer_addr; - socklen_t peer_addrlen = sizeof(peer_addr); - _GETPEERNAME(fd, (struct sockaddr*)&peer_addr, &peer_addrlen); - DEBUG_TEST("getpeername() => %s : %d", inet_ntoa(in4->sin_addr), ntohs(in4->sin_port)); - // compare result of getpeername to remote address - - // TODO: write an ipv6 version of the above ^^^ - */ -/* -int levels[] = { - IPPROTO_TCP, - IPPROTO_UDP, - IPPROTO_IP - }; - int num_levels = sizeof(levels) / sizeof(int); - - int optnames[] = { - TCP_NODELAY, - SO_LINGER - }; - int num_optnames = sizeof(optnames) / sizeof(int); - - - for (int i=0; i 0) { // TODO: what should be expected for each platform? Should this mirror them? - optval = 0; - DEBUG_TEST("setting level=%d, optname=%d, optval=%d...", level, optname, optval); - if ((err = _SETSOCKOPT(fd, level, optname, (char *) &optval, (socklen_t)sizeof(int))) < 0) { - DEBUG_ERROR("error while setting on socket"); - *passed = false; - err = -1; - } - else { - DEBUG_TEST("success"); - *passed = true; - } - } else { - DEBUG_ERROR("the optval wasn't set correctly"); - *passed = false; - err = -1; - } - } - } - */ - return err; -} - -/****************************************************************************/ -/* SLAM API (multiple of each api call and/or plausible call sequence) */ -/****************************************************************************/ - -#if defined(__SELFTEST__) - -int ZT_control_semantics_test(bool *passed) -{ - // TODO: Each discrete operation should be tested in random order among every other discrete operation for a sustained period - - /* - std::vector *zts_get_network_routes(char *nwid); - int zts_get_id_from_file(const char *filepath, char *devID); - void *zts_start_service(void *thread_id); - void disableTaps(); - void zts_get_address(const char *nwid, struct sockaddr_storage *addr, const size_t addrlen); - int zts_has_address(const char *nwid); - void zts_get_6plane_addr(struct sockaddr_storage *addr, const char *nwid, const char *devID); - void zts_get_rfc4193_addr(struct sockaddr_storage *addr, const char *nwid, const char *devID); - void zts_join(const uin64_t nwid); - void zts_leave(const uint64_t nwid); - int zts_running(); - int zts_start(const char *path); - int zts_start(const char *path, const char *nwid); - void zts_stop(); - void zts_get_homepath(char *homePath, size_t len); - int zts_get_id(char *devID); - unsigned long zts_get_peer_count(); - int zts_get_peer_address(char *peer, const char *devID); - */ - - //int n_times = 5; - char *nwid = (char*)"17d709436c2c5367"; - char *path = (char*)"fake_path"; -/* - // Perform operations on ZeroTier before calling zts_start(). Doing this makes absolutely no sense but could happen - zts_stop(); - zts_join(nwid); - zts_leave(nwid); - - DEBUG_TEST("---\n"); - sleep(1); - - // Perform operations on ZeroTier immediately upon startup, try to catch it with its pants down - // Ideally, the service wrapper should perform necessary checks to prevent any sort of issue - zts_start(path); - zts_join(nwid); - zts_leave(nwid); - zts_stop(); - - DEBUG_TEST("---\n"); - sleep(1); -*/ - zts_start(path, false); - zts_join(strtoull(nwid,NULL,16)); - zts_leave(strtoull(nwid,NULL,16)); - zts_stop(); - - DEBUG_TEST("---\n"); - sleep(1); -/* - // start the ZeroTier service many times - for (int i=0; i used_ports; - - for (int j=0; j 0) { - if ((err = _CLOSE(sock)) < 0) { - std::cout << "error closing socket (errno = " << strerror(errno) << ")" << std::endl; - //return -1; - } - } - } - } - used_ports.clear(); - //if (zts_num_active_virt_sockets() == 0) - // std::cout << "TEST_PASSED [slam open, bind, listen, accept, close]" << std::endl; - //else - // std::cout << "TEST_FAILED [slam open, bind, listen, accept, close]" << std::endl; - } - - // TESTS: - // (1) _SOCKET() - // (2) connect() - // (3) close() - int num_times = 3;//zts_maxsockets(SOCK_STREAM); - std::cout << "socket/connect/close - " << num_times << " times" << std::endl; - for (int i=0;i<(SLAM_NUMBER*SLAM_REPEAT); i++) { results[i] = 0; } - if (true) - { - int port = 4545; - - // open, bind, listen, accept, close - for (int j=0; j *routes = zts_get_network_routes(nwid); - - for (int i=0; isize(); i++) { - struct sockaddr_in *target = (struct sockaddr_in*)&(routes->at(i).target); - struct sockaddr_in *via = (struct sockaddr_in*)&(routes->at(i).via); - char target_str[INET6_ADDRSTRLEN]; - memset(target_str, 0, INET6_ADDRSTRLEN); - inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)target)->sin_addr.s_addr, target_str, INET_ADDRSTRLEN); - char via_str[INET6_ADDRSTRLEN]; - memset(via_str, 0, INET6_ADDRSTRLEN); - inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)via)->sin_addr.s_addr, via_str, INET_ADDRSTRLEN); - DEBUG_TEST("", target_str, via_str, routes->at(i).flags); - } -} -*/ - -/****************************************************************************/ -/* RANDOMIZED API TEST */ -/****************************************************************************/ - -int random_api_test() -{ - // TEST_PASSED implies we didn't segfault or hang anywhere - - // variables which will be populated with random values - /* - int socket_family; - int socket_type; - int protocol; - int fd; - int len; - int addrlen; - int flags; - - struct sockaddr_storage; - struct sockaddr_in addr; - struct sockaddr_in6 addr6; - */ - - /* - int num_operations = 100; - char *opbuf = (char*)malloc(num_operations*sizeof(char)); - generate_random_data(opbuf, num_operations, 0, 9); - for (int i=0; ifd; - struct sockaddr_in *remote_addr = fdp->remote_addr; - //fprintf(stderr, "fd=%d\n", fd); - int w = 0; - for (int i=0; isin_port++; - } -} - -void bind_to_localhost_test(int port) -{ - fprintf(stderr, "\n\nbind_to_localhost_test\n\n"); - int fd, err = 0; - // ipv4, 0.0.0.0 - struct sockaddr_storage bind_addr; - DEBUG_TEST("binding to 0.0.0.0"); - str2addr("0.0.0.0", port, 4, (struct sockaddr *)&bind_addr); - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) > 0) { - if ((err = _BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { - micro_sleep(100000); - if ((err = _CLOSE(fd)) < 0) { - DEBUG_ERROR("error closing socket (%d)", err); - } - } - else{ - DEBUG_ERROR("error binding to interface (%d)", err); - } - } - else { - DEBUG_ERROR("error creating socket (%d)", err); - } - - port++; - - /* - // ipv4, 127.0.0.1 - DEBUG_TEST("binding to 127.0.0.1"); - str2addr("127.0.0.1", port, 4, (struct sockaddr *)&bind_addr); - if ((fd = _SOCKET(AF_INET, SOCK_STREAM, 0)) > 0) { - if ((err = _BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { - micro_sleep(100000); - if ((err = _CLOSE(fd)) < 0) { - DEBUG_ERROR("error closing socket (%d)", err); - } - } - else{ - DEBUG_ERROR("error binding to interface (%d)", err); - } - } - else { - DEBUG_ERROR("error creating socket", err); - } - - port++; - */ - - // ipv6, [::] - DEBUG_TEST("binding to [::]"); - str2addr("::", port, 6, (struct sockaddr *)&bind_addr); - if ((fd = _SOCKET(AF_INET6, SOCK_STREAM, 0)) > 0) { - if ((err = _BIND(fd, (struct sockaddr *)&bind_addr, sizeof(struct sockaddr_in))) == 0) { - micro_sleep(100000); - if ((err = _CLOSE(fd)) < 0) { - DEBUG_ERROR("error closing socket (%d)", err); - } - } - else{ - DEBUG_ERROR("error binding to interface (%d)", err); - } - } - else { - DEBUG_ERROR("error creating socket (%d)", err); - } -} - -#endif // __SELFTEST__ - -int trigger_address_sanitizer() -{ - // Deliberately create a bad read to trigger address sanitizer - /* - int stack_array[100]; - stack_array[1] = 0; - return stack_array[1 + 100]; // BOOM - */ - return 0; -} - -/****************************************************************************/ -/* main(), calls test_driver(...) */ -/****************************************************************************/ - -int main(int argc , char *argv[]) -{ -#if defined(__SELFTEST__) - if (argc == 4) { - DEBUG_TEST("generating id..."); - if (!strcmp(argv[1],"generate_id")) - { - DEBUG_TEST("generating ZeroTier identity for testing purposes..."); - if (strlen(argv[2]) <= 0) { - DEBUG_ERROR("invalid was given"); - exit(-1); - } - if (strlen(argv[3]) <= 0) { - DEBUG_ERROR("invalid pathname was given"); - exit(-1); - } - uint64_t nwid = strtoull(argv[2],NULL,16); - uint64_t nodeId = zts_get_node_id(); - - zts_start(argv[3], true); - zts_join(nwid); - sleep(2); - DEBUG_TEST("generated id: %llx", (unsigned long long)nodeId); - exit(0); - } - } - -#endif // __SELFTEST__ - - if (argc < 6) { - fprintf(stderr, "usage: selftest to \n"); - fprintf(stderr, "usage: selftest generate_id \n"); - fprintf(stderr, "e.g. : selftest 3 test/test.conf alice to bob\n"); - return 1; - } - int num_repeats = atoi(argv[1]); - std::string path = argv[2]; - std::string from = argv[3]; - std::string to = argv[5]; - std::string me = from; - std::vector results; - std::string remote_echo_ipv4, smode; - - std::string nwidstr, stype; - std::string ipstr, ipstr6, local_ipstr, local_ipstr6, remote_ipstr, remote_ipstr6; - - int err = 0; - int mode = 0; - int port = 0; - int op = 0; - int start_port = 0; - int cnt = 0; - int ipv; - // for timing - // how long we expect the specific test to take - int subtest_expected_duration; - // (T+X), when we plan to start this test - int subtest_start_time_offset = 0; - - char details[128]; - memset(&details, 0, sizeof details); - bool passed = 0; - struct sockaddr_storage local_addr; - struct sockaddr_storage remote_addr; - - // load config file - if (path.find(".conf") == std::string::npos) { - fprintf(stderr, "Possibly invalid conf file. Exiting...\n"); - exit(0); - } - loadTestConfigFile(path); - // get origin details - local_ipstr = testConf[me + ".ipv4"]; - local_ipstr6 = testConf[me + ".ipv6"]; - nwidstr = testConf[me + ".nwid"]; - path = testConf[me + ".path"]; - stype = testConf[me + ".test"]; - smode = testConf[me + ".mode"]; - start_port = atoi(testConf[me + ".port"].c_str()); - remote_echo_ipv4 = testConf[to + ".echo_ipv4"]; - remote_ipstr = testConf[to + ".ipv4"]; - remote_ipstr6 = testConf[to + ".ipv6"]; - - if (strcmp(smode.c_str(), "server") == 0) - mode = TEST_MODE_SERVER; - else - mode = TEST_MODE_CLIENT; - - fprintf(stderr, "\n\nORIGIN:\n\n"); - fprintf(stderr, "\tlocal_ipstr = %s\n", local_ipstr.c_str()); - fprintf(stderr, "\tlocal_ipstr6 = %s\n", local_ipstr6.c_str()); - fprintf(stderr, "\tstart_port = %d\n", start_port); - fprintf(stderr, "\tpath = %s\n", path.c_str()); - fprintf(stderr, "\tnwid = %s\n", nwidstr.c_str()); - fprintf(stderr, "\ttype = %s\n\n", stype.c_str()); - fprintf(stderr, "DESTINATION:\n\n"); - fprintf(stderr, "\tremote_ipstr = %s\n", remote_ipstr.c_str()); - fprintf(stderr, "\tremote_ipstr6 = %s\n", remote_ipstr6.c_str()); - fprintf(stderr, "\tremote_echo_ipv4 = %s\n", remote_echo_ipv4.c_str()); - -#if defined(__SELFTEST__) - if (me != "dummy") { // used for testing ZT service wrapper API (before, during, and after coming online) - // set start time here since we need to wait for both libzt instances to be online - DEBUG_TEST("app-thread, waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - int portno = LIBZT_DEFAULT_PORT; - if (zts_set_service_port(portno) < 0) { - DEBUG_TEST("error, invalid zt service port number: %d", portno); - exit(0); - } - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - DEBUG_TEST("I am %llx, %s", (unsigned long long)nodeId, me.c_str()); - if (mode == TEST_MODE_SERVER) { - DEBUG_TEST("Ready. You should start selftest program on second host now...\n\n"); - } - if (mode == TEST_MODE_CLIENT) { - DEBUG_TEST("Ready. Contacting selftest program on first host.\n\n"); - } - } - - // SYNCHRONIZE test start times between multiple instances of the selftest on the network - ipv = 4; - int negotiation_port = start_port + 1000; - port = negotiation_port; - str2addr(local_ipstr, port, ipv, (struct sockaddr *)&local_addr); - str2addr(remote_ipstr, port, ipv, (struct sockaddr *)&remote_addr); - wait_until_everyone_is_ready((struct sockaddr *)&local_addr, (struct sockaddr *)&remote_addr, start_port); - DEBUG_TEST("both instances of selftest have started. beginning tests..."); - - long int selftest_start_time = get_now_ts(); - subtest_expected_duration = 5; -#endif // __SELFTEST__ - -for (int i=0; i - -#ifdef _WIN32 -#include -#include -#include -#include -#else -#include -#include -#include -#endif - -#include - -bool node_ready = false; -bool network_ready = false; - -void myZeroTierEventCallback(struct zts_callback_msg *msg) -{ - switch (msg->eventCode) - { - case ZTS_EVENT_NODE_ONLINE: - printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address); - node_ready = true; - break; - case ZTS_EVENT_NODE_OFFLINE: - printf("ZTS_EVENT_NODE_OFFLINE\n"); - node_ready = false; - break; - case ZTS_EVENT_NETWORK_READY_IP4: - printf("ZTS_EVENT_NETWORK_READY_IP4 --- network=%llx\n", msg->network->nwid); - network_ready = true; - break; - case ZTS_EVENT_PEER_P2P: - printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); - break; - case ZTS_EVENT_PEER_RELAY: - printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); - break; - // ... - default: - break; - } -} - -void delay(int n) -{ -#ifdef _WIN32 - Sleep(n * 1000); -#else - sleep(n); -#endif -} - -int main() -{ - char *str = "welcome to the machine"; - char *remoteIp = "11.7.7.223"; - int remotePort = 8082; - int fd, err = 0; - struct sockaddr_in addr; - addr.sin_family = ZTS_AF_INET; - addr.sin_addr.s_addr = inet_addr(remoteIp); - addr.sin_port = htons(remotePort); - - // Set up ZeroTier service and wai for callbacks - int port = 9994; - uint64_t nwid = 0x0123456789abcdef; - zts_start("path", &myZeroTierEventCallback, port); - printf("Waiting for node to come online...\n"); - while (!node_ready) { delay(1); } - printf("joining network...\n"); - zts_join(nwid); - printf("Joined virtual network. Requesting configuration...\n"); - while (!network_ready) { delay(1); } - - printf("I am %llx\n", zts_get_node_id()); - // Socket API example - if ((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating socket\n"); - } - if ((err = zts_connect(fd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) { - printf("error connecting to remote host (%s)\n", remoteIp); - } - if ((err = zts_write(fd, str, strlen(str))) < 0) { - printf("error writing to socket\n"); - } - zts_close(fd); - zts_stop(); - return 0; -} From 7c736e45af8dba811a77e25e652de664a9fdee55 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 10:48:53 -0700 Subject: [PATCH 38/78] Add updated examples --- CMakeLists.txt | 30 +-- examples/cpp/adhoc.cpp | 248 ++++++++++++++++++++++ examples/cpp/client.cpp | 288 +++++++++++++++++++++++++ examples/cpp/comprehensive.cpp | 373 +++++++++++++++++++++++++++++++++ examples/cpp/server.cpp | 299 ++++++++++++++++++++++++++ 5 files changed, 1216 insertions(+), 22 deletions(-) create mode 100644 examples/cpp/adhoc.cpp create mode 100644 examples/cpp/client.cpp create mode 100644 examples/cpp/comprehensive.cpp create mode 100644 examples/cpp/server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 920c9ae..27e50f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -540,28 +540,14 @@ endif () # ----------------------------------------------------------------------------- if (SHOULD_BUILD_TESTS) - # Minimal functional example - #add_executable (example ${PROJ_DIR}/test/example.cpp) - #target_link_libraries(example ${STATIC_LIB_NAME}) - - # API test - #add_executable (apitest ${PROJ_DIR}/test/apitest.cpp) - #target_link_libraries(apitest ${STATIC_LIB_NAME}) - - # Selftest - #add_executable (selftest ${PROJ_DIR}/test/selftest.cpp) - #target_link_libraries(selftest ${STATIC_LIB_NAME}) - #set_target_properties (selftest PROPERTIES COMPILE_FLAGS "-D__SELFTEST__") - - # client/server performance test - #add_executable (client ${PROJ_DIR}/test/client.cpp) - #target_link_libraries(client ${STATIC_LIB_NAME}) - #add_executable (server ${PROJ_DIR}/test/server.cpp) - #target_link_libraries(server ${STATIC_LIB_NAME}) - - # Simple Example - #add_executable (simple ${PROJ_DIR}/test/simple.cpp) - #target_link_libraries(simple ${STATIC_LIB_NAME}) + add_executable (adhoc ${PROJ_DIR}/examples/cpp/adhoc.cpp) + target_link_libraries(adhoc ${STATIC_LIB_NAME}) + add_executable (comprehensive ${PROJ_DIR}/examples/cpp/comprehensive.cpp) + target_link_libraries(comprehensive ${STATIC_LIB_NAME}) + add_executable (client ${PROJ_DIR}/examples/cpp/client.cpp) + target_link_libraries(client ${STATIC_LIB_NAME}) + add_executable (server ${PROJ_DIR}/examples/cpp/server.cpp) + target_link_libraries(server ${STATIC_LIB_NAME}) endif () # ----------------------------------------------------------------------------- diff --git a/examples/cpp/adhoc.cpp b/examples/cpp/adhoc.cpp new file mode 100644 index 0000000..989f498 --- /dev/null +++ b/examples/cpp/adhoc.cpp @@ -0,0 +1,248 @@ +/** + * libzt API example + * + * Pingable node joined to controller-less adhoc network with a 6PLANE addressing scheme + */ + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * [ 0] ZTS_ERR_OK - No error. + * [-1] ZTS_ERR - Error (see zts_errno for more information). + * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. + * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. + * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). + * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. + * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h + * and closely map to standard Linux error values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - Since libzt re-implements a socket API probably very similar to your host OS's own + * API 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, so here are a few + * guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * 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!): + * + * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) + * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) + * + * 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: + * + * struct zts_sockaddr_in6 in6; + * ... + * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); + */ + +#include +#include +#include +#include +#include +#include + +#include "ZeroTier.h" + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == 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->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Network stack events + if (msg->eventCode == ZTS_EVENT_NETIF_UP) { + printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + msg->netif->nwid, + msg->netif->mac, + msg->netif->mtu); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { + printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + msg->netif->nwid, + msg->netif->mac); + + networkReady = true; + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", + msg->addr->nwid, ipstr); + } + + // Peer events + // If you don't recognize the peer ID, don't panic, this is most likely one of our root servers + if (msg->eventCode == ZTS_EVENT_PEER_P2P) { + printf("ZTS_EVENT_PEER_P2P --- There is now a direct path to peer %llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to peer %llx\n", + msg->peer->address); + } +} + +/* + +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 != 5) { + printf("\nlibzt example\n"); + printf("server \n"); + exit(0); + } + int adhocStartPort = atoi(argv[2]); // Start of port range your application will use + int adhocEndPort = atoi(argv[3]); // End of port range your application will use + int ztServicePort = atoi(argv[4]); // Port the library uses to send encapsulated and encrypted UDP packets to peers + + uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); + int err = ZTS_ERR_OK; + + zts_set_network_caching(false); + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!nodeReady) { usleep(50000); } + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(adhoc_nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", adhoc_nwid); + while (!networkReady) { usleep(50000); } + + // Idle and just show callback events, stack statistics, etc + + printf("Node will now idle...\n"); + while (true) { sleep(1); } + + // Shut down service and stack threads + + zts_stop(); + return 0; +} diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp new file mode 100644 index 0000000..f23dffe --- /dev/null +++ b/examples/cpp/client.cpp @@ -0,0 +1,288 @@ +/** + * libzt API example + */ + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "ZeroTier.h" + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + networkReady = true; + } + if (msg->eventCode == 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->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + // Peer events + if (msg->eventCode == ZTS_EVENT_PEER_P2P) { + printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + // A direct path is known for nodeId + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); + // No direct path is known for nodeId + } +} + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * [ 0] ZTS_ERR_OK - No error. + * [-1] ZTS_ERR - Error (see zts_errno for more information). + * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. + * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. + * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). + * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. + * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h + * and closely map to standard Linux error values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - Since libzt re-implements a socket API probably very similar to your host OS's own + * API 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, so here are a few + * guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * 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!): + * + * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) + * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) + * + * 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: + * + * struct zts_sockaddr_in6 in6; + * ... + * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); + */ + +int main(int argc, char **argv) +{ + if (argc != 5) { + printf("\nlibzt example client\n"); + printf("client \n"); + exit(0); + } + uint64_t nwid = strtoull(argv[2],NULL,16); + std::string remoteAddr = argv[3]; + int remotePort = atoi(argv[4]); + int defaultServicePort = 9998; + + struct zts_sockaddr_in in4; + in4.sin_port = htons(remotePort); + in4.sin_addr.s_addr = inet_addr(remoteAddr.c_str()); + in4.sin_family = ZTS_AF_INET; + + // Bring up ZeroTier service and join network + + int err = ZTS_ERR_OK; + + if((err = zts_start(argv[1], &myZeroTierEventCallback, defaultServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!nodeReady) { usleep(50000); } + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!networkReady) { usleep(50000); } + + // Socket-like API example + + char *msgStr = (char*)"Welcome to the machine"; + int bytes=0, fd; + char recvBuf[128]; + memset(recvBuf, 0, sizeof(recvBuf)); + + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); + exit(1); + } + // Retries are often required since ZT uses transport-triggered links (explained above) + int delay = 1; // second + for (;;) { + printf("Connecting to remote host...\n"); + if ((err = zts_connect(fd, (const struct sockaddr *)&in4, sizeof(in4))) < 0) { + printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again in %ds\n", + fd, err, zts_errno, delay); + zts_close(fd); + printf("Creating socket...\n"); + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); + exit(1); + } + sleep(delay); + } + else { + printf("Connected.\n"); + break; + } + } + printf("Sending message string to server...\n"); + if((bytes = zts_write(fd, msgStr, strlen(msgStr))) < 0) { + printf("Error writing to socket (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 writing to socket (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno); + exit(1); + } + printf("Read %d bytes: %s\n", bytes, recvBuf); + printf("Closing socket\n"); + zts_close(fd); + printf("Shutting down service\n"); + zts_stop(); + return 0; +} diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp new file mode 100644 index 0000000..6e61bfc --- /dev/null +++ b/examples/cpp/comprehensive.cpp @@ -0,0 +1,373 @@ +/** + * libzt API example + * + * For more straight-to-the-point examples, see the other files in this same directory. + */ + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * [ 0] ZTS_ERR_OK - No error. + * [-1] ZTS_ERR - Error (see zts_errno for more information). + * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. + * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. + * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). + * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. + * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h + * and closely map to standard Linux error values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - Since libzt re-implements a socket API probably very similar to your host OS's own + * API 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, so here are a few + * guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * 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!): + * + * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) + * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) + * + * 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: + * + * struct zts_sockaddr_in6 in6; + * ... + * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); + */ + +#include +#include +#include +#include +#include +#include + +#include "ZeroTier.h" + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + networkReady = true; + } + if (msg->eventCode == 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->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Network stack events + if (msg->eventCode == ZTS_EVENT_NETIF_UP) { + printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + msg->netif->nwid, + msg->netif->mac, + msg->netif->mtu); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { + printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + msg->netif->nwid, + msg->netif->mac); + + networkReady = true; + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + + // Peer events + if (msg->eventCode == ZTS_EVENT_PEER_P2P) { + printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + // A direct path is known for nodeId + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); + // No direct path is known for nodeId + } +} + +void printPeerDetails(struct zts_peer_details *pd) +{ + printf("\npeer=%llx, latency=%d, version=%d.%d.%d, pathCount=%d\n", + pd->address, + pd->latency, + pd->versionMajor, + pd->versionMinor, + pd->versionRev, + pd->pathCount); + // Print all known paths for each peer + for (int j=0; jpathCount; j++) { + char ipstr[INET6_ADDRSTRLEN]; + int port = 0; + struct sockaddr *sa = (struct sockaddr *)&(pd->paths[j].address); + if (sa->sa_family == AF_INET) { + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa; + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + port = ntohs(in4->sin_port); + } + if (sa->sa_family == AF_INET6) { + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa; + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + } + printf("\tpath[%d]=%s, port=%d\n", j, ipstr, port); + } +} + +void getSinglePeerDetails(uint64_t peerId) +{ + struct zts_peer_details pd; + int err = zts_get_peer(&pd, peerId); + + if (err == ZTS_ERR_OK) { + printf("(%d) call succeeded\n", err); + } if (err == ZTS_ERR_INVALID_ARG) { + printf("(%d) invalid argument\n", err); + return; + } if (err == ZTS_ERR_SERVICE) { + printf("(%d) error: service is unavailable\n", err); + return; + } if (err == ZTS_ERR_INVALID_OP) { + printf("(%d) error: invalid API operation\n", err); + return; + } if (err == ZTS_ERR_NO_RESULT) { + printf("(%d) error: object or result not found\n", err); + return; + } + if (err == 0) { // ZTS_ERR_OK + printPeerDetails(&pd); + } +} + +// Similar to "zerotier-cli listpeers" +void getAllPeerDetails() +{ + struct zts_peer_details pd[128]; + /* This number should be large enough to handle the + expected number of peers. This call can also get + expensive for large numbers of peers. Consider using + get_peer(struct zts_peer_details *pds, uint64_t peerId) + instead */ + int num = 128; + int err; + if ((err = zts_get_peers(pd, &num)) < 0) { + printf("error (%d)\n", err); + return; + } + if (num) { + printf("num=%d\n", num); + for (int i=0; i \n"); + exit(0); + } + uint64_t nwid = strtoull(argv[2],NULL,16); + int ztServicePort = atoi(argv[3]); + + // Bring up ZeroTier service and join network + + int err = ZTS_ERR_OK; + + // Disable caching of network details in networks.d + // (read function documentation before disabling!) + // zts_set_network_caching(false) + + // Disable caching of peer details in peers.d + // (read function documentation before disabling!) + // zts_set_network_caching(false) + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!nodeReady) { usleep(50000); } + printf("This node ID is %lx\n", zts_get_node_id()); + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!networkReady) { usleep(50000); } + + // Get multiple peer's details + getAllPeerDetails(); + + // Get a single peer's details + uint64_t peerId = 0xabcdef1234; + getSinglePeerDetails(peerId); + int status = -1; + + // Get status of the node/service + status = zts_get_node_status(); + printf("zts_get_node_status()=%d\n", status); + + // Get status of a network + status = zts_get_network_status(nwid); + printf("zts_get_network_status()=%d\n", status); + + // Idle and just show callback events, stack statistics, etc + + while (true) { + usleep(50000); + status = zts_get_node_status(); + printf("zts_get_node_status()=%d\n", status); + display_stack_stats(); + } + + // Shut down service and stack threads + + zts_stop(); + return 0; +} diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp new file mode 100644 index 0000000..98f4d9f --- /dev/null +++ b/examples/cpp/server.cpp @@ -0,0 +1,299 @@ +/** + * libzt API example + */ + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "ZeroTier.h" + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + networkReady = true; + } + if (msg->eventCode == 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->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + // Peer events + if (msg->eventCode == ZTS_EVENT_PEER_P2P) { + printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + // A direct path is known for nodeId + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); + // No direct path is known for nodeId + } +} + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * [ 0] ZTS_ERR_OK - No error. + * [-1] ZTS_ERR - Error (see zts_errno for more information). + * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. + * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. + * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). + * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. + * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h + * and closely map to standard Linux error values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - Since libzt re-implements a socket API probably very similar to your host OS's own + * API 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, so here are a few + * guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * 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!): + * + * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) + * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) + * + * 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: + * + * struct zts_sockaddr_in6 in6; + * ... + * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); + */ + +int main(int argc, char **argv) +{ + if (argc != 5) { + printf("\nlibzt example server\n"); + printf("server <4|6> \n"); + exit(0); + } + uint64_t nwid = strtoull(argv[2],NULL,16); + int ipVersion = atoi(argv[3]); + int bindPort = atoi(argv[4]); + int defaultServicePort = 9998; + + struct zts_sockaddr_in in4, acc_in4; + if (ipVersion == 4) { + in4.sin_port = htons(bindPort); + in4.sin_addr.s_addr = INADDR_ANY; + in4.sin_family = ZTS_AF_INET; + } + + // Bring up ZeroTier service and join network + + int fd, accfd; + int err = ZTS_ERR_OK; + + if((err = zts_start(argv[1], &myZeroTierEventCallback, defaultServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!nodeReady) { usleep(50000); } + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!networkReady) { usleep(50000); } + + // Socket-like API example + + printf("Creating socket...\n"); + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, ret=%d, zts_errno=%d). Exiting.\n",fd, err, zts_errno); + exit(1); + } + printf("Binding...\n"); + if ((err = zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0)) { + printf("Error binding to interface (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 placing socket in LISTENING state (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); + exit(1); + } + + int bytes=0; + char recvBuf[128]; + memset(recvBuf, 0, sizeof(recvBuf)); + + while (true) { + socklen_t client_addrlen = sizeof(zts_sockaddr_in); + if ((accfd = zts_accept(fd, (struct sockaddr *)&acc_in4, &client_addrlen)) < 0) { + printf("Error accepting connection (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); + } + socklen_t peer_addrlen = sizeof(struct zts_sockaddr_storage); + zts_getpeername(accfd, (struct sockaddr*)&acc_in4, &peer_addrlen); + + char ipstr[INET_ADDRSTRLEN]; + memset(ipstr, 0, sizeof(ipstr)); + inet_ntop(AF_INET, &(acc_in4.sin_addr), ipstr, INET_ADDRSTRLEN); + printf("Accepted connection from %s:%d\n", ipstr, ntohs(acc_in4.sin_port)); + + printf("Reading message string from client...\n"); + if((bytes = zts_read(accfd, recvBuf, sizeof(recvBuf))) < 0) { + printf("Error writing to socket (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 writing to socket (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno); + exit(1); + } + printf("Sent %d bytes: %s\n", bytes, recvBuf); + printf("Closing connection socket\n"); + err = zts_close(accfd); + } + printf("Closing listen socket\n"); + err = zts_close(fd); + printf("Shutting down service\n"); + zts_stop(); + return 0; +} From 9eefbd55d346137814438ac47bbb00f508bc2013 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 11:35:43 -0700 Subject: [PATCH 39/78] Improve documentation and error reporting --- API.md | 443 ------------------------------------ README.md | 411 +++++++++++++++++++++++++++++---- include/ZeroTierConstants.h | 12 +- src/Sockets.cpp | 3 +- src/java/ZeroTier.java | 12 +- 5 files changed, 383 insertions(+), 498 deletions(-) delete mode 100644 API.md diff --git a/API.md b/API.md deleted file mode 100644 index 1cb842e..0000000 --- a/API.md +++ /dev/null @@ -1,443 +0,0 @@ -

- -
- ZeroTier SDK -

- -The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard [socket API](https://en.wikipedia.org/wiki/Berkeley_sockets) and simple network control API. The network stack and virtual link are exclusive to your app and traffic is fully encrypted end-to-end. - -ZeroTier is licensed under the BSL version 1.1. See LICENSE.txt and the ZeroTier pricing page for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. - -A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md] for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.). - -
- -# Getting started - -Before we dive into the technicals, the first thing to understand is that there are two API families to choose from and each is intended for a very different purpose: - -`libzt` Intended for convenience and simplicity, derives from [Berkley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). - - socket API: `zts_socket(), zts_connect(), zts_bind(), ...` - - control API: `zts_start(), zts_join(), zts_leave(), ....` - -`libztcore` Intended for raw performance. If your goal is simply moving frames as quickly as possible and you're willing to put in some extra work, is what you're looking. The API is described in `include/ZeroTierOne.h`. For an example of how this API is used, see the living documentation that is `src/Service.cpp`. - - core API: `ZT_VirtualNetworkFrameFunction(), ZT_WirePacketSendFunction(), ...` - -*NOTE: The remainder of this document will focus on the usage of the socket and control C API exposed in `include/ZeroTier.h` for `libzt`. For more information on the `libztcore` API see `include/ZeroTierOne.h`. We also provide bindings, frameworks, and packages for other languages and platforms in the `ports` directory and example applications using them in the `examples` directory.* - -
- -# Starting the service - -The next few sections explain how to use the control API. These functions are non-blocking and will return an error code specified in `include/ZeroTierConstants.h` and will result in the generation of callback events. It is your responsibility to handle these events. - -To start the service, simply call: - -`zts_start(char *path, void (*userCallbackFunc)(struct zts_callback_msg*), int port)` - -At this stage, if a cryptographic identity for this node does not already exist, it will generate a new one and store it on disk, 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. - -*NOTE: The first argument `path` is a path where you will allow 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):* - -``` -void myZeroTierEventCallback(struct zts_callback_msg *msg) -{ - if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { - printf("ZTS_EVENT_NODE_ONLINE, nodeId=%llx\n", msg->node->address); - // You can join networks now! - } - // ... -} -``` - -After calling `zts_start()` you will receive one or more of the following events: - -``` -ZTS_EVENT_NODE_OFFLINE -ZTS_EVENT_NODE_ONLINE -ZTS_EVENT_NODE_DOWN -ZTS_EVENT_NODE_IDENTITY_COLLISION -ZTS_EVENT_NODE_UNRECOVERABLE_ERROR -ZTS_EVENT_NODE_NORMAL_TERMINATION -``` - -After receiving `ZTS_EVENT_NODE_ONLINE` you will be allowed to join or leave networks. - -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, but the user-space network stack will continue operating in a headless hibernation mode. This is intended behavior due to the fact that the network stack we've chosen doesn't currently support the notion of shutdown since it was initially designed for embedded applications that are simply switched off. If you do need a way to shut everything down and free all resources you can call `zts_free()`, but please note that calling this function will prevent all subsequent `zts_start()` calls from succeeding and will require a full application restart if you want to run the service again. The events `ZTS_EVENT_NODE_ONLINE` and `ZTS_EVENT_NODE_OFFLINE` can be seen periodically throughout the lifetime of your application depending on the reliability of your underlying network link, these events are lagging indicators and are typically only triggered every thirty (30) seconds. - -Lastly, the function `zts_restart()` is provided as a way to restart the ZeroTier service along with all of its virtual interfaces. The network stack will remain online and undisturbed during this call. Note that this call will temporarily block until the service has fully shut down, then will return and you may then watch for the appropriate startup callbacks mentioned above. - -
- -# Joining a network - -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 be received before these calls will succeed. After calling `zts_join()` any one of the following events may be generated: - -``` -ZTS_EVENT_NETWORK_NOT_FOUND -ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQUESTING_CONFIG -ZTS_EVENT_NETWORK_OK -ZTS_EVENT_NETWORK_ACCESS_DENIED -ZTS_EVENT_NETWORK_READY_IP4 -ZTS_EVENT_NETWORK_READY_IP6 -ZTS_EVENT_NETWORK_DOWN -``` - -`ZTS_EVENT_NETWORK_READY_IP4` and `ZTS_EVENT_NETWORK_READY_IP6` are combinations of a few different events. They signal that the network was found, joined successfully, an IP address was assigned and the network stack's interface is ready to process traffic of the indicated type. After this point you should be able to communicate with peers on the network. - -
- -# Communicating with peers - -After successfully starting the service and joining a network, communicating with other nodes (peers) on that network is as easy as it would ordinarily be without ZeroTier. However, one thing to be aware of is the difference between relay and P2P modes. In the event that a direct connection cannot be established between your nodes, ZeroTier offers a free relaying service, this means that your nodes are reachable almost instantaneously but at a temporary performance cost. One should wait to send large amounts of traffic until a `ZTS_EVENT_PEER_P2P` is received for the node that you're interested in talking to. This event usually only takes a few seconds to appear after data has initially been sent. Similarly if after some time ZeroTier determines that a previously known path to one of your nodes is no longer available you will see a `ZTS_EVENT_PEER_RELAY` event. - -One can use `zts_get_peer_status(uint64_t peerId)` to query the current reachability state of another node. This function will actually **return** the previously mentioned event values, plus an additional one called `ZTS_EVENT_PEER_UNREACHABLE` if no known direct path exists between the calling node and the remote node. - -
- -# Handling events - -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/ZeroTier.h`: - -``` -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_physical_path *path; - struct zts_peer_details *peer; - struct zts_addr_details *addr; -}; -``` - -Here's an example of a callback function: - -``` -void myZeroTierEventCallback(struct zts_callback_msg *msg) -{ - if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { - printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address); - // You can join networks now! - } -} -``` - -In this callback function you can perform additional non-blocking API calls or other work. While not returning control to the service isn't forbidden (the event messages are generated by a separate thread) it is recommended that you return control as soon as possible as not returning will prevent the user application from receiving additional callback event messages which may be time-sensitive. - -
- -A typical ordering of messages may look like the following: - -``` -ZTS_EVENT_NETIF_UP --- network=a09acf023be465c1, mac=73b7abcfc207, mtu=10000 -ZTS_EVENT_ADDR_NEW_IP4 --- addr=11.7.7.184 (on network=a09acf023be465c1) -ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 (on network=a09acf023be465c1) -ZTS_EVENT_NODE_ONLINE, node=c4c7ba3cf -ZTS_EVENT_NETWORK_READY_IP4 --- network=a09acf023be465c1 -ZTS_EVENT_NETWORK_READY_IP6 --- network=a09acf023be465c1 -ZTS_EVENT_PEER_P2P --- node=74d0f5e89d -ZTS_EVENT_PEER_P2P --- node=9d219039f3 -ZTS_EVENT_PEER_P2P --- node=a09acf0233 -``` - -## Node Events - -These events pertain to the state of the current node. This message type will arrive with a `zts_node_details` object accessible via `msg->node`. Additionally, one can query the status of the node with `zts_get_node_status()`, this will **return** the status as an integer value only. - -``` -ZTS_EVENT_NODE_OFFLINE -ZTS_EVENT_NODE_ONLINE -ZTS_EVENT_NODE_DOWN -ZTS_EVENT_NODE_IDENTITY_COLLISION -ZTS_EVENT_NODE_UNRECOVERABLE_ERROR -ZTS_EVENT_NODE_NORMAL_TERMINATION -``` - - -## Network Events - -These events pertain to the state of the indicated network. This event type will arrive with a `zts_network_details` object accessible via `msg->network`. If for example you want to know the number of assigned routes for your network you can use `msg->network->num_routes`. Similarly for the MTU, use `msg->network->mtu`. Additionally, one can query the status of the network with `zts_get_network_status(uint64_t networkId)`, this will **return** the status as an integer value only. - -``` -ZTS_EVENT_NETWORK_NOT_FOUND -ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQUESTING_CONFIG -ZTS_EVENT_NETWORK_OK -ZTS_EVENT_NETWORK_ACCESS_DENIED -ZTS_EVENT_NETWORK_READY_IP4 -ZTS_EVENT_NETWORK_READY_IP6 -ZTS_EVENT_NETWORK_DOWN -``` - -## Peer Events - -These events are triggered when the reachability status of a peer has changed, this can happen at any time. This event type will arrive with a `zts_peer_details` object for additional context. Additionally, one can query the status of the network with `zts_get_peer_status(uint64_t peerId)`, this will **return** the status as an integer value only. - -``` -ZTS_EVENT_PEER_P2P -ZTS_EVENT_PEER_RELAY -ZTS_EVENT_PEER_UNREACHABLE -``` - -## Path Events - -These events are triggered when a direct path to a peer has been discovered or is now considered too old to be used. You will see these in conjunction with peer events. This event type will arrive with a `zts_physical_path` object for additional context. - -``` -ZTS_EVENT_PATH_DISCOVERED -ZTS_EVENT_PATH_ALIVE -ZTS_EVENT_PATH_DEAD -``` - -## Route Events - -This event type will arrive with a `zts_virtual_network_route` object for additional context. - -``` -ZTS_EVENT_ROUTE_ADDED -ZTS_EVENT_ROUTE_REMOVED -``` - -## Address Events - -These events are triggered when new addresses are assigned to the node on a particular virtual network. This event type will arrive with a `zts_addr_details` object for additional context. - -``` -ZTS_EVENT_ADDR_ADDED_IP4 -ZTS_EVENT_ADDR_REMOVED_IP4 -ZTS_EVENT_ADDR_ADDED_IP6 -ZTS_EVENT_ADDR_REMOVED_IP6 -``` -## Network Stack Events (debugging) - -These events aren't very important to the application developer but are important for debugging. These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with no additional contextual information. - -``` -ZTS_EVENT_STACK_UP -ZTS_EVENT_STACK_DOWN -``` - -## Netif Events (debugging) - -These events aren't very important to the application developer but are important for debugging. These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with a `zts_netif_details` object for additional context. - -``` -ZTS_EVENT_NETIF_UP -ZTS_EVENT_NETIF_DOWN -ZTS_EVENT_NETIF_REMOVED -ZTS_EVENT_NETIF_LINK_UP -ZTS_EVENT_NETIF_LINK_DOWN -``` -
- -# Errors - -Just as there are two APIs (socket and control), there are two sets of error codes. The control API (`zts_start()`, `zts_join()`, etc) errors defined in `include/ZeroTierConstants.h` are: - - - `ZTS_ERR_OK`: Everything is ok - - `ZTS_ERR_INVALID_ARG`: An argument provided by the user application is invalid (e.g. out of range, NULL, etc) - - `ZTS_ERR_SERVICE`: The service isn't initialized or is for some reason currently unavailable. Try again. - - `ZTS_ERR_INVALID_OP`: For some reason this API operation is not permitted or doesn't make sense at this time. - - `ZTS_ERR_NO_RESULT`: The call succeeded, but no object or relevant result was available - - `ZTS_ERR_GENERAL`: General internal failure (memory allocation, null reference, etc) - -The socket API error codes are defined in `doc/errno.h` - -*NOTE: For Android/Java (or similar) which use JNI, the socket API's error codes are negative values* - -*NOTE: For protocol-level errors (such as dropped packets) or internal network stack errors, see the section `Statistics`* - -
- -# Thread model - -The control API for `libzt` is thread safe and can be called at any time from any thread. There is a single internal lock guarding access to this API. The socket API is similar in this regard. 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.* - -
- -# Statistics - -Protocol and service statistics are available in debug builds of `libzt`. These statistics are detailed fully in the section of `include/ZeroTier.h` that is guarded by `LWIP_STATS`. The protocol constants are defined in `include/ZeroTierConstants.h`. An example usage is as follows: - -C++ example: -``` -struct zts_stats_proto stats; - -// Get count of received pings -if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_ICMP, &stats) == ZTS_ERR_OK) { - printf("icmp.recv=%d\n", stats.recv); -} - -// Get count of dropped TCP packets -if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_TCP, &stats) == ZTS_ERR_OK) { - printf("tcp.drop=%d\n", stats.drop); -} -``` - -Java Example: - -``` -import com.zerotier.libzt.ZeroTierProtoStats; - -... - -// Get received pings -ZeroTierProtoStats stats = new ZeroTierProtoStats(); -ZeroTier.get_protocol_stats(ZeroTier.STATS_PROTOCOL_ICMP, stats); -System.out.println("icmp.recv="+stats.recv); -``` - -
- -# Network Controller Mode - -The library form of ZeroTier can act as a network controller and in `libzt` this is controlled via the `zts_controller_*` API calls specified in `include/ZeroTier.h`. Currently controller mode is not available in the `iOS` and `macOS` framework builds. - -
- -# C Example - -``` -#include -#include -#include -#include - -#include "ZeroTier.h" - -bool node_ready = false; -bool network_ready = false; - -void myZeroTierEventCallback(struct zts_callback_msg *msg) -{ - switch (msg->eventCode) - { - case ZTS_EVENT_NODE_ONLINE: - printf("ZTS_EVENT_NODE_ONLINE, nodeId=%llx\n", msg->node->address); - node_ready = true; - break; - case ZTS_EVENT_NODE_OFFLINE: - printf("ZTS_EVENT_NODE_OFFLINE\n"); - node_ready = false; - break; - case ZTS_EVENT_NETWORK_READY_IP4: - printf("ZTS_EVENT_NETWORK_READY_IP4, networkId=%llx\n", msg->network->nwid); - network_ready = true; - break; - case ZTS_EVENT_PEER_P2P: - printf("ZTS_EVENT_PEER_P2P, nodeId=%llx\n", msg->peer->address); - break; - case ZTS_EVENT_PEER_RELAY: - printf("ZTS_EVENT_PEER_RELAY, nodeId=%llx\n", msg->peer->address); - break; - default: - break; - } -} - -int main() -{ - char *str = "welcome to the machine"; - char *remoteIp = "11.7.7.223"; - int remotePort = 8082; - int fd, err = 0; - struct zts_sockaddr_in addr; - addr.sin_family = ZTS_AF_INET; - addr.sin_addr.s_addr = inet_addr(remoteIp); - addr.sin_port = htons(remotePort); - - // Set up ZeroTier service and wait for callbacks - int port = 9994; - int nwid = 0x0123456789abcdef; - zts_start("test/path", &myZeroTierEventCallback, port); - printf("Waiting for node to come online...\n"); - while (!node_ready) { sleep(1); } - zts_join(nwid); - printf("Joined virtual network. Requesting configuration...\n"); - while (!network_ready) { sleep(1); } - - // Socket API example - if ((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) { - printf("error creating socket\n"); - } - if ((err = zts_connect(fd, (const struct sockaddr *)&addr, sizeof(addr))) < 0) { - printf("error connecting to remote host\n"); - } - if ((err = zts_write(fd, str, strlen(str))) < 0) { - printf("error writing to socket\n"); - } - zts_close(fd); - zts_stop(); - return 0; -} -``` - -
- -# Java Example - -Starting ZeroTier: - -``` -MyZeroTierEventListener listener = new MyZeroTierEventListener(); -ZeroTier.start(getApplicationContext().getFilesDir() + "/zerotier", listener, myPort); -// Wait for EVENT_NODE_ONLINE -while (listener.isOnline == false) { - try { - Thread.sleep(interval); - } catch (Exception e) { } -} -ZeroTier.join(myNetworkId); -// Wait for EVENT_NETWORK_READY_IP4/6 -while (listener.isNetworkReady == false) { - try { - Thread.sleep(interval); - } catch (Exception e) { } -} -// Now you can use the socket API! -``` - -An example event listener: - -``` -package com.example.exampleandroidapp; - -import com.zerotier.libzt.ZeroTier; -import com.zerotier.libzt.ZeroTierEventListener; -import com.zerotier.libzt.ZeroTierPeerDetails; - -public class MyZeroTierEventListener implements ZeroTierEventListener -{ - public void onZeroTierEvent(long id, int eventCode) - { - if (eventCode == ZeroTier.EVENT_NODE_ONLINE) { - System.out.println("EVENT_NODE_ONLINE: nodeId=" + Long.toHexString(ZeroTier.get_node_id())); - isOnline = true; - } - if (eventCode == ZeroTier.EVENT_NODE_OFFLINE) { - System.out.println("EVENT_NODE_OFFLINE"); - } - if (eventCode == ZeroTier.EVENT_NETWORK_READY_IP4) { - System.out.println("ZTS_EVENT_NETWORK_READY_IP4: nwid=" + Long.toHexString(id)); - if (id == myNetworkId) { - isNetworkReady = true; - } - } - if (eventCode == ZeroTier.EVENT_PEER_P2P) { - System.out.println("EVENT_PEER_P2P: id=" + Long.toHexString(id)); - } - if (eventCode == ZeroTier.EVENT_PEER_RELAY) { - System.out.println("EVENT_PEER_RELAY: id=" + Long.toHexString(id)); - } - // ... - } -} -``` diff --git a/README.md b/README.md index c74e2d4..63413cd 100644 --- a/README.md +++ b/README.md @@ -1,90 +1,413 @@ -# ZeroTier SDK (libzt, libztcore) -Library edition of [ZeroTier](https://github.com/zerotier/ZeroTierOne) -*** +--- +title: API +created: '2020-02-21T23:54:22.194Z' +modified: '2020-04-17T17:19:42.935Z' +--- - +

+ +
+ ZeroTier SDK +
+ Connect physical devices, virtual devices, and application instances as if everything was on a single LAN. +

-The ZeroTier SDK is composed of two libraries: `libztcore` which is the platform-agnostic network hypervisor, and `libzt` which is the network hypervisor paired with a userspace network stack. `libzt` is a superset of `libztcore` and is distinguished by the fact that it exposes a standard socket API and simple network control API. With these libraries the stack and virtual link are exclusive to your app and traffic is fully encrypted via the [Salsa20](https://en.wikipedia.org/wiki/Salsa20) cipher. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml) -*** +The ZeroTier SDK brings your network into userspace. We've paired our network hypervisor core with a network stack ([lwIP](https://savannah.nongnu.org/projects/lwip/)) to provide your application with an exclusive and private virtual network interface. All traffic on this interface is end-to-end encrypted between each peer and we provide an easy-to-use socket interface derived from [Berkeley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). Since we aren't using the kernel's network stack that means, no drivers, no root, and no host configuration requirements. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml). For troubleshooting advice see our [Knowledgebase](https://zerotier.atlassian.net/wiki/spaces/SD/overview). If you need further assistance, create an account at [my.zerotier.com](https://my.zerotier.com) and join our community of users and professionals. -
+# Usage -## Downloads / Installation +### Downloads: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) - Tarballs: - - - [libzt-release.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-release.tar.gz) // [libzt-debug.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-debug.tar.gz) // [libzt-source.tar.gz](https://download.zerotier.com/dist/sdk/libzt-1.3.0-source.tar.gz) - -Homebrew +### Homebrew ``` brew install libzt +clang++ -o yourApp yourApp.cpp -lzt; ./yourApp ``` -*** +### -
+### Building from source -## Example +To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `./lib`, test and example programs will be placed in `./bin`. - - Complete example: [test/simple.cpp](test/simple.cpp) - - Slightly more thorough example: [test/example.cpp](test/example.cpp) +``` +make update; make patch; make host +clang++ -o yourApp yourApp.cpp -L./lib/release/linux-x86_64/ -lzt; ./yourApp +``` + +Typical build output: + +``` +lib +├── release +| └── linux-x86_64 +| ├── libzt.a +| ├── libztcore.a +| └── libzt.so +└── debug + └── linux-x86_64 + ├── libzt.a + ├── libztcore.a + └── libzt.so +bin +└── release + └── linux-x86_64 + ├── adhoc + ├── client + ├── comprehensive + └── server +``` + +
+ +# Starting the service + +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** section and will result in the generation of callback events detailed in the **Event handling** section. It is your responsibility to handle these events. To start the service, simply call: + +`zts_start(char *path, void (*userCallbackFunc)(struct 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: ``` #include "ZeroTier.h" +... + +bool networkReady = false; + void myZeroTierEventCallback(struct zts_callback_msg *msg) { - switch (msg->eventCode) - { - // + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE, nodeId=%llx\n", msg->node->address); + networkReady = true; } + ... } int main() { - zts_start("yourConfig/key/path", &myZeroTierEventCallback, 9994); - zts_join(0x0123456789abcdef); - zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0); - zts_connect(fd, (const struct sockaddr *)&addr, sizeof(addr)); - zts_write(fd, "welcome to the machine", 22); - zts_close(fd); - zts_stop(); + zts_start("configPath", &myZeroTierEventCallback, 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; } -... ``` -After you've created a virtual network and added its `nwid` to the sample code, run: +For more complete examples see [examples/](./examples/) + +After calling `zts_start()` you will receive one or more of the following events: ``` -clang++ example.cpp -o example -lzt -./example +ZTS_EVENT_NODE_OFFLINE +ZTS_EVENT_NODE_ONLINE +ZTS_EVENT_NODE_DOWN +ZTS_EVENT_NODE_IDENTITY_COLLISION +ZTS_EVENT_NODE_UNRECOVERABLE_ERROR +ZTS_EVENT_NODE_NORMAL_TERMINATION ``` -The complete API specification can be found here: [API.md](API.md) +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, but 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. -## Build from source +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. -Build scripts use a combination of make, and cmake. To retrieve sources for all submodules, patch them, and build all targets (debug and release) for your host machine, issue the following: +
+ +# Joining a network + +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 be received before these calls will succeed. After calling `zts_join()` any one of the following events may be generated: ``` -make update -make patch -make all +ZTS_EVENT_NETWORK_NOT_FOUND +ZTS_EVENT_NETWORK_CLIENT_TOO_OLD +ZTS_EVENT_NETWORK_REQUESTING_CONFIG +ZTS_EVENT_NETWORK_OK +ZTS_EVENT_NETWORK_ACCESS_DENIED +ZTS_EVENT_NETWORK_READY_IP4 +ZTS_EVENT_NETWORK_READY_IP6 +ZTS_EVENT_NETWORK_DOWN ``` -All build targets can be seen by using `make list`. +`ZTS_EVENT_NETWORK_READY_IP4` and `ZTS_EVENT_NETWORK_READY_IP6` are combinations of a few different events. They signal that the network was found, joined successfully, an IP address was assigned and the network stack's interface is ready to process traffic of the indicated type. After this point you should be able to communicate with peers on the network. -Resultant libraries will be placed in `lib`, test and example programs will be placed in `bin`. +
-*** +# Connecting and communicating with peers -## Commercial License +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_P2P` has been received for the peer you are attempting to communicate with. All of the above is optional, but it will improve your experience. -If you want a commercial license to use the ZeroTier SDK in your product contact us directly via `contact@zerotier.com` +`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)`. + +One can use `zts_get_peer_status(uint64_t peerId)` to query the current reachability state of another peer. This function will actually **return** value of the previously observed callback event for the given peer, plus an additional possible value `ZTS_EVENT_PEER_UNREACHABLE` if no known path exists between the calling node and the remote node. + +
+ +# Event handling + +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/ZeroTier.h`: + +``` +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_physical_path *path; + struct zts_peer_details *peer; + struct zts_addr_details *addr; +}; +``` + +Here's an example of a callback function: + +``` +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE, node=%llx\n", msg->node->address); + // You can join networks now! + } +} +``` + +In this callback function you can perform additional non-blocking API calls or other work. While not returning control to the service isn't forbidden (the event messages are generated by a separate thread) it is recommended that you return control as soon as possible as not returning will prevent the user application from receiving additional callback event messages which may be time-sensitive. + +
+ +A typical ordering of messages may look like the following: + +``` +ZTS_EVENT_NETIF_UP --- network=a09acf023be465c1, mac=73b7abcfc207, mtu=10000 +ZTS_EVENT_ADDR_NEW_IP4 --- addr=11.7.7.184 (on network=a09acf023be465c1) +ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 (on network=a09acf023be465c1) +ZTS_EVENT_NODE_ONLINE, node=c4c7ba3cf +ZTS_EVENT_NETWORK_READY_IP4 --- network=a09acf023be465c1 +ZTS_EVENT_NETWORK_READY_IP6 --- network=a09acf023be465c1 +ZTS_EVENT_PEER_P2P --- node=74d0f5e89d +ZTS_EVENT_PEER_P2P --- node=9d219039f3 +ZTS_EVENT_PEER_P2P --- node=a09acf0233 +``` + +## Node Events + +These events pertain to the state of the current node. This message type will arrive with a `zts_node_details` object accessible via `msg->node`. Additionally, one can query the status of the node with `zts_get_node_status()`, this will **return** the status as an integer value only. + +``` +ZTS_EVENT_NODE_OFFLINE +ZTS_EVENT_NODE_ONLINE +ZTS_EVENT_NODE_DOWN +ZTS_EVENT_NODE_IDENTITY_COLLISION +ZTS_EVENT_NODE_UNRECOVERABLE_ERROR +ZTS_EVENT_NODE_NORMAL_TERMINATION +``` + + +## Network Events + +These events pertain to the state of the indicated network. This event type will arrive with a `zts_network_details` object accessible via `msg->network`. If for example you want to know the number of assigned routes for your network you can use `msg->network->num_routes`. Similarly for the MTU, use `msg->network->mtu`. Additionally, one can query the status of the network with `zts_get_network_status(uint64_t networkId)`, this will **return** the status as an integer value only. + +``` +ZTS_EVENT_NETWORK_NOT_FOUND +ZTS_EVENT_NETWORK_CLIENT_TOO_OLD +ZTS_EVENT_NETWORK_REQUESTING_CONFIG +ZTS_EVENT_NETWORK_OK +ZTS_EVENT_NETWORK_ACCESS_DENIED +ZTS_EVENT_NETWORK_READY_IP4 +ZTS_EVENT_NETWORK_READY_IP6 +ZTS_EVENT_NETWORK_DOWN +``` + +## Peer Events + +These events are triggered when the reachability status of a peer has changed, this can happen at any time. This event type will arrive with a `zts_peer_details` object for additional context. Additionally, one can query the status of the network with `zts_get_peer_status(uint64_t peerId)`, this will **return** the status as an integer value only. + +``` +ZTS_EVENT_PEER_P2P +ZTS_EVENT_PEER_RELAY +ZTS_EVENT_PEER_UNREACHABLE +``` + +## Path Events + +These events are triggered when a direct path to a peer has been discovered or is now considered too old to be used. You will see these in conjunction with peer events. This event type will arrive with a `zts_physical_path` object for additional context. + +``` +ZTS_EVENT_PATH_DISCOVERED +ZTS_EVENT_PATH_ALIVE +ZTS_EVENT_PATH_DEAD +``` + +## Route Events + +This event type will arrive with a `zts_virtual_network_route` object for additional context. + +``` +ZTS_EVENT_ROUTE_ADDED +ZTS_EVENT_ROUTE_REMOVED +``` + +## Address Events + +These events are triggered when new addresses are assigned to the node on a particular virtual network. This event type will arrive with a `zts_addr_details` object for additional context. + +``` +ZTS_EVENT_ADDR_ADDED_IP4 +ZTS_EVENT_ADDR_REMOVED_IP4 +ZTS_EVENT_ADDR_ADDED_IP6 +ZTS_EVENT_ADDR_REMOVED_IP6 +``` + +
+ +# Error handling + +Calling a `zts_*` function will result in one of the following return codes. Only when `ZTS_ERR` is returned will `zts_errno` be set. It's values closely mirror those used in standard socket interfaces and are defined in [ext/lwip/src/include/lwip/errno.h](ext/lwip/src/include/lwip/errno.h). + + - `ZTS_ERR_OK`: No error + - `ZTS_ERR`: Error (see `zts_errno`for more information) + - `ZTS_ERR_INVALID_ARG`: An argument provided is invalid. + - `ZTS_ERR_SERVICE`: ZT is not yet initialized. Try again. + - `ZTS_ERR_INVALID_OP`: Operation is not permitted (Doesn't make sense in this state). + - `ZTS_ERR_NO_RESULT`: Call succeeded but no result was available. Not necessarily an error. + - `ZTS_ERR_GENERAL`: General internal failure. Consider filing a bug report. + +*NOTE: For Android/Java (or similar) which use JNI, the socket API's error codes are negative values encoded in the return values of function calls* +*NOTE: For protocol-level errors (such as dropped packets) or internal network stack errors, see the section `Statistics`* + +# Common pitfalls + + - 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 a 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**. There are subtle difference you should be aware of. + - 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** section. + - See the **Debugging** section for more advice. + +
+ +# API compatibility with host OS + +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: +``` +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: +``` +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!): +``` +inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) +inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) +``` + +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: +``` +struct zts_sockaddr_in6 in6; + ... +inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); +``` + +# Thread model + +The control API for `libzt` is thread safe and can be called at any time from any thread. There is a single internal lock guarding access to this API. The socket API is similar in this regard. Callback events are generated by a separate thread and are independent from the rest of the API's internal locking mechanism. Not returning from a callback event won't impact the rest of the API but it will prevent your application from receiving future events so it is in your application's best interest to perform as little work as possible in the callback function and promptly return control back to ZeroTier. + +*Note: Internally, `libzt` will spawn a number of threads for various purposes: a thread for the core service, a thread for the network stack, a low priority thread to process callback events, and a thread for each network joined. The vast majority of work is performed by the core service and stack threads.* + +
+ +# Debugging + +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** +* **API compatibility with host OS** +* **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/ZeroTier.h` that is guarded by `LWIP_STATS`. The protocol constants are defined in `include/ZeroTierConstants.h`. An example usage is as follows: + + ``` + struct zts_stats_proto stats; + + // Get count of received pings + if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_ICMP, &stats) == ZTS_ERR_OK) { + printf("icmp.recv=%d\n", stats.recv); + } + + // Get count of dropped TCP packets + if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_TCP, &stats) == ZTS_ERR_OK) { + printf("tcp.drop=%d\n", stats.drop); + } + ``` + +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: + +**Network stack events** - These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with no additional contextual information. + +``` +ZTS_EVENT_STACK_UP +ZTS_EVENT_STACK_DOWN +``` + +**Netif events** - These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with a `zts_netif_details` object for additional context. + +``` +ZTS_EVENT_NETIF_UP +ZTS_EVENT_NETIF_DOWN +ZTS_EVENT_NETIF_REMOVED +ZTS_EVENT_NETIF_LINK_UP +ZTS_EVENT_NETIF_LINK_DOWN +``` + +
+ +# Network controller mode + +The library form of ZeroTier can act as a network controller and in `libzt` this is controlled via the `zts_controller_*` API calls specified in `include/ZeroTier.h`. Currently controller mode is not available in the `iOS` and `macOS` framework builds. + +# Multipath + +The multipath features available in ZeroTier haven't yet been exposed via the `libzt` API but this is coming in the version `2.X` series. + +
+ +# Licensing + +ZeroTier is licensed under the BSL version 1.1. See [LICENSE.txt](./LICENSE.txt) and the ZeroTier pricing page for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. + +A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md](ext/ZeroTierOne/AUTHORS.md) for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.). If you want a commercial license to use the ZeroTier SDK in your product contact us directly via [contact@zerotier.com](mailto:contact@zerotier.com) diff --git a/include/ZeroTierConstants.h b/include/ZeroTierConstants.h index d818dca..89d1fb2 100644 --- a/include/ZeroTierConstants.h +++ b/include/ZeroTierConstants.h @@ -37,16 +37,18 @@ extern int zts_errno; // No error. #define ZTS_ERR_OK 0 +// Error (see zts_errno for more info) +#define ZTS_ERR -1 // A argument provided is invalid (e.g. out of range, NULL, etc) -#define ZTS_ERR_INVALID_ARG -1 +#define ZTS_ERR_INVALID_ARG -2 // The service isn't initialized or is currently unavailable. Try again. -#define ZTS_ERR_SERVICE -2 +#define ZTS_ERR_SERVICE -3 // This API operation is not permitted or doesn't make sense at this time. -#define ZTS_ERR_INVALID_OP -3 +#define ZTS_ERR_INVALID_OP -4 // The call succeeded, but no object or relevant result was available. -#define ZTS_ERR_NO_RESULT -4 +#define ZTS_ERR_NO_RESULT -5 // General internal failure. Consider filing a bug report. -#define ZTS_ERR_GENERAL -5 +#define ZTS_ERR_GENERAL -6 /** * The system port upon which ZT traffic is sent and received diff --git a/src/Sockets.cpp b/src/Sockets.cpp index b64323f..9adc0c2 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -548,7 +548,8 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recvfrom( ssize_t zts_recvmsg(int fd, struct msghdr *msg, int flags) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : -1; // Not currently implemented by stack + // Not currently implemented by stack + return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : ZTS_ERR_GENERAL; } #ifdef SDK_JNI #endif diff --git a/src/java/ZeroTier.java b/src/java/ZeroTier.java index e057a47..a123910 100644 --- a/src/java/ZeroTier.java +++ b/src/java/ZeroTier.java @@ -36,16 +36,18 @@ public class ZeroTier // Everything is ok public static int ZTS_ERR_OK = 0; + // Error + public static int ZTS_ERR_OK = -1; // A argument provided by the user application is invalid (e.g. out of range, NULL, etc) - public static int ZTS_ERR_INVALID_ARG = -1; + public static int ZTS_ERR_INVALID_ARG = -2; // The service isn't initialized or is for some reason currently unavailable. Try again. - public static int ZTS_ERR_SERVICE = -2; + public static int ZTS_ERR_SERVICE = -3; // For some reason this API operation is not permitted or doesn't make sense at this time. - public static int ZTS_ERR_INVALID_OP = -3; + public static int ZTS_ERR_INVALID_OP = -4; // The call succeeded, but no object or relevant result was available - public static int ZTS_ERR_NO_RESULT = -4; + public static int ZTS_ERR_NO_RESULT = -5; // General internal failure - public static int ZTS_ERR_GENERAL = -5; + public static int ZTS_ERR_GENERAL = -6; ////////////////////////////////////////////////////////////////////////////// // Static initialization // From 907a2e84178f5c4bf41a627412f58a49f82922ed Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 11:37:46 -0700 Subject: [PATCH 40/78] Add Ad-hoc network ID convenience function to control interface --- include/ZeroTier.h | 25 +++++++++++++++++++++++++ src/Controls.cpp | 7 +++++++ 2 files changed, 32 insertions(+) diff --git a/include/ZeroTier.h b/include/ZeroTier.h index bf92169..60aa17e 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -707,6 +707,31 @@ ZT_SOCKET_API int ZTCALL zts_get_6plane_addr( ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); +/** + * 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. + * + */ +uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange); + /** * @brief Return the number of peers * diff --git a/src/Controls.cpp b/src/Controls.cpp index cc466b3..f3befdc 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -747,6 +747,13 @@ int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, con memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr)); } +uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange) +{ + char nwidStr[INET6_ADDRSTRLEN]; + sprintf(nwidStr, "ff%04x%04x000000", startPortOfRange, endPortOfRange); + return strtoull(nwidStr, NULL, 16); +} + ////////////////////////////////////////////////////////////////////////////// // Peers // ////////////////////////////////////////////////////////////////////////////// From b8849b9edeed705444ae3397db135fde2f17c6e0 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 11:47:03 -0700 Subject: [PATCH 41/78] Fix header section of README.md --- README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 63413cd..4fea069 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,13 @@ ---- -title: API -created: '2020-02-21T23:54:22.194Z' -modified: '2020-04-17T17:19:42.935Z' ---- - -

- -
- ZeroTier SDK -
- Connect physical devices, virtual devices, and application instances as if everything was on a single LAN. -

+# ZeroTier SDK +Connect physical devices, virtual devices, and application instances as if everything was on a single LAN. +*** + The ZeroTier SDK brings your network into userspace. We've paired our network hypervisor core with a network stack ([lwIP](https://savannah.nongnu.org/projects/lwip/)) to provide your application with an exclusive and private virtual network interface. All traffic on this interface is end-to-end encrypted between each peer and we provide an easy-to-use socket interface derived from [Berkeley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). Since we aren't using the kernel's network stack that means, no drivers, no root, and no host configuration requirements. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml). For troubleshooting advice see our [Knowledgebase](https://zerotier.atlassian.net/wiki/spaces/SD/overview). If you need further assistance, create an account at [my.zerotier.com](https://my.zerotier.com) and join our community of users and professionals. +*** + # Usage ### Downloads: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) From a35cd211e11fc789993e17cfc6d65206488143c8 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 18:03:39 -0700 Subject: [PATCH 42/78] Modify README.md for better exportability to PDF --- README.md | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4fea069..12f3126 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ # ZeroTier SDK -Connect physical devices, virtual devices, and application instances as if everything was on a single LAN. +Connect physical devices, virtual devices, and application instances as if everything is on a single LAN. *** - - The ZeroTier SDK brings your network into userspace. We've paired our network hypervisor core with a network stack ([lwIP](https://savannah.nongnu.org/projects/lwip/)) to provide your application with an exclusive and private virtual network interface. All traffic on this interface is end-to-end encrypted between each peer and we provide an easy-to-use socket interface derived from [Berkeley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). Since we aren't using the kernel's network stack that means, no drivers, no root, and no host configuration requirements. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml). For troubleshooting advice see our [Knowledgebase](https://zerotier.atlassian.net/wiki/spaces/SD/overview). If you need further assistance, create an account at [my.zerotier.com](https://my.zerotier.com) and join our community of users and professionals. *** +
+ # Usage -### Downloads: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) +### Downloads and examples: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) ### Homebrew @@ -23,11 +23,10 @@ clang++ -o yourApp yourApp.cpp -lzt; ./yourApp ### Building from source -To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `./lib`, test and example programs will be placed in `./bin`. +To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `./lib`, test and example programs will be placed in `./bin`: ``` make update; make patch; make host -clang++ -o yourApp yourApp.cpp -L./lib/release/linux-x86_64/ -lzt; ./yourApp ``` Typical build output: @@ -37,12 +36,10 @@ lib ├── release | └── linux-x86_64 | ├── libzt.a -| ├── libztcore.a | └── libzt.so └── debug └── linux-x86_64 ├── libzt.a - ├── libztcore.a └── libzt.so bin └── release @@ -53,6 +50,12 @@ bin └── server ``` +Example linking step: + +``` +clang++ -o yourApp yourApp.cpp -L./lib/release/linux-x86_64/ -lzt; ./yourApp +``` +
# Starting the service @@ -92,7 +95,9 @@ int main() ``` -For more complete examples see [examples/](./examples/) +For more complete examples see `./examples/` + +
After calling `zts_start()` you will receive one or more of the following events: @@ -185,7 +190,7 @@ A typical ordering of messages may look like the following: ``` ZTS_EVENT_NETIF_UP --- network=a09acf023be465c1, mac=73b7abcfc207, mtu=10000 ZTS_EVENT_ADDR_NEW_IP4 --- addr=11.7.7.184 (on network=a09acf023be465c1) -ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 (on network=a09acf023be465c1) +ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 ZTS_EVENT_NODE_ONLINE, node=c4c7ba3cf ZTS_EVENT_NETWORK_READY_IP4 --- network=a09acf023be465c1 ZTS_EVENT_NETWORK_READY_IP6 --- network=a09acf023be465c1 @@ -207,7 +212,6 @@ ZTS_EVENT_NODE_UNRECOVERABLE_ERROR ZTS_EVENT_NODE_NORMAL_TERMINATION ``` - ## Network Events These events pertain to the state of the indicated network. This event type will arrive with a `zts_network_details` object accessible via `msg->network`. If for example you want to know the number of assigned routes for your network you can use `msg->network->num_routes`. Similarly for the MTU, use `msg->network->mtu`. Additionally, one can query the status of the network with `zts_get_network_status(uint64_t networkId)`, this will **return** the status as an integer value only. @@ -223,6 +227,8 @@ ZTS_EVENT_NETWORK_READY_IP6 ZTS_EVENT_NETWORK_DOWN ``` +
+ ## Peer Events These events are triggered when the reachability status of a peer has changed, this can happen at any time. This event type will arrive with a `zts_peer_details` object for additional context. Additionally, one can query the status of the network with `zts_get_peer_status(uint64_t peerId)`, this will **return** the status as an integer value only. @@ -267,7 +273,7 @@ ZTS_EVENT_ADDR_REMOVED_IP6 # Error handling -Calling a `zts_*` function will result in one of the following return codes. Only when `ZTS_ERR` is returned will `zts_errno` be set. It's values closely mirror those used in standard socket interfaces and are defined in [ext/lwip/src/include/lwip/errno.h](ext/lwip/src/include/lwip/errno.h). +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 `./ext/lwip/src/include/lwip/errno.h`. - `ZTS_ERR_OK`: No error - `ZTS_ERR`: Error (see `zts_errno`for more information) @@ -280,17 +286,19 @@ Calling a `zts_*` function will result in one of the following return codes. Onl *NOTE: For Android/Java (or similar) which use JNI, the socket API's error codes are negative values encoded in the return values of function calls* *NOTE: For protocol-level errors (such as dropped packets) or internal network stack errors, see the section `Statistics`* +
+ # Common pitfalls - 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 a firewall/NAT settings. + - 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**. There are subtle difference you should be aware of. + - You should first read the section on **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: @@ -329,6 +337,8 @@ struct zts_sockaddr_in6 in6; inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); ``` +
+ # Thread model The control API for `libzt` is thread safe and can be called at any time from any thread. There is a single internal lock guarding access to this API. The socket API is similar in this regard. 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. @@ -369,6 +379,8 @@ If the information in those sections hasn't helped, there are a couple of ways t 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: +
+ **Network stack events** - These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with no additional contextual information. ``` From c794eb3514dbe76a5c7f58e2385147221d067d7e Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 18:04:22 -0700 Subject: [PATCH 43/78] Add README.pdf --- README.pdf | Bin 0 -> 262885 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README.pdf diff --git a/README.pdf b/README.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3548a3b829456816309b86a0e95b190e139fd491 GIT binary patch literal 262885 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�htEG1l&yui@Yv&ZqJ*C82g^Sf}>4^Y^lnB zT%M`)Ej*d$#C`pX`51Sqnx5VgPUY8Y{#m%I)icv+DP7Yt9(xh^^zeD`ZgTIj;4WR< zc3d1ac=;vV-8RP6(lg&0J~a4!@TI2@y?;es{Hq@b$yr+bWNNR(jVJ+gemQYyP?RmF zVcAS$v&0Df90{i|+G_L45MisHlP?m*RT;JAx9h?i9ttqU#R7q0<4Cn+bcXAh|2m-( z;P~7<=@9fXRJ77emetO^Z|o?|8QD|W2?J8HxRprEa0^SI3$RF=-9QdMrc7&)LL@O# z)z&jq28hbTipFhF7?$`9fHGoI9LVsGMPHGn&dg@byQyWh?P}3EYL)V7QL)!T-kaiP z-TG^KH;J4G1!c~=<;pgM1~C!JEuFqT(};|^I@H1<8huB{IpUQyIpSf#KnQ?QX(cmQ zOdh(B|Au#U-8KyW(kUMt*3vZGZ$A3-rQ1FF8an;iLuT{U z+Cmt&ZTTynEIKP^$L4Cj>#g&tX6?JxygNc|0%i z+p~MiH;a?F%P{gG0h6TrxcaUY^m#hs55ogPLw=7)Z9!PA&)*N-ES+@DCHAbe7)?E< zfJV{BqPUT*Xq%x2?x`_8mcnb7#i!#Rg=@=Bhv%8YkiY%^kZ=9(^0zG9>@5E!f2*hM_`^N# zpJzBf3oO?d&1nRtb$;V&0{jYmz9yW{j&$-3T5fVr^;D2MAIBj)SDDK82_N5~ZRT7w zSM=ey@bH*}5;2~I{vJRbzR2G`$ zoBpVH{+bC#N(e=c_rCK0ao}eyP6E$9R6J{iu!dU_L+3glcqkUgKq}Gnud7PRK(6hI zN9DX3K@nuNuzP@eE2?K-!YG~Hu=A3yFKKz5b{+e|-y*+yS)ELuf$THO8N3yra{NGnJ! zI`l9S>s30UqNpKF2#bEL8A>N_lmc{N@9au1lWOYCm%_cs%F^(jT#g>VM zV8B|!z+euYGC~){(j7*ESY;l<)*D5F&}0tn2Vf1{(1e>g#S!06;)ouXGKWuPG);1p zFc~vVaEckW46qjU>l8xJc8?;pL@S z!Wkz(;YJxTad*ZN3m~~N3=@YlwZ#(0+7BTq3TxV8?L(O&^~;howIECWtVcU>oLSv4 zvSAtu+of?VX>)s1HAhX8sBlQrxXGG|jpjNq`M~_B1`!Xbe?210I}f{1lfa z55zf;6mHn4n(=<<6TvkRx~J4uQl}zt_kFRruTGq>uXg58X{9@43l%6Rx1p+!s&z{m zMJY*7Y5Mxc!or$V;j8a8e2i_ZsAcw8{h@ZTcv-koMCr8d0`j8|R3j^x#<)- zvyv`fW(AiEm%XM)L=6{i&Oa?NqVOkg?EdEcg&CwA|8CLmTe5i82sC9X1kYc-FHLwn zH1PB7Bg9qV6pb!Dm2EgYAY^sR)_H>T86>XYSqCf4a^zYwa`hudAfJyq)(D#;CGI3x zO_7GYP5POnc{*gKdL5bfwWU{|C5L$vW81j+Kh&ccw~1yiPLdl&*go93?zVY&@~)!~ zIv!Vwt7@qjN5zgG6<^IvEqyrf7|r!!KE`FDw3$K2MV&OG>a zlA|^#Nj?uINmn{Xi4FO!m!q|5oyDD)?2M`JZ7Az3Anf!d_~bpjAkAJ*kdwIRPIPhj zsJzi^lF~t%0%9{@wFnJ}&fIL{tH%FMpP5*sC=B zX8%^e<6jn=d^yweLH?tr>{=*WMA*@MS#J@D_rn|EYmJC;))asLxYS?Pq_8xN>@l9l zDjeo-h=`x8yvmfi*6xFcEn;FN{-8~Q(r<0w9yN=fdb4Wgi)-|3efo76><71reSlDZ zxb?Tgl(lWJqKBNUh7qR<0bBE7kq&1deDVs$hgEFTG z+aVzP;;JrdXH8+($>`IAr5#R(yfv}j==U)mYP?Q*@D3)O! zjNUnki;9>nNKp900BRzM`rQ2m{NX}IUMc23+Cse(Qhv9TpV+aDh=L^23^_Oz3|R`T zGy&`syKo&m0-Z$!tJqbd^UwfShN#X1RlzS!Pj1;DgotugWd5)B&~H1P3AHBQqZ;Ou zSjz2BIV0r3*y$yx-|iXgD~Eq2t$eKRq5PnIIxKki2EceCvNVRmy6jAr(yJ4n-V{0= zOz=72{qc>9stL>yQL#L3{xKt!MJ0`)tL;ayjWp}YCHm42jfrLHg_`7)bbXGA6IYLE zQZZpcT{;%Lh>XLt#&#YiGasRc4U(tZu&5l3} zF-s?PICdHZCHS1lx(2pAey(}m5+yoW44T~Y4f3Ge*Fi^(z4g3ykM@uk>W*?pRXZqV zgLMZIOHtacXBSFKK-P%hG%M!Hz&1ulb!v_xb%&{Ul&g2Pc7B1gd!#hjweyKPG>2Py zCi)(fRjxX|W4b6QXFEdeu;=58(jkC}vsjXbQ70+*M$EW3GW6z zLp*5_%0gChO{)2;Hu^m350V=1y6S|sf#Br1rS~UYtz*XV|9$fQCh(slOs;9Gf)!o< zdanFj)SraZb*;7_e{rcGc{Pn^_usVjdX=**oj>Vnb||?8W6pmLY4nTm)gM4*&`pM- zTFX85MQ>kC0km~r>%Sj?tpw^7eZK1ScpKg0AMpQmX7}^t?>LWKfclkUw+ipJbUjaf z(gg7ad|S8=Sda!_;UC9CUR7^s(&@~~?lG*EH;8IG$mTwl45D}H>ipFx&jG7;*TS#q z3D|nyh`wV#;W^=ty(b7Cv)8CR)Q^4%#98*8;#a#3Da(jKVOW)#15#eH?IyNDS@|e! z)=}qrDSD}m)6yP6%ypq`K~OeFX#eOg+xJw`xaFm(e8IOry5Uf)&6M@=@#{|Bp18>w zG^j;9C=L#WnU7)-CkBw5nHJ3_9q&^bqnu0D>w_!y6|p7?Eyz$bI-CWjL|Xm; z*>A{ZyeY_Kgs{dzZiGI9PjMHAWeAmq3U9r%DXdXk3U_eFN2K?V`F!c1>4lDpT}S!g%7PfqlonJh~N7Wh|1-)EVwp-GRN& zk&hBL_CZI#`-Q((YQg(nhowK=API0N&pr4qqj8J z(DUs^uQX*%gsG5Rs%DJ!nv>9aT!oygpcS6q|boV;@P223_51bs-% zq#o*i2OaKMh|X{{UYh|t#lJv7dd9ccC zINbxvQVr|xn+b595`n*ScZMI~G4bQ{dqwGA2glZG#9NB|ZRvt8C{vJYE?s1F?n z$+Zf=&%52Gy#d^xt}x%}6VTy0UQds+i;jZzz1DipOb?d4AnTYq5OiJspJQ)`hJtxl zf+X)Bj*`e%{onP)gJ-Pk`nDr&?qHnBK?_}T$K5l8Y;V^8RV#a7-+;h)$^552aex<O z6opt?wv{5%{e4u`zf)Y%`*TVkzG-{7SvGr+&+vdlNiNa2O=Ex@Cq{}85c3^^ac zO7KZG1toLs1v`r*DLV6roEsUb8wn3M71s;q#gQ6D|7jDal!ReF7J@&R7=uumj6etzAf$cyjuToJ-#a=$ePzfg%5II&1L=@ z-NmPY!TSGV?=7I>*s}d$5(2?3SkT5LSU2wO?(Xi^SP1T}0fK9QKyXjcKnU(0EI=T* z6Z~tqxih(Q@0~Yq=FPmfzV%Ov)u&EZb)EX{UHj~PY742Nn4HZs>+lbP3>RZWY}=ag zpYvGq!rv2Zgmo4>d<^hEbQp~e=iU!}4i%X>Qjss0GDeMaQTVE+gVP+t&DJtYr?LKv z(l<^xiLqJq*JgAcwt;ew(c)OD9<@a|W-hUb3r%{!88EKhg1MU1@%9dsb z%RF17_>Li+VBFK52sX%Oo_Tuv;cGA?z2g@%?v~``x>tq8R!+k;Ky<>QHEO|VDfNg2S~+i4c_m3p6JgQ2e=e9 zy2#Xi4wg_+E6&C35mW$=e2gq?+JsWO(n45cgcL!e%F3(i&>dGQOATL=WiD&Xkt!-e zY7O7nWg5lvmQtpL)=CRIa_$lN)?Md;AVu8ZA_uY~Fl}xBswUj;?g-i_LX^9G+x7Z}0p45cynWfBJO2T3)t|H2yRPLDC!GncORkrg7 zvhQoQ2q{&e!_!QWTz%)Be{>M&^Z-h9(=M4Y?{31#CQaZw*w-B6f{fnmVl`Vsau0U9 z+%_{G?>BhU^<%9!f0PUTD(6}A+$7g(r`kP(2lRUEOo<~)fsBFKJ^M+G;hRd)d^&W; zs@;z=Wg;D1lgz%W%&zW!cppi_oN`7BLQS}qY8ZfOsXC&E;c=bMb4y7l6ZY{-uWr#e z3YZD#hSQ6egNmL;(&6ebmBt1=o5R;=F#B+1VbcyZ@=*f68&K+HgA4ei*&y0C-*oPw zZ2{Y;$RQ}z#Wz7`e5ey)iNrJ{oDIb7uZpDcfO(&Yv#wZ}0aq4OhsFN6ZXDVU$-B+n zOGZkMe){jh4#3#H`-^5WGR=HTJtySKf`>D4b$RfB4gWggpk|#oOKP<&~`phre z|II9YupZ9}ObHjkWQ+m+DL6)I(M%MxSgF zP-qko3ofW{LAnoP$Jwo%zS(Ipvh&$BXv?!thJEZ;uK&=P_MY3ijCw8O83MM#@P5|j8Ly=4_fjWKufD~PoosHr25o!_?(u8k>_sGn7f`T@{}&HQu-Z-r+Q*ywicvh_OXpH}+-r z)7u9ILR1@nFmwHsFy^041JBA1U}t6fkvyFl2)s)G&ko55|BoiS|1T53%SZ}~$|;L7 zfZf5rN&e2x@mDvrSU*67?QBYMyLS9Lk!PexrH}zB0&_8)oASR|?6Z11O{Yn=m5P*$^ z`5z{Vt)Zn2*q+PS-VSVN3>NrHAp&E2+usbA(bbG0 zphCM}%8ct*`YfTA-%L3arq+upVI&<#Kr?uo+;Keocz9x%h1t{Hm6&HtT39ecU(ukW zVaJDS)0;nTMxD%8qhb>5^Md=zljAxAUYMJqc&Fvo<@L58Q~-S&(^~k z-aeNF+&9$;%EA>x_jYf0$PU*Lj|Q;oAHb5UnX! zivAfK+_Sqjx6E9SsEn|Eyf-x8w-~aX{GohzDvmO2c~{)!no6wEGYenVq#H&eR|j=W zgyX^4HB0~b*zqY94JNPb!0LLw$YU*Quu^1GtF$54K z^4_VeJ9;v#l6gUv>jRzXr<=?Ge8SdA=Dd z_4OlW3ry%yT0C4=WI-=~GAU)y6ME{jh2wk~uWk{+QBj^xE7xr^-fusr0kE4sMTzlh^4 z3~&}T*S&->>U4VD)-x!Ux*I*oj@IeBrh_tehzG<^dqO2D^bCvHoPeCt*rbAx%0d9a z%;f;k&3ws`14v{t@>({pSqCxcybu|Q!QO)vE=b6JgkeF$@mVvSUcStBwA;W+N0nu`f8Tv zqYp!CftVSVvq-Pl*u4Kpm*L%L?KcpW#ZM0!z zb|z$I=M)Qn3NdSLNm;cg`O!1Ps30f1*El-LDUXJ0}lL>Q5t@(b^~? z=%~>a%IAAY*S*3Mi=9BeubSyts^1}96}(*we>m@tmq@R-N5J3SA2~XG6sc`EHAfo% zkSr5jka!A~6gO6fXUg}9A+esa(aFAX_gi)e%Gaqel$2U%l;kEE+5zr$T&}$(psMK5 zeu6#a!Jy?mOo|G{z7FafzqDq}u1q(9CkoIVtRz%LAk7LQ&m!6(W*UoG^?CLzyQLt2ntOw6aMjAtibmRuDTm zu}fefL+qPC?kp9L4ddxNf+E?Rf{qs?l_e|B`RP-1h%c8&w^n3BW~+b`cu!}w9R0hI zoSUPevt+(zs=-aFTnC0|(GD|mWqf}U_c(^lMztPguSRw`zP}Jlx|X<;D<>bjxF zhJOh8>2lrEsa+ZkRhyd=OLkyDX0rY z#(MQlRb#ltUi zajR=gR+dY+>pk7wB&lT)GuZW4Wh)H!qZ(;b({UwTOp}eDA5!Ni(NchhNf{-(D|bz> zjwNRCj!SJUIw8f7;9sX~&7n(LG`(}$%n-pQ7PFGBix}?tI&$LuT15@GJp(8z9&v*v40es(GP?)5Qx}i9^$okdp^Zbo#2-#LqnXFX6XP<~nT}5*(`eIm zt88o4PsWTpH?$=izp0=xuA6Gy70YP{JRoW|Fm-q$-Nf%p&+trFnzn4Wo5WMkb2BO2 z_6%XqnX_Fw>2)pQroxBJ5;4*E&lGFE$ZvXLr(2)1lMpwQR(pyI9U;`Z)Z2`HO5Z4mjRhZ1J)n7xVdpyx=o`vS|631787nX0vF-hI-ua zBhDM|=yP1ggVCAp*f-=dk;BPYChTqY_!0y9;dGFEP7&kws-7p zt_>j$sj|sVl@O@w7>+3?Cx1vrEcI>UR@&G$!k)=kQc3nng_)fKVon!K)d(rcAO`JsI!$$H0&+bn6sm(OVRo$d1DM-o)o`}S&{m?sp;$iFjS)dAM*=$GLPL9z@Wz~ySwHJ;e z)LOM&{5yoY-&_Jj)kk#-hiQy8x2ka_)YVk<6B`ZN74&o{F7>@~i&(<@)Zyr-d3!RT zIrKxLQ(Mmp@ir(!xITJ8k);oDdSo>$ewR9K-o<%4*clKt&*zq;U6npK_;S{y>A>s2 zlf?IS@A=K+huD;8zt_KiS8z%JQp@SCmXx!-ixZ^069A%CDLC01E1N=UOED^lhyfT? zAc`b_QPLK2pwN#`;UAxpkkV43?qG3cup!v=?o@GRz}>56B|bh#(JF{4ch@Jz0=O%h zqy*`KR6Y99Vgs=KXhD?uA1w~Roqm0H94FvTDZOg}nE;UOM;izL-UTAv_k#eyyL-8B zGXsD>u6lP}SyK~BLm_+jUn*s>aydMqZU`S;vu&Ev7aNv(oDTAF{jKKnJ=HFFL`(Fe9UtsrNLH)-8`&HC1 z-#-FBvoG@x5%^mKng1NY->seeuOawv5#K*E?LUt1Um5Zbr2(XH;-5%o=Kqj1_!Eiz zZ-?;ix&I$})>(cCkKb}C>z_i)awlMZEYkchPkjG&nEwe*W%)5<{uWNQKZo;9)c)Uq z^M62e{uy6F`dJ{V;;&i*3o8J!{W4FnumK<&r2Wr;{X@O{E!gaT4)&cG`F~*l0G<=T zat~Pcco!} z?Pmk9{!kLcAa6Y&+mGWp0Ic`3-9JN?KQv-E|CB6j|96a-f4eNd6HWh>sth@X4G4g2 z5K{c(@c*g>-dB890^C4QBA;AB| zruoN#|BHIa_J{KT@F#2WZ;|~EO$2`;kN$&bLXiEJB-6hjjoAKB8UcSYBmagf?Eg<3 z-K}{+%=TX__kT_t{pG2+&+G&F!M~9=KN+Eai~rsJ{|$NbKctKPR|5Z^kTvXoxHba( z$rk-vRR2SZBYz@m{_PX%KZD^P&xAi2oqvPj{p#!QIMexV{niDt5)Dy2r7cY$YrglV z0q)mkl>nf7`2)CLoKgZX-_H?%`;`GDz`Yd<`M4)0WRm`M9pQc-g5-}?UXDL}B7T2e zmV1p2aIfYTBKxn}!Jk~1{XaDI|0kK|A7_+bH7fRB zXm;+kZ>XyZt}sBOt_d{DqNzS?&ki(;sr>dqrLMC%*XI zGc9ZgHng!fzvCox$g38PzrFAs$N#vnjH#VD*y8Sm2RjplbN`;F3kNF@__L==^KE+|1IU=)YYac($H-ILDyY`S?&?h`rP(kR zg!>`B{1m*p%sN?fM&c4<@F;SbXm7raU|oNJs|`Q~$(h8n=8vT#y?FRt^~p;cNBNy9 z*ClJXZw!M{QDO7;@2^b*vArG~$3zHK?oRMN^m>PkwqgWKz|=cmm=4M2hdKU03YX;F zfcAyHMK_EQ+xjF3?(Ksc$))amC$6yO>=}QBsmE>Q&nUNE%C)S(1SZivUQp;Ck55Lv z`bxKC3r9~T-DC8XcqB&@?j#f0I8?27Vm3V#1b4!lH?2;|{POnVfV1_{4gII{%3hPk z!%xe}H~2Zo)o_+`OV0eRFr$Kt(5bq^E2HPwo)2JH`+U_^vPH7EXrp>)@iGGP2nk`m zNy_N)&|Hn7D1|($2TMdko^V^0zZ&V^c(mOk?|Bg!@O9NwOC7T0ySaTWx13$J#7` za)A>E;x^3tm&0QlZWc%f8;+}jZbou7i05&HSe4AfG}A*POt~ZM!@|RPe!T{L+2y%< z8!oq>^8ol_Zn>JrD5Z518%T*zjLEs>L%h*;Lgm@@>vox!%vITlZC_>@aYg`KI5l`R z7-|or4vYMeEv%ezat6@$xWN1u^sXJB&5k)uMAOiMsw6~2RfP0m=L0r0kELy}BF&(l zlAr{9*}wo(G-1~cqXKzdv0a7d{5c&NVvJ%1i7vd5#g15>KfPwJ!}P@UG_src%vY2J zdqRRws(MWXhHiQ~pBwpz_W>BL*)OQFp-$d9V=I$+s2TNCI@>DmQ$!0CZ}^4{ZjZ~x zs6h6$2=nBN- z=nJgj7$E138> zChueC6ZK<~Nxu^VDj}513Y>~5gE+d0(#Q7vBT2RIYTW8`>UeD#H`ulqxL(XH3p>v= zmaKboow>Qty9n$|<)8!bJE#4Hs3SSZ1>PcVUVP_j!ru|S%yAvcT^~W*5C-FNKmEw* zABP*mTB%*xT}f5x#)>B!BZdl`vSt|l3rskb;7o8UzUK$)cUf;JrJpTILd1}zhZ5LoNhB!m*yeVXdYYQi8086Ey{Pu*w zlwOeUEZfs>H=vYA;PG%m>>Tm1cEwjdUoJX@tqKBK!_Mt#N*m_YZ*A25Q6800*na0> z#_)7uYn6o4oYTpbyaMb7>>oHzAMA7!wRwm0MqOiUpq+9Jzr)LTEnKZLR;XF>bi&>5 zi-07g*teBaYVKE1_8g0w^{J&REj&pTcYLx!yXhIZfM`8s3@H z7jrpvt+ma0TO&_h6m?6zJl-{XHC?CeoPja9b~oLL1_Yv79~)n+#)%;hu%@03bK+55 zWn9Ct`DR(@Fg@X}@T{;GDrkY(nnXCp&sGFH85YVu$_d8zjouaI;(RuVNr%X)Xt|87 zsEoNLRSW+Pk(QzWegf~Z^M-Y(y(OSstGy<#uuRukYH{5Y`65BRoXc`8e2pq|zi}l- zyROE(=gY3BE+P)twPtI!1`lZ&Ow4R&@s`lO_U(>yBP?bMBfS4A)uR=9=+G3OI?JaY zRhd8VYe)O9b+&3m4{Aqyn}nU_Lhd+=McLgF=aCsa;N;TZ$Jd5hP%^wD7!@wU7S_zc0XGsfrL}~+IN^+UZ1rvO`1q-7bCf~u#e(~F7DW$P z{3$E1JsAw`*!f5})HOO!gj`p1wS^#4CC-PIjsYWQoPJWP6YmCN%c0M(n=ZXhpA?7j zQ%*SG*p44Ua(m!@N>%(iP|;V>N52hXf<>o%ZyPo1Pg)&;AGjR>I{mqxr4kZhnkG z@QNHX|8;m4+r$t93eRec`==73w)PN%z!g2}ky!)MM2igRjB&gI4r=()tblfrEr3eg zN}JVEo=nbsoVGXldQ|LD8!S}7kzU6Vw2s3&*8yj*=C=XwTr+B<&$(09&4I_|{v%}m zn)zJ51)zuwd==I8)0516xQpkP&q?T`{T0MC^u!C@uwUH_VX^sCbzpy*ELEGnxGm}s z&MN5mkX774)?HSC>_ntUYjb<6zC6aKXgHk|%m-Z%y}QEQIm|NO@Ki^29`P-STFfHG7)Zw%!;Zi`b-HzBN;z63QRQ>x)yv*LXc*fM=U_DDw7OhuY?FINSX7 zH0*f9e9IXuTvdlp(YkzJ`+1V=H0a$m>Eg#5LSBBRr$EHXkZTo=7Z-tYFYNKeM)E$t z!>+0K*_6~X#Bl}p^K0Hlyf3LfN;wqmj(uix))Ca5$hf90~T=suS@KlC9}pB%q> zr*wvj5<^V1w{U$hHxjqPG5@6Zc^ZH8!RW~=)F?u!ycV3i9kgRSjS)Nz7~TzVAeWw{ z&vxjk#f!Dm1xks6ZS?KpLE|;q{ZW$wQa^F`5WUDkr+6r45!#u81ox)!Li~o<&LE^r zn&nrtd~qMBC>_!Hl-=dpoK<<$KNR5Um44~M(~I9ncwzP35d% z!*O!Y@x>wkp1A*T8s`nEUaTWckhG=_ZISTH*S!vG4wiU~W|RkCL|_@smdU=)Bve=SrKb1ESeUF9+Q~W4QVuS;OB@hs4dXN z--|Bvd=0Ho$fmy$qQm%9`NRa6A*IYpVt)XmlAjD+)7FSgzs_lV^z$zBB*aWH&QB>@9_&5ZpjZKb3673qAft zxbPtM0_Dgc2SrZc2AeYz@x=SZ_*x1I?fDghVE9U&3=~JcBtxkuVw~%aj+8kfQYh%!u)Dm?M>cc%g_0OkR#lY48!0}nvg1*4 zP_CkCO)r}aT9|HakEdvaj>@=@tH`|bP@ll42o~Z|j=RR(aiw@+N4x&4!7`5kPCZqw z*EYKAStCG|mCB@&0DLIyh=(m*^Z6Kb;t(u}T1fGZ;AMkgD42f6>sa!AB<$w)vWnV-x!ts-SIOx3%@d1lvVaUE{ zdYp~_ODj=zy}FAkp(t9U%D@60+0^PxaC$+mX2K)q{sS97$^FSV6G(NI^imN?B~n>Y zXB`@|e91+yQXP!0V_HNQ^VkDPR~Wo~c14*!{}DOF^FyY%La&w=&pAfPb@Vs~_R$5A zzVCp`BL6L69vB02sLqtyj z@8&DxkR&idO({wVz#N@4$#AY9Nt0(Xl_ue0#*Wu8uEJe&S5K6}MyL}R*FRnll<|$H z@u8VR!W1UT(LjIl8m&L0cq8Y{36Kh>Fk&}9!7w7xvFKH<%#-B~g$aErR9NyRA-j65 zhcHeea0#4l^ISUv9`QtB5x}oPfn?WW?HJ)O!eWJF&k!xu=ZVbS`!xJy;cJ=~?GGiF z12xtnUHKBrSB0ar;s>3jcJ2e3ma)3D2sBtcQ36`_3g_GWRkdnxbilIIk zLL|$lu{Ip=77qmmdrXP}E25hdJv}L{=?kj~)>xPg3t!<&2EMWwXTgfZ<&23N3&==wchfySW}M;D&>loKGIFy$2)P}}{f zQ-vI_7T=2#h)qJ9Pep}#nfsA)Yaa>Skpw4O1SmL_-VSGbf;DE$SBHk-^qyZMwqGZ2 z0gUk)$=Jvy@xv+>d3*$m2PcgtVuy(hwNMC;Omr*{vOL!?L3{xGv_4H7Dt3uVup>@D zK7hH4MbOlk6dh4jCz3{W^qcf zUU-_7ECUs8vBXPw&NaYtk^2*5;8Hyt37>po2Re{a;wCYR;$eP5dh{!qYjg>=uMXlI z#{HdatIGxp`qGWC%{0!bU@S@eSD_T2;!}<)912Tu2<+6GS4kQ%7MJLIa?1s{K0A(D zW=LQQuZqN#zutP!8Ed}A(IvBgVlk3%v>Kqu-#)S%*ftklsbok#4yXF0F`S(oUS$?* z$J|5>miWc!w%H4hGNNbLl%xmG+rJgzb*CPOF51L=r&C1DfKzo3rc4kc6E^u~@Ub1E z__>OeN%dG_5ID#w=%#`CCCsS4>B=PO$UbX^N&A}f$No~`vN**&e#^4wS4pXHVV9}k zd7P6zC*G8Vd?1i($CovhhAH8_g$d!n`(*ogJ7RP)%MJ#v{`VAk)iL|=De`F{o9`n& z#h)}$DJQN(CcF&^JACx?5XoG_60|K9f;;&&0GHt-`ihW^1*u_~MscnRO7f0q;YiY| zsd8AJH9W~OgU7(+H-Tw3Y?L_>{e|6NUiy@ti~dR-uGR1f(VR~0;@2aceLM+l;+*1Y zNS*^N0wN4)xQNO}A$`kD8m=Z%Hqne~bw@LJl0nn{Sr{6`iu)B59q~P4-%^xMVT?>s ziQh=qbWsVW2bWt6DWkyGElWv;YKDZ;=^LfLHVoMw+K)?2200Iep)FdlMV>(J)pW5O0oQ_2O zJYSiq;8{HVO7GW{1{dm`<1oe7YD>8+U1?)Tf=2}U$q_G<`?)ts*R0(U7{uXDd1hAJ zUJ(?>uI9jbr+~(~Oa|He*SyrYGG+61$wvTD%7dlbU)FM#Mf~Z>HN<)YgXxIW$Ki&c z(Yc7LxL)C{)g)3VxuTj1-GnI=ZSMD|$x{k82#aY)QOSCTY|O|h>5#b7XXgbr8d|h$kY7dm@G8iW-oUy?AYB!35^I0c1{jhiD z5fx5RmBuUW`g40JRBUM&S>-#%5 zc_`+t-xE++ntvjq$csi>j{Yv$+K)ZTN+KMIoC zUo|6F(h;;nJ?)hgP8TTZoVqUqiaIoTO)g*VS zl_I2PmdV%b4wE#3Q08d(P<;w1X4^k?R&*V3YSk>}u@> zQ7*wwKlXdOYwg0J8L3jw z-Df^D+jEspXFb{{QGJWjtxh?d5c9eS$2Pq?LE@VcE;^t~1y0SGX5_+?Eas zhb}74(hwJRT3S3TR~u1pI9@dGl(u(GT{E6{p`vUNdAY2fk5QrY(_ek{+U?VCT}z+Y z;oiIOK3ejj^U%PX-=8I_Sq?0lo8o>~y^`GOA-n1*yEiN>e|1`Z#z*g#hH`C8dITvf zs?P6bW=VB>xVxzDU~#y)R^IA;v^=B#%4g@&U_iFTZ@?}6xZP5})qX5@N@fo=qse8a z4|PGKA?K_tf_P|uueom0b7|q3Jui3d>g3i9hzI-Prru2_P3yM%j0h`irjUX8`rMqV zu8feU^60&%e)GkUwmf~U_X|?4EF8^)DddQ32$m=Hcs0C!D!Ez;IqdDn4 zagoqlek;9VvJLwHqn)3-8Ow*x*)da2E*zXfhS-c{wbnIoEUv*;Q8cD}H={(H0!=865n zG5d*+IF->e{tJF3cwU!B#u6dk;789E-|{&GGZQg*izNMntg9OSjW6% zB~I6yO_wT!(fc6v#g}d9zO2vYFXq4Vu1J4Wk0K@*Nfyi!N$|b)rBXz1$e&$bxe329 zejE7E_fZ9P4u4M1Rro0DyR5L)@K>8q+6qye-Sk9g)CRD7D+ZCGX= zN0LFmHe^f4h>uTrGfxPcFmFdtF^4QtKx&EV z)@RIsWA=kdD0QxbX6l=@-h$0;-e`-iNI#lnD_AEhP(r%W#L<=r?^jT-y(XVmKdJI9 z9apUnxW_+Bw1NWNrf+Pk5}$0EdL*hDweM4r9`a_bcr)qt^$3F~SoE>0DEFy4X}03c z;|m<*{-<0;iY<$_hR#JW8-}t}ovrx8^S&$_Vo^@C8xeSq3P)XY`yS|{eAkIWnU&L6 zF^x?WRDzX!N^ZL$v*eGqEHhyY#X(^uuol^7`(UuhDFDqDtwHeRm^GJq#o)+gWik`- z$Axzyw;QKj7tkp3jj(PbNzWeqqb02*6yh*vFNnUfpYxK>o=63pJ z(hJ)jEa~BgzL#YiH?vp(&r71Q?97y|h4)jrd)*-?)zgEo;7|sV%}Cug!-fpkb53k= zBR6zv^zv%V$)s%YQxt8tQWPipm;<(Qv|Y@O*t2K&s?X69^L*Lf^j<}-p-I>>$JstN zdSJ%+Wh}Btu7EIQ<^@F&d;gOBx3GfgVhgf5*v>*g#G74V?1`%*@!8W?pR|~!b^=7! zCx?utBX9P*s`0k@t}g>hZ!)5)Z2=yhD>hUtJ{tTbjm~1X9RQX*s!bo>2HD$^0ix@b zrRB;`B-bAT;%~flb{rnN2pmM)bw^I`jxb!;!KWY+E!Tm#04`Oe*u4ne_a2@j?~%HW*5E&k324(v9CZcex&jpzU$yn$y*P&uqc#` zyqRcb?p#7LLSe#>(Y1uuTynb_^Nugps|GU%yD}oUa9mwo>)h&GI+}KPyj^$C@~(To z`n>Rr8Lg?OnPS5_vq&s4ySeSdD^-S;mOdA&3DR}#I8IvAPO{arGA}aL`52>f8RJrF zr@MkorM0LzP}`4?qCMcDE!>N91!2Oubx-@Puq*P5ye(vF{amosvrhoIr+J~HzQhAeTIJvUD7Z^gchcGMA2Aw;(L@=! z!Xdvg3rH1yGou_nvk);gHokDOrTS!E3|wjDRB4xI^bjU%=rf#dadSYxn~4r=GnFGY z2Akq&wUTH$Y^E6-MVZr7dV`87BejVD;*tXoR_<~$)sxAX3H*ZS(G3knwkEaA!(INTv1Hluc-c1(Q|}|D}MT&Ig9Xs4dMKo8-bT@b!Xh__RxQX{+Hl$P*3gSz$|c*8laob?}VKn^bYEiiH zpZTv|PNT$bcbh%n_id-Hcw7nXOZ<`O;g$0?U`gMg73vD<6lrrbrPADRS%W31Ko_s% zhmZ@VJ&1{!0SJk$q-OzfFn~ZHh&K_$Uj&j@h7;mi4g|3S8CW=2nC}8!?@od^k(h!2 z9QS@zKe?&g`Oo~u-R17sU(WdJT>|c5fVj~J89JNZ9q>0dtL$O~_HckeAS@|z_w`-W z4+wHUk|HYh;*uh=h7ORBVH3!mTY^3AyOlkhA$KonXJ&sFBdla7y&iI0o#=4d| z=V66kDSgW2XW4l`pxuLuH%C24T3^16q+HY%9I`-_NzQofO#A}&TyO*>#qh8lz08p; zg6Ye4fCUzZ{gLd%A_FCS&wfSu(_}swMKGc7)#`4lWyn}U1Q{RPxg1JQ$Wyt+WWSS& zg5vuL$fBZ9rcCJK;hDqBmPd!1 z9W4FYueVA9@TfdlRpw*1?eHcxau&Wq_6_V^3I?>+T(1{p z0)yFZ>szdx-Ik7sI7Kc%iXescBNHRQ9{to0=5_q(u*23#w{G&)P_?~o|l6io#Ni3s6F^6tDcui?y6qb&Jy;> zW7|8@>gP%Leo~VCzsHsHCr9SLcRwJu-|(QUg0xc?6WrW7W{Uvmlds=V{@6Ft=G(gO z3F_v?IRJr&>4;bkiBCwCpxgz|J09e;JDwA!^g26S`^X1T+VnEAESId|B<%MlGc#A2 zX!`JcA0?OR^%7Y@R32`_i7`Xlo~TXgj@3%W^^_7Y6i(32c)w^J_Ic_`Gemih5Z44Z z?=8|nAXPRl-Yz~*m=+LwDdVNe@Ma9pipgo8nY2lLt+VnfqQh4L4I< z5%4oBtL%BO9|ke2E>$-&W4j9rPt=_@M8+x<%cER~ph}cl2W*83>&rK1Dk5)uGgjv6 z(y9qgv*J6e)HMj4Xvvt7>2nxv=Z$jtZbS5LWp4%5zVo^0A7J`Z2+`lel#S&#WlB|5 zq^t0OaG!^0zy2KzTH1I!ywxZWH{n|a*MMzmeY>q8VM>!cR@M&kI^<=Jc_n*13lS=C*$uNPmnz} zdA0yU9~V=ulH4bXhG;jI2SQj5n49W(FY~8QxwkqSM8ZRl8{doMBNU9)=QhK*_vh?R?&v~AF5(&@Oc)d|5Ds9_vW@HF)^ukv(x_{B{H-4?txVGW>T|-D+q7}L z*IX3NAC!^c{K+%+?~Mq=3Hn(C>ncgRb^?&*oDuW^9yieIlKT2MW0&^UNUxYOkgEif z3gBtD0kL2X301!aRf7OV+Kv=izeSU@d zJ9>rICKZPjcJ{JaL0-ZEoPLHB&X)2-#9(rFgGGVS>Lz|bK)MPJ-htye|)p>j(fu}E0N z{vQzkr)l@^LCyIa*I(TMAVM1xT+s5ovb&!^7Eo+YSt{i8##$DHS>?4Sj%M@{HF^-{ zO|S3M0C@%X(_~OhXSISsd>ID5v0hQ(vsr$XEnK6tt~iwT-LemSN2BiEW1_@!41n}M z7~oIN%71TwAQq0_@Ivg6RUi}E$32g5PCHcTTy0GRQo1<=qKlAOJ^>jqc7+7AmG=5Q zTvJ3|@~hxiducJtQ(qe^DIea#$<^ZKu&X^z|4e`>6cSKL*C&XNbw+z_m6+*>QQwE@ zXk)Xdq4{uMep3!e=g?p=wG6$oCuq#%>iglWwxB7a`L-S1_EGyA1eVj)W7_L90(AFy zLVQ`TC&^@%Hr%A&sKm;vRVB&I z?GNDlZ&(!!%3^ZL^bi%<#l{d~Zv2fo@K=)o$if1N+xcNsa6oi2jUToIkcs8aZn(Ec zfRIP?Za8|{1O2R z`I(=L6G(QS`)+kh6R^cyhzDeh_oKTT;UE160`Hyg|LCh<`hcMOoIrnm03`xH07|KdN>0|c=#L$Lgd<_$5gexdHKJrMod(#*^hlD+6I;S~dxg8n8aT)pq|9#$^rGwXX z9+LY&@lJLlS|C9$o2!;8mtA~lF1Gl%>KBZZcch6Q39^tWb`TXyBfu?z93{r(GVdvM z)D_H0sokRsyk%)zDW6(PaSp$69XRAdlPa0b2pQq=Vv*N}^~vOqiAP0<4Ks>C7fJKW z=EiOU=Ji%CWRTT9TfJ;DaTXVCQ#;fU_0{(L{+Y)NwOU(1mEou<&ICW3sXm^!Z@}h* z{0CB|_Ks^W&NUn&E{oN~K7kF@V7KcyEVkI~ij>qsrlCDdj7rx)JlU5_mToOq_-T}C z#RR8NcEn?CghzrJFZ*v7Oh%(Wv6_B0I)7;$LbXNFx4=q@M8ia$yD4w2wOzTqzWnLk zMJCt@&czpvOE>JT8r`PWiv$Nt^>@)PpJ-#?kB6=Ym(;Ozbca7wb4*9XV5=wcnt`wC z>Tz4cnaH%(OFma34(iAJ~`kBxmU8DR~W|^|?SKg`kQ$qHRuc8S8 zy8}jhv`JpfrB(263X3jX68*Fq@mAXQ_Zgk)VPB+g6$zhTUqL@0TNwL;iT)p4wqRlX zjT*12{1=n-fqYn&?-9q#Ued-}Hs6$?qYgWAXxXucfTzw-9p9mtQB$O#UtIYcE9y*t zccY$e{Ic@&P5Ber?+$)lOg76jjs^$UyaYQeq~WR2K^DmM9;1AZ;`}W}P{1#kc3U6I zqa?X*4>9y-sGHxNMqP$Lq z@#6$sm|4Kr(;6aCEg_+9U0xl7VeYOB-6`lpsxKw!XQ^GMV)NqF!zt$jz9Qv5n-J8; zznN#M6ZLy=@Ho@M-u2d7zzsn!w&}T30IP&?Vl5UDD`#zXVz_L#Sw1iXZvAxb$>Pco zNl!;%pkUGlg%lC>dy&&%T0^vF8RPaDNaeiR7)tL}Klyw_Y0L-pv6fGiRgJ4&&0e7N zp}}xIT9i^Fs_nF%BHu`zK{cIPJ@^tBYsS!Gu=CLUmD|S=>n|?^3q<{y9$YsVdX8h` zw$qm=?Jk~carh%490`OxU@!UJ>n55#2U5NyeI0+p4Jp6a@>zLV%~*J9PR7lZXLhoR zoBGS{e#Njmt?fWaanANrnHnA^NHJof@|m&6^}?eJtueRWtJ4#|X9pnDof7a5h(;Ec z-)Q!-%Hma@1>oklFm(iIm7WYTGbH0Io-N{WS@0kt4+nNb!_7do!`XGj#rg@rw{A$e zHPAn~(X%|)xW=$!pEa^-BaB3)f7zfiW!|=qz}}Fs0*_7tSN9}~Vd=Flax)L%x43UA z*c27z*30m?;o28EZ@3n1v`uobp`+z8bmU}U-c>4+*jg+!7;Tq!s=)2F!6#4(l!>Lg ztW+Gb58l?aYYfuUd=x_~fImp9RY@w7-w`)t}(`b79Fxx}orlnQIMUBYE~ z$x_c#1CLKMqhfI%`4n3*kwch#8t0cVs~YIhqNqx~P^LJ_R4x$e=VhE7_mY6Z+BLom zt}8Jz}leSX`V? zt0w#Sttl8S@kzJw_W#G;TL#p%Wb2~A-4fj0f-PKv26qSqcY?dSI|O%k0t9ypPJ#ss z4nY$nxDz0}1=+hhq&fT2r~CFj_r1sb!K^7YYmTZ_v&Q!g!K!c#gb~~AR_-hnsBBWw zrt+gUoO+=#rnzjuoRy=L*YwV{q=7yc!%!L(iK7l`HwSsYs$u~r(t>qDcrth^Gsst< zc#x|CW<~%-e1R&Vy*Z&sS%t58lHLwvUj@Pb`JS8DsdXu#)xyb|>sQ{%jBlsMNPdbv zC-)722Vzvr9CyDZFT|or0MN~oPtUxbc99Re&H(^{JJ(uJ$VDe-+6BlylA3_;HzY9<2gtFrbmE-Kw5oKW8 z*8c{7it|`=ghfCa#@PoBsvJu!9pSMD2JSvi?-5)y>l*FF^%MeJAK3mzT@UDC;0#cw z6;)@AR^H_PDdJ*HgM_fr{y%>V~g;kY~meD zuyV6tg|_X8VoQJjB2Z5@^EKq%tmk^0Rp4$?oFx2ptQ`$W@VQ<%G_-Mprw&Dws=s5=X=Aox&AW|q;PD2(AZpzo!Cg3*e6GF(r!yrC`6({n_NM^xkJ8-)0 zof1Z66tyM1^wu7xJOC*1v-o4Dy_5s*JYn}p7%h6R9i;phdT;Ay>ekX!99T<|C6PEa z@O>MPY1uT!55^d$#<8#{E_-Fyuf6vxeHO)Z z7v(Rg+!(%s-ar0+bs+VveNUnL@}pa1&U00Ak4=KPkr(Dz)!N$K#4;a`pqw~Ywf(<; zgzRAC2enC#1T+@D>{?kf!6mAEs4A7`*KXwr($>EidINY1|coW)&Qw`M+aqWv!^YZCQE<{8pk*S{?w9mjZ1mQ8An@GLCcH_bd0t7E#8CP5=g%0`d~+V3>o z2X5f0cTdLQfC6>s>W^8%bqR8dOTI=3b7BRtSbX>=l|5ZY%N^z}Kbj~%ot8}z=sAcm zxjrxb0DJy^s~|A1R>Iw0e$2EGygr$pY@3#;WwdHN6-#OsmeOZYrNo!PX{@TPI9_5F ztN0Mx(R=}s+__-lEB2U2g@l2=G)4f{_(qAyv`JT`UHe|bUhl}RN!y3m>n>UsD;9~a zPD`zpzWJwbg^|S9U1Xs2eDDtH$}3cW7P6~Op+di6HVurp^60+K4dpFOvS=cR4c4El-C}C00av5(; zXxlwl*`#KEAhxzjY?pqaPlw(#?9GIQPS37;#eV4ML43oqCW`FwvosMsgfV21wUa$8 zJ$F56e5}L$uXf&>QAvmJLi*cZjek%MaCdOSYR#((Jnp!tRAehgect)3g|bG^nA*%> zmC7DyS1FtFBI4SvBkdI5*)%yYEf;Sy1ey*X?Q}cleFw3Sq9qc+xo|Ar{yx58R~kxH zZbE99F<`)vd*t&Lew|2ryEhWo=gExz*mi8l7m$@O#@X8tev3;c?dnl+MrDto%qept zs`1Q6rZmky#|sHmL%y9{HkS$lPi7Q#mG55}Y;S&C=u60(@a;_NM2D(RWYvS=IfkQq zRDoq)`bE;~W{e#Wa=x7TOP0D>D{hMqx_Bivpwna_;fNL@18UvrgR*aMWl$~VYr(~% z-3@4&%de}mB-+U`-AS8H2<_)-g?@NwSAojrrpSE$oH_c8GcHi8(r*bH3q8)bfPkeW9OM}#o*obYAe z^@%1kdC6)$@>k*oSlLB9VqFyp7mRV^Ce`cRE9lE3Bc6EnYCdB!(lZ}cH{2AsoX$2k zw&K1Xoq=|wRP$H$m0Wwp%DDn8VroncSK#t1BrB+ zrn_v>*kVpi;pU2z6f^<0h%T8?9Y&#Lu1e^yrF1Rx3JtZs=d7Y3~~KL zI1FrlSlFmnZ;$r0-V^e`9)sgR$~-)JceN~@!g;5jj_n9qe5_cvYa16!@dMeH%!|19 z?>_@n_t5lN(qH47m&oWfG3phQYgx&@H_JW~t=CC5$||_%-sKSg+$n>$m3`pyW&VQ7 z>7{=+wUzl1Z>ibBcJURcA*F8AeZu<%wf!sM0od>zxjA&u@;`uwoW`SQjnY8 zE1ixfy&v|G?Bv~}1ZGaab2uMog@#mJij*kbnoK(K&0Rgy=nTaX$_6jhX~xf>MKdJQ<5?C-~0_&mrB>1 zUd;jHe4K&V3@WhQCxTzhMgCF*40rSw#n=3>K+_4ZT@QaAl#V?_}H`qqWOOZ|i=FxLcD_citx+~#LQ6+*UOdWQKn#5)k`1p?5_z`&!7K->yI zs1JYz+{QFFhh@A4md$Se-(12(Xm)$<*L?=yF(X(5*!v#;K;575HwrJR-@=yxSqPX{ zUL`*?NJJc))K5?}O8F>j)Z@_p_<>N+iAIC2eA2Jw?abotJqYR>C@jI%qrS;Gj8#ezdzOiDQPGK}tO#t`>!8R2HcM5CU6EL?fE*J~38>0#%Fkg8Hj z?>!V=+$IxmSsvbYxw z$8Z7%0qp-M+h2Z&{{iaa)>`ZD)CDlg{Xb7#08P1WVA7uy$iKo}{AXB%hdl+ToBTD= z@~~?Dm8$@N+sNuSRRCLaGcN9K!1Fijy1N^Y5xeP)|K7%WJ39dWWWau{1OTk~-dcZ7 zHNRf^U+;6=0_uN154`t3$>;o^IqM%!Wqke+W{^=3@K3aoQ4p9a=U-x1?|VN+;lFAF zqcHouJl`!Tcf;A;3}lX*Y5TSS>{E_EzD$O<3!qzs`t}sC+W-uIa)3A8^An1?JqHvU zfZ;ZP-A#=^(fyqHZqETl1Yr1ssW-e40f6BTg5L0^=>UdXYtNhegm3Pg{5* zX$0<1UX$K*30)5r>!`|u%vpVSFDhC)qp>@9zMLrlVkJz^mTUHfcE?n z{?G{oZem^XV@mz#V*0ZNw*$7=04B63bA&_9A_e-)4HG?~lvJ|st$JYR0Oe@6HJ7hS zE+(~-Rd4D7o*<$pg+9#~?OU1JD&L9nSA$*ANY_cHz_ViwYR1nR&z7GzZy)mMomca_ z1e-ix`|f@PmTNt${Eur`;0oel!<>=%7p281RZ31Qx{Cq2=>pBIjS5|Fkvl0l`T5Xw z8A9#Ycmp9EDM{ckC~^=cXBMO%0QpjuB--qf&7uFhYRyopYvvNmA>86#D69Zme!q|( zA11w&u1S1T@8US-FrMCPsG!#ph#aetzVctk;?3TaN{r5|RJ@E8L1*V1XRDu$1v8w& z+S#!?VWt|}(d95PSnIH8?~iEfp!T(75OGF-d#Q6&h^0wjM1=gfe|imIlOp8z5}KkA zjHtf~rp%U(Cm69Y1s4C@pl7uNr`pNayB6!M^T>FFTetaBzl=Z>@RJZrG@n6MH}k~w zxV#_xY^C2PJ!Wy5Xbq6=6W;?NPG*KX;!~8DsQf|*oo0w;Mo8sBT)OtoWjyBdbu~mw z)JN#osRYO_P~ar>Ftd*YSTjL*?N}5E16vcQghy1C;@`jWkQ@$dFS#<_Yna2`;YUhR zF~oeU5)L!7+6x(|!%46}!^s6L_r&Be+=kw{TjdajcztnCqGb+i`|0?`wpofvCzq-? zHTIl2uNLR{DS$SteLC_4`qdJQ-?tB4L_W&nYx2h$p(Qjwc9)X`xH2V4JeBjvR>lhz zaR5qJAB*LRMOKoZr@j=B6Eevw^lS8T-0^Q3$lr8(U*EEYh0$sVLyV{jBiD7frm7>B z;+pd!oOZZg)`Q~>QIJGtOdv?q4~Vsm=aDZ|aAys`cDT((U~A=c-^aOIt9-3ibgTMm z4H28SbY%Da?U*!W6jubYP2vUa>s6x=&IF|fH9`y}9^{ZUZ}SpqR9oRGR&`C~i_+&O z_Md#Gao#m?oj2BnvnbALl^Y?HiU&Lyeigl^|dJzmQ> zQRw7+^GC5foR3c60-OQ?4NugrA_RdAjOa4P<`<6s*37_IL2Yl>hrtOPGLs-tnW^&#PJ@9vtJ53Y zs_}U>QDvPY^5loHs1@+2w4SM?CH*Bz)JWP6Tv4l+7O+lqLi?#I8A0dA%y{qKhsv6N zmZieLNQ9pUe<55O-PdkWeA!=tJh;OtZX8wQk8Uoq>7+BqLrc082F+7#Alm(?z`5He zr}BwRH5;3zp=wZp=x!)V#cTCInT3V~EX(!GwPBT0b#k$}ECaa}4H{Y!me&q=fvb#D z?;T_=#z@H^qmKv=QZS*Kj|W+VF?!q#Voxe3rJ$o7=#0CH_0G5gsO_tbvL?9;-{Sf0 z+pM|uDb?qyTDfxJHfr|FoP;~P5>n^UN*nKqUkOd>rY~bpF>2gqdYyDyrs6J2kM{VP z`GLAsqv8IC&)xR4+?(b#liYpeh9BOWo4kjvrPAFQM~#>!wtT`6lRE9;mnYnQ*cl|B zF_~LkGj|M=E9!3Dw%Bk|kwi>my)Fs54}}a?#=$?~8K6rKGYVS9F-p}s>q#2)P3^u> z^I&}Q&X-myN-7%YCgOj?dytY{JEiX=CkqW-)qy4E&LCvSIQd|cNb-@gTWx}-fI?g1 z2BkuI`gSmRaN?8iFCN6$5)D^Ap~b+ZtFi{kNlo@q3z*c-XJS-agq{m(!;YYI!k{ba zVAvAKdjAZxOaQRmRBVMp`4OQkD@;l0LeMaU7M#U0OwpLgbtQB9N+YVs{t0VC?U$)U z8DEYwB@60LsfapCT6s3MMW2;&v$=z?HJ-$O9fYUe|ALu0fbnHr`ZTr#x^$cJ+^+88 z$VFo_=1Xb@isMN3T3~kmVN&7hyZtJ_e8{)D)`sQXGpuWPe#85~&D-pU4_KqHu-~!h zRaB4~#Ur|Lo@j29?2RB?a|Sw3(4Q{k^QV1c3oU$^)ead(RKix}@Pc6E>yhw?#|T=^ ze0XQ8|G*l_*&CE_OA=3El0yfiU9XH#7z1Ajxb&)d&V)eLByA;$8~}8r$c3h`JJaXJ zELNPOff_3QOh$sU1>Gti<*w%j0jPq9T~t!WX4k5fGKq9=PTFvtT|ASLh?5NSU0zJ- z)>!Jvi+I-Y+kunCiwo{g1S z<_blf(@>L&_K~A_J1vo`o`yIHwc2||vJGzfFL3Vk>QsgB)ijM(a3%E~utK;sL}D^8 z3l8n|Rbh$4h~F4GXTJJMQ(eZAEU?xsT@{;2(^H;<93Q}7niYy{ACa!E2t&| zW{O``IYNSca=pe`%x(2#VLW}>Caek~JM3}DYbhS71qP!Jy{S^zNnRP3JN!N$v3BL% zFEAB^zDbO;uYm$SaZ(< zibFY^Hahasw4)t-rTWi52!=mrX-shy)5?&7>bAk>AsseMKCHTpC};iB~7Kj zz*LBsQZK|1u;*;8O5!rgcLZ+|;L|D@qZ%{ZDR@sZ+~nk5>jKzg*o2i~A*fE#l=v23 zwwn87t2sU6+tBgFcc{Vp)EK6|R+h<@+z ziSpOS@y&V;rq3P;=^fJUMNea-ClB3Rymv1Y_hK^mh480#F z2nW~1DqdD*RA5j-6nenD3eS)1q$=nrEUaxZi7E)kDg8BM?cCeU*H5MEe%QWYIm%Kz znDKp{NfA*gLk4OAQ9~TqgXH-Eb~cI#MWrAL3jfUnrgH+ zSbNL!a+)RTrWa^J>g1YvZ6Ak4qC<0+d``f3`aq(P+%3?b{yx1W%|DLdy>d6E^0I%B zb&AqD$rqSOu;O*NogoG^`c%)g?`I-P=Tr+{kf^mBY3`HhFQE8eA^42!cg+dpB`koB zXF!@$oe*97aNk)|(SG0M+Pr;A#ss~WApoR@s2T#=4-#pKRC$vR?%f+&TEwNgX>6!g zI2lb0#7>^&B<+uB3ic3K*!fcs_^jSaRZH_Ep*}$gS@V(c7Qh8&rB|41JmqX$&IS&) zZ=xgjmMrpD&@xtvZT>ewOVy^#0);j(tkE`5dcI}Uo4;qc=# zY5&~;6mexi<-RAe9r1GS#2qS!f(>Jok;6_Etaq)oD&18<4NfXpEaqR9;?0zO{Q$9m zlaF#fHn+>NR^VR8%N}Nrcb|wK2>i3(;YftSDiGQT;lW*H_TdC8Ujv*nD6Of-s zHf0LNL=YGhtf5l~oB6C@{V(LhQp5-89V;n)0}~T@6i!-)t5>qtzM7bx8W#v-a1WJY z^xxC2UlY>)Ro4R*@L!iyQXc_`U_|hOBcBWQAYL9Mwl7`?%7qMG&Cc@?7CHr3!Y~vi zyNrPeQz|D55Jaj0=%f}vkvsxlXdfR&r=ENXpOPxKeDaE{THUUb?h*qdH#{%Qx>nc4 zuGm+7AQ7!C>FVkvHsRS9P$N1j`uke_K#M8HyUMeae1r8P0Q7)jH|rt}56qjjXZTPK z*MNsSyg1ZJ^X;m?E zA44WMv*2dEm!po)B_yqLU6tXnPZs(#XA#<6DAzd<0!sKjb4zy+GSCokde)ZBMlvkQ z(y>%*LS>;;;%O!DiU^q79u%r^BX6v1-Vgb_R*!^|e%m28y)|S=iX@jux`Wvby?#AB zXX8`Q+n|-lA&qoc)cf=aTeMegtz4ZUU2#48=c>_Fiz&8vk_o8Oy%&$U*4nAgQ8(!$ zR&B9UITn0mRtx40ZPLNDQi~VokQ!uGdhDOJ?0T2=48vZ229EXOypD^qktzp`oeK!u z5dTmADBE8?R{sG~3cv!4E%0|z3YY@>zd=gO@M0wMh4(Lax{-lqEgv-Io#|MC@nz38v^Ic^;*fYM-Qz0pbj zaRVkL|4;KeOboyK$}j<;$%iA@>g{-0rT=a z{OVt8Vqy{gC+}op75FFb`1N`Jj(sNfUt7S${?B-DF>!F*%P;&y?QaNxn^E=?^1dwr zN7zrO^JfW22LR`)pX|d;uK@rzK+o+tV2=TA{qAl{z#akIDy2V5!obp>#KDaOfj)eH z-Uoaepf}#%N5sfOkwcN23n*}T zrmkB#QRL&+hJEAxNX5{M2};-!9Ac8$9*^|oTn)1@jh>v^Y!h^9tuhMo(h+|$yGlTL z%{gsYokHQ!igCP$1mcw|Y~PO^^%_kkmRJ)*5(glpYwY6f+y_$il6LNKwgX&5#M_z7 zVB0V0(AWz51YN|TPo*Jh@`gWUjEdShG!5#uRN93X&|Qe@(xy7pv2~k)fQFhf+CSyf z))Te;RNkUs@j@74p+7TYi*yRJ6U7WLmBKk8SvjNB2a!ncoXkg5y^TX}wjO&xOkrZj z8PnT1QRCCA;^|PuJ08%{Z#_1<0q|gMd_5sEr~QaHhWCGS%*BBCB5nPtd()) z$t`gK$M81Aj`O*Mo!c*136);eg$3Z%kzt}LNTXHSou#2e=3TW<;aBzWbBRMONzU2&myX?~`kcesg0n${v>3iSM|7Gp=SDFzc$DL>_igNrmz|aOwYiMqS zSp8n!r(4nmf!o(*aR|&*fr6~HG;pg#Vvi3*Da5G+M<0*kK!pg>6=bwU^8^~d_N?-F zmEQ9PsUa*7ex@wBXV~sU{D_l~q)b4b%9aldt{3b&wfT_K+>9QAXJJ%jc6H#XMUf_2 zeVr6X6JfTJA!z7huYnB!T!@EGbVDkPOtjdW?l0U8wd))1G>ovqI;ASJ7ydZtOt>xh z`0nAmB*h{#{O{qNbsF=qLn;{4nq31XDr&6>Oh2$NB2~$5z8JAk`gJ zR|~Zm5*blMba}oTav6$)<{GFMbFpJUsdrY~?FFh<=2EO3*W^^J$dh&&-x6tJt3|+d zI&HpT9A=!Hp2T}x9ldvGHO~Em(*vY?#Fyhfkv|ZJxEta?agZO;ivb$EjjL=CjNB|M z?#KH?{G)57kr^Y)D*dWQu;>9VHMI@kM+O!VP%GKvyC{sKKju{FBTY0)+nrHf2CYK3 z+P+{I<4Tl_my0YrZ?>o&gGhQp6n$R{e^KM_GatOo;RgU_3;bz5$OQb%+?o#pgH8dz zH|DT6|Nmwx$b5JI#%%ECb^oRLATSm2gII7zmOBK9h8)KYJveX-%cGr8KjPY4Q`5e6 zJqDsL!vMPl$v;+YmE8u;&n_!SsP2vCVFlgZ-n`g~l9ZMT#jgZby4uC`7?!5cgBz{X z8@1re{@WmXT^$g+Z*SY9?9<>0!?#u}a%zIBnA2TPV3Lrx7g(6k~8C*k(Y` ziRmld?rnOMoq`sgh)ZnG6~|_fKrFq{dphw}F?hfy0%*VRE9Rm2x&qAI#_}clA5nGd?Qh$Z@1kawhmt}2#hRX+~A2FUpf%modf%-Yyoi7>_kmQtO zkY!-5RTySr1_0!MAs?8P7!*VStDuzwUOooGunp=x!jQ-Yh_S}Hy72lq$(fZ1e9WRa zsUYLA?QuO2j{xnc*!Q*Pfh;RfW4a^1@TMF2J^v}c@2OkvK7>M*o~}{8heT)u`^v~v zeI4cP2se zt{li%Ij<}jCgx+_quHIPKGSy$?OVw z2hk3H!!cxBKJk3N`4Bud^SEPmvBb`(k7|uk{gnc|obJ`yxrLSUm-29Yd0yaeF_#Y& z5VYp_w9IDsa#~RV%@ArkFP+>Z4?eGEff0n7TYTpG>WKaYRyRRKU-b;oF|>@^Z+q3V zAq&Bs)_!$pqe!kZBe=k1`l^XA*W^VLxY!q7hJ2sO<|~t#f=|V9)Y5fsrFM)K?-j}# z1#O%3o!N~l6skJDtCn17Jfe@8bpEd2@y_~*P;BEV8mC>;Ok7O}lZAXGN!Tpsr_<54 z&V0itHK&6f{jO(`f}fjT9%a4OYgWwi(}tUafQ#TCvBib&vSNqKN5jmiCL_TKwIebd zDz%z#Z-}x7@J{k}w9ZrC*R{WxCjZsoV`RUBv1*JA0*$`mZ}7E__dBvEl;ba9h|rA4 z5HCiQ2_8Kj6T&8f-SDo{9qzM zsw~&yTN?glQ7xD4K{Z|?(k$~?@xG^Z7KLkN%cd;QDnVoHuM^4W@-d8D-GyFSX$n~L zB*52PCgik2({@t6bCt;BqW?dX$rRZAcgIIP@1#ScG`Qn?$HvF7L-y`9li&tgX94wzOs@uVCI&<+qz{KWc!cu z{bkYcAHdic8E*pT{4Hh;P{;ajU~Ei(7{A@r^AN-DyQ0l+9*!1clZ&JDnP zYsGi>5y17%-2*ozphkbAV*h71=G*nxzw?%jli!K*8;ynlE1g=`S0lN?-%@9e*Qbi{ZH9!3qHRe-n>N&Zy`nC z^l&p7-0TB?Qa@p{n*wln0w<0?&H#rYFaq-*1>kT4+)CmbBLk@a%s-Y4(LLC%$l?`DIY znszZB7{2qpo3(>%M=8k3Nt%Cf#APl)G~BP@a(#?XPV$P~`Vo8v#0LNIyMGgP>TlZc zJ@_W;op^2c{U!6UE>ELPa7G~sX-4KWK?kGc{=g!IXs zngYXMQ2IHF5eSMoxduOC_9J)4p!87B_80^$Md@wCh$`9z&vS#3YUGeTZDZ)7Ss&$5 z!rmsXgW^6G4QGRPB^QcXXa07fO5W(Vgb>c8lgs-=96dte^D{_-S}d*FIRD@aFzb7Lw_&UXZWj!VyK6(3bL}2F`M{JZ4Kk_uOihLpoIxji$YYwRb;#UYUR^GS zjdOzQ=#Be^x*gY#Kd*gvv@cfjzFw;gdyRskg%wW`!0q1sJ?XU>AZ9bR^F+J?Nz`sI zUXBBv|FYIt^cAW!!P<|Uq37k6tWhsUmxQ?O-UZ-SH0~z>{8F-3!FRih7S}M^>F1-g zxriE3RW_j@2>{X}qa*v~MXq4Rw#dGF6CD0S76Bu2Xb4lurmtPh-ZU`6p1xx?vv-Ck zbkH5kBt%I&C^PR4hEKi3#img=@;*&a8M$=5f*OaN(Y|kdJkT(L`M1Cze+ze9-&Wrx zBAR*=?zs0h-0{g_kC#_KzWI(+AxeK{in*k24dM1tXxUQQr}YIE_h#G-Q8t(6{36DR zCu}Q91!~wW^adjsBr+R2PAf)=kO?UX8<3Pt-~synT~1jEpc-~bA7s%QT3F4 zg}pe@nnEwAnX!=a_iXHbHo}VMkujY!{mi}tP9|zZf7kKwNL5Fcl(|W#<&;Iw{;R&2 ziDweCVmhU;^t(PMEgp~NqsYzVN80(Jw4?Ba6Yht>ME-B5@h@MN7iog8?ukx=&!bEC zDR-#NvVTEbl9aSvX1FlOXk=KhRnW3%oVWmm2sUly*NRz+ck6-d_fFnT(?{&+YoK9r zDmV9LH%F?$N@~w2Vt)_)t@uz>n0UDz4c8?NwXT2PZyRQyOD7_ayKu?APBM5k0Bo7= z%VHmyceDb>uv|sQo^)Joyf>BF2h2>_CRDk_tXemA>MqU;Oo~r$6+dK_(}eKEN9>N< zyIg-T4jWWfjSSz6sH1Dbv8Y35Q!(4vA`3k(9onq4HnFmA7FRsY1#`swYD;?R~S6Rni1R|#-#_0u2EXb zZlVhFig~RD-#2I;h%W(sfqunvq~!Rc!HJ-oPM=$kV@YK5#~{fGL9{UdS#u75ke>+Epq7=}hC0R~Z=c zY*Kso`M^lU8YE&ZzGYH^2nIiF=0t&9R5%`v?JF_|oD>^H?;shZASoEHm#3Q$0U65r zREF*0{MD=ZpP&mAN67T5{B=d=I9ASe&a+N3Cle%2_g&!!gYcG;ip<>z(4z?gnyqOy z#)@P711Zxg^`BItF15!?vGP{(e?Lcvx6}P>G1P!{!Ey7rj9+=GvyebT-85VRmT!{*vYE$gYB-{-*&P^vpzGOMO|?gLjGDmObX?^f0_p zpUgj_Xg&%e#vbb|Jag6R+ytF{V-yh&uzG3CZI8A4l#%)=y+|bL+_T06s%iB+n-PK9 zNFTwc%jyP}!oeZVTl6#h0di%JycQYUl#0TR_!UaR?6(+;shFg&iKRny{9m>pXGdk& zkhJiHGsAHw@Kc&ss@LcWR)s(-c&a|JYca~WDq|exWqK23-cn|FWd%00ct#k;^4vV9 zzi-S`mDD-kzg-37h=Xo{e6AxApmhE;NP`tyz343YK)oOx3gSs3V!0pT;1m?J1PL$n z5x68i!D*&>&P1v`xD+mmyG>!(lfLyIekOfiLkr(9W|37^2NEBSIUDfgVsonJsvcuY zBW)eke}7(-w9A~|B05fMFuotA?C*%LLi4Rq%P5w#h?c&67>nKj(b!(CCt58DU)c`c z#PsTwi7S%7P013f?7K$&c=y$8H7sq(&Z2YPJQQLFj7VZ3k{Go3r?>w|x zOL^_)PH68NSq}v2?wY%6gmVEyY{Nfy%n-nU7Mwai;>oiU-D#c3f?-ZO>S7X>EF|<% zL@_eRGy$=ePpvHe(NP{kuKzuuuT!PUyk2_qqxJVVq6@F%jn2*?2q}w(RggA0LhJjQ z_X{I=5BbD$o5$rQjPW1j6VS-$C;7yB^J4~*Pr(0wBcIss?%$G6x7Yobiy^rvqcA(k)0;wdmah0evbski>=a0~=ADFYL$REQ8%F}3+;KOwDW9qW%$ zJ#d1(ot9}nhsO*K1f!(M_O4-XX&`Zdd4UH*e;1_fJWQ!2P zZrivGi+3rDXii9k%7g$x!KTYI=bON0*Nc}8Z~ERLkdy2j!B%G2l>LWJ8Ugf#$ix`Q z0&KSQ*W#D4Dw~i;`RbdY@vSL{0jmNYzNYKT@B1}$cHVIYf2=dDKLscqy&kR{j{>G+ z2d|Z}*=^jcb?}`IM5{Yh2b4Cd)V(C04=TBkC>vUP*E4j6@5x-Et9f7lJy_Ye3-JS$ z4Nw3uP>Q=hFEW+%sM&V{KlZlkvg&z=_THGVS1x3zG=U7nd^nKHJzufLnjOR^+p6q& z2|2zCnv@k%Of3dT3iN%YI(H3{OhU#p=H_4fGy<6QaO7f~e!Be0figvpjHRb=O}_Bm72$ z@Zh`7LOg`^^VL$pf&Otbh+ty16X4*2onut(&6IyC0>jSJF#TY{>diI}N5@`y)o0Sg`=P5I>s77LUkV^E8h` zT_y;My6m#3=0mie$!+$)w`-yu^lOHO^;1Z0(~1r=M)${Sz8y9FP!W8wE#BGt;o`eW zFwrBK0nC*KEQ+ys%FdZ8OezSl$i7ayJ?*O{RMfe*QzK&HYO`kK7vu`9Y^nVp#g4cr zP`$+)a zTQ`@}eorKPlA77`kcJ^n>juwg6pr(EKOTW`PdnW=79P0l`%M-2ovK=I^@~)!WvbTk zy~y)JnFUZs!7OwnV z6^}}EV940U*2~N6*~*_miSigb+Bx`i%OSG4>GU?m8O6QpTrM`Fw7~DV)Q*?|F%$xM z%+W>J_Vt4A0&= z*m-pXkIPU4g-w!B!skP7w1(sb+Y`DRJ#SmGcM81WhDhC=swP^AD(fk`iKAz@#aLlO z2MhF=8&UK33H+DE?BCg|zjH!Axu&1FtP`9e6vRUcWgj=*_(a_x)j_jitlbfvEBEWp9|2tm;P%dAc=U>2w*dqZnF*ky*6O; z{$BWX55Rs4vESZwe=~u#0j>Q16+8R?&$4gKzgxurznFew`;&ZP`<*-bm00?7A2>W8 zMy&n+4BQ`r`nND}x181u8wN~ndP@i0?B6hOxAVqL0XSrVMhn0x};Qif~&1vUazwNq1R0mIq=If9;jRoy~e5rNS<{U8No|A6clKa&-Q;r(}0Bw4^nNbN+*S)DM3ZaAy?6q8jWm1j>_&Yczi(pW$72i-(~;Zm@|m%(+M_puD7m-WJP9AVm{6 z`})#U0V5W0u!{!+akRMZujc8RQ-T;<)IYo!vp#LIJ|A-Kgqn-bc{a5HPd)jZcW1Re zMQ%qqp(%+oqA9iAflW-D0`%N~51=XXLJejF_Uco?D|Us3SZ6*DZFJdM>rfRf>%pg3 z(iq6ylX44+ErAX6HS>eG(@6N~LjXtoDSVdUsu1*9mqd?~7b0(kP6m+LA#5{5dBkix zy=JryrB=z^r#G`Gl#HWFx}#2L)XvJ|%vo$#o=^7#8azp#+X{i@rA-OIky+nun6|z0 zfUXRKtu}vaT~TUXko?mMG)v*@H7To=rwR3dXrrH z!`Vc`nfH|%(-}sh7;D6fR0$K}1!nMzbMsM)7Pxvs2GKIyVzwWIxO#y;1ClionNZ@% zO)x!QxBxxEpi%5vM~PsuW}Wl!&N+qYi}tOBM4_}?q=qIcSkAryPD^_UrTo`7#?B-$aCQ(0*NFvXF`+U!&$9*1UQ`FRAEoF3G8>WJC*5QXE1t}Mk7G-Om zw4SR$_fV{HTF}BWFB($KWTLRxH-gFxvDImPxBOoF<#`XMmEx7^u$JZHqlF4go}Ph^ zeWVdeH2hMZquBX5hvHlbD<`nQab}BXbgEh_UR~2>KQZw1BO2X&mha)Hh8vmx%x@%I zhbvqGTM#wpyZSg7c1>pPdF(%X_G07o7vw=X9K(I;jolBpMli9lpf5;A z*e@!3Q9Yhapbn?HyirUINnEx(^@Yh!-+hL`?=`S5|Ee43eUx;(_-;R65t8MJ8H)iY z7Q&UsQ^q{fwyf#KFq+yDY0gs`DaWh|YB@JL^JjfMuS&**s|}h8MrL07j-w_}GtHZD zE!&KUNqaiueyo~Hk{tMG=AfP=U7B{DWh6t+QZ|iO8or;uk@%wtr2>CplHjrSq|rS+ zd~0_0kood1Dy{f}|Hh%|hMn`?VAN)4Hk2>Kt-Nj(_HuP#3dEm&T|xS(t5gq>5t}}F z$=7HEnZ|SE)jLzih_e1J%l3$m;gA#un2pay;`iU#m3B40ue3MSRbfj?3hN%>Vp?dX z4=xI7u_)`&RaMFq5>1`eU&4n1;A)$m)e-GTO7>jqR3xDp9lq8)2bEoe+VKm^F3;w7 z!Vj4MPp~f4Un#Uj?>A9|mQ|u!EP8qR7`uFrieY5dg-&V45`5bh@!1e}nUypQx8cVu{sk<{k4CRsR!G;mGj1yvd zQQ8$~Yz2C)QwXv+S}KBFi!z+o_B^$&AN4KFlifF79*C2&+~j>E{3GFkQz{V7`y-nS z&_pB%@AFD*8A!d*W zJJMj4E@*Ze+K=5xJA>HdjiGYLwK(rX$E}g2)hCTBOXzk+jeA5W!c9bhtX=ArDP!&J z17b?W%wZeGo)#sPXd$l%)wqj4yw~Ok7RU`htV7^Mch|b66RTy5%-8bA@3U{Oy;6)` z(JqQlbM3Qh4vzf3m$du3@qNO(OvXv#fF&rtcamLT5;Ev2?FUAI$+GI5lk9k}S{fOm zUNP_NrW+_XMjkqM)J&Z$hSJVHrJKUb`2q5{9nnCnW} zM|;}6(=>6tHHIUD;dS;1=+o-0Ef(D4!l4;@<#V@7sTKHQ9;K@uT$Hs(sb&k}UpD94 zqbxJzS)^$Cin6EU!an=RFeVl)<4lEyE)zR*)jpvuYu80(Td~DPrS9d~ugl@~cvyH?}I>nT5;uz@8oP4!H-0}Hy zp^xNs*hzT#iM!JPlGJ56;7#xmlDvO;?tZVYQL+Srz;{y8qK_wsk})ww9fL+_ur$H@ zUWH$omIGRM^?Lk4HyFRoc_V$J&Nl7MN>W{FreGav#$Yz#F9oY=xA;;y=}|a(D;$b z1~wTJZd%M&+A1b~RX+#Jz%_?~EI+Tgw{M8YBrfF+T z8jkJUIUMPrK4p;U!ADzUJD*vuAReOyfW+nAc07A$28D;=#0q>W6q*71Sm8UlPyI&u zdL=X?dw5%jl+SfV)|u?zDhotNCMJzbhpUB~5pWDP^J>*}_FrN)YV8wC!+~c-*WJdgXV88R!zt3%QzJ|hx-+A2J4!D4` z=_2DWnrfZ`jPjKV0z@0Ngpru{%yeC(L~T)vycegtu4Sxu1vn@)?=Gz5}Snwya@R>B5Y+1l2a|K8gMU=l|7m{5Mx0 zMR~Csr{U&9Sob!%>d;eQYDb-eYg0e3Bv_1@Y&D5~(SczQw55(6u<|NU`?HRCS!(RP z*$$(X$qwQxK8IG02tK1$+My4G`!MotLWORsb|u;`N%FI?>&IS8_>r_xSxBu%gTgiF z!N_u9u8fdCz3B#(iOGb35TP>o77(Y5z-~pJx8uzH`u}0?Ex@YS_Wp5^lolyLx|_Y} zE@=^@LAqNSDFu;`ZbU$&MUa&4RwSfBQbLgK5S0Jy&G8=aa?Z=Wzw^KGzCJt;d%iQX zXRleaX0302KE6vW;PmQ)Ti{VaqQNXoIo@~f_m#hf@qG1W55<>bunf7K^Ar|6uO~PM zg?tPnJ{%)CxYRE0HVwY#$Fi@^W0{?(jWf}NFDl+tKhMldei^asJEh7y>=+XDW#6uE z$nqo0hR&x;-JkOMXBP!$Q!o<;$wJ2NUl%~}--FkMe;;#zQqxel0)f#&mAoHzUjJUu z8*(-+NiLWNl9mMLUS}ZWWke$wc}#lq>i5ZM(mV^voSpzPuTS3M+`NxVL<^9qVwSA9 zUusmjhP%qG^52wiW)E#%rh>TgrsoPPJbv z=_cWm_%vf7BI2yjo#@7chnB$}aehR48Zad7jl>?p(=AV5Hr`XXv&(uQsgNM=fc2ui z&5Mfy*K~guu3$towk4D424`uLLt3ncOqPbFk#_pWB$gsfg9VP&0>9%b8qY6cc!h^_ zap$%Bf>!uX2w>u&vgl2$Nbq}!)7te|Pjx+#X%`!7j!TDCW^pL+r=A3OMLeQTgwa>U zw;|&aXXEssYlCk|C)hAp;i;H)pNqC(9Rs#!$wglnpUrKpR6DO;7ZE;QXUaghiQQQ;@h7D?+aNpzC z#M~<+5@9TkgMm&TcLS`Q1NM|+1SM{@jA9n6OkCYZXqdXnevIrPl{`@K6>E34>j;quT=$GU9>fFYWK{HCYUg}~`Zg!<~Ibf)pgRT8+cb@&g1#y;Mz~1~h zIT4cihXns2Ny*lm`kSbEMHmR(?O3IN*YPXMCs?mX3}V437B0?&K$1l`u}{JnIG+ne z*(cRHqw|ry-A=Nd&0{jd5?SWm&kP`x1EbP*6X?&+P&zhAdPmvt zJX0Nyz_HU3y`w@UM)iHp4Ef#{x9c9s?IZyEkhD3!_wDC#V;|TS9`ip zP>@h8VQYSq!Qp)*G6~*5nltm>gG&OA(VOS=_^C1RF9`X9IJr*OSaR_EGa+B-AORff z7Y_MC?(zIT5H}P`2yzev{-cUM0FwO=zJC5i|9me{8w61$AU_CfaQ{J=8JLq#;-lwZ zjEfucqMY{!+{TaS@_C0ZvJU`hI=NvGC$tXg^mhQLr;}d>PCCDVzyZ+zO9=Ky-0dIv z{J#Qq#s-bb{sKGuk{ZmK#nt>*AfFEZ(f zo_tKN*u3AsfygT~=b!pE3jQ>sWru(#iAhCQiiw^s=TU+4&i0YmMo(dSfZtPb$4(U%ew_PYv7Ve7n2TJaLsF}$@a zS}l|235D@BbDNc#UMQ~LwQcQ-nVf&!vw_B>?u;217bk95iO^=SS{3#=t9&$M&(1Js zk($#XWY^3+{aFu4PcthOyC#z}TBEvW-e6a4ZaZ8d^<^(Ty@fao1ATbZ+R_^oxNiQ* z(k$-k#6Vv-&?8c`Iy`+33wz|Oh2aV0;y#2|>udu?o5k3-voMy|q_`a%#|a!Cy4>w8 z9FuLpOzCFGiWR|9B=iB#&b}5Y6X+Z5a9(LqPlUJiAoPjg6O~1;fPYihXlzPEb#pEB zQ*QO6yF!Z_(PPqcHP1IFD&EP%7z~-Bg1bG3O~L5y3pZAg?g7@56#%`W53<^?(U&P{=KdnYzsO%BFj>WUjsBZx{&+4` zhsuznbSK*raY_M(8I>T)mC&wB6yLY2X+C*=tGT6QgEXKX{qa#>TOsA!`eOMq$?L08 zRcK{*dHGUhcrES{j0R^{9i}XeiB^%^af@n4s9He@q~Er7)*P8NGE7afR->>~S=SRA z=I;rb7lX-kk-eUoENkt}PP+g%6 zxa>5Vi#=(=wF!#)S;7rVT=V8QjYQJ62>DZ)5;mvWMH+&TM(dRJFS8ie`fV9mSl|lm zO_tU_$X;c{y850JjvD#8!w0RI%y<|h&ikQ4jxdYmajp9px%DEbmv67BZy8hgv?Vcm ztuX{eyuW%=CL`@Wk08&ZVuOSiOl6HCEDfe;VOBSpE^W;EOvK53b;?Zljjcs;H4f+s zqzhXE`PKn9oW(Z6$W<8ivI%wGo%v+EIM=(^b2lB<2gi)NMKA#OnTbvN8k~-Ek5}<+wrr3%M| z?$}Qhu?F~Nc3%sI4H2!5a5@goQwpKX9PBf5muh0JR2z))=C{4bQo8amx+N=A^|>3; z(>;ydk2gyVAHQ>R=8Gw#f-it!c=()gMPxd@;Y&@!%J#$zpJT{EZ*p7jR*uWz_YJ?g zyoW=RON28dbsx_g0~hpav2&lPtT{_FE{W7Nqn6DW3DAs_j?8p4_DyGHHc*H5xTOh> zO2f)83KH(^inXy?V6Uv{E7!Lkl?O}-le|kV9r?ocF?)qKGZdgMdZEbwU}#gR;#NWv zqR-qGuSYbWs(#^T<`@}T^08FHdZWh(TaO+hJ0JPvc#3S6ZAX3&OXGVX$!|;DUvY2K zyZzIf5k8(xcU{yT;pax6#FcSllIz7(UXL}e>&EMC1xtPNGAxaDOj%Me3x76TDz&YW zAzkuus0Xq%tTUI%wR!TF^aWn<36)Fsf{BO3>C{r?B2NQWB z9V+oJJ{zW{IHOmL5Bf(rHnXU>s=Sg+Hch%WR z4HP?x9~yjy9(@qV=##Z3vCmneL`%D+c%#}mGez0b5v7!4wV&9r9zK3l>+Ibmk&D_R2VB;IhIyi_(pTn3%1n=ikj?M z&GOhTw>9)@2rqP^z!S}njU#mF?W1)QT^thEfYf>F+M(=hArYB$Q(D7Kt00+U+*ptW}&`&j=FD zVv3LuC07r6*k%|^BjLcBETfOCTttVvV?cxjP*3|!B1k`{k#^&cvFjSN#wWOe=buM| za+`#(Q7>LhO&&3-k|H2+W+;BQ&FAHd7wNbr6(6=o{PY+x;G{Rlaj)y7CGOQCU12p^ zwK3BvvHSRPh^*Mex4n`{w3^>)L1FlLFV!_Q^Ht}^FADo%@b6ly<)Ir}Xn0t3k-2M# zIGbBM!_dPeV$W~jaCfe*l-a_qVN@pM+o4*(?EcKW==FS17V#*#BZ8MC`zGmNTe7U(#^f+jVR4n-V)tW z+1VOKt95AP2ia?_k|<0sM{;Xk^&`@0z^5yIO988E9kcX)I%L>wv)@p8;UTuT8gZQ~ zvjq=>D8y3(N7I^jg`rKfAjd#X`xDG$viL2+hM__11}&#;B0`lUiZ2?D8uyjxUpNdB zrXs`A>}pAI>lt&stxd%(UOkcppr*ZDK@wtr*-_Tl@NyEH;Wx)H*YOOv zhfcDau3>d-Gq;K-B|po!WCn~hx=(fBI&_8|2N0}26L|h~a15tm-bMM|{aP?qZTaz0 z{!uI!L6>Rzc^!5^A2TQW8PX2Ov`h$s&>uO%R5g=y%_!3c#U0**mD;>j4eDKAv4J~D zq=0ARXDVwTox#(JoX2|^Ru_j^P4}>Xd3b-tb(WtOL*n84`(z9ITC@8Iow_;&7zP4) z`0Pl9TGFK2pri85U~e725%A4)t#;*9mx_${MEciwL5Ydf8+qZ9;)s@e_+fck8;;8j z*Vms?E3_F7>fHzty`C%^<3i%8f1h`OyRY0~BBts&3enJE&|Ut+P&%5~=S=m@nkrKA*mO>$O{=zvLFo>#Y2e61%Sg0Hd}L_J<@vWk=HRw0bmeB5uA!t9;2T3o2C zR65Ywdnch9@zzteOhqb+Y%&YMB1(eYXw06_9UVAqr^{JP3mN3*F1F!Pq% zf$V-m-1NtUBHh|(`1cikA79T}t@zpA6&i~FVC!{Y*R?x@Mc!%Xbp4GAD$d^JYx_gf zA$OnXi{F{PRT`its5tx0WCN31Yp~CjILM9r0gw`8Va!i*&OqjdSm_s7F2EdTELatQ zE(HMW?9hNGc!5B&(eFSU23vL9PfDBACR4EQB65`l2G0JGGzt-tHB18{ntfhJH|9Xm zq;-~bwSKGd4%J)Dp+{eW@-VMn-dGTJiyu-*U-4@fAAeDlM<4wR>-()9;ipeD(Sml+ zyJnXaAI*)bASm%naa1GAm%_TOvHml|K9TwT^`~!Eg(<;BxaaHSg__>x(g2l6X#!mO!#AZ;$(9Y;1_j=JxNAZj{)r@{Mk zEp3!>NFM)z59vPni&D zS4Ml;_v<53cEd9t|FRNMGysI#bkIqqSx1Ec1 z6&#k9voTVWZylIvCkhxv$v381?}bcPw^<4BBv*nj_l=o`A?Fr?+L`$qvZ|brY(EPC zpgoGlX*pA+EEKHoVs`S8UszUEp*;P1wXR1A=|kDrtf0)4dq+N28DUlT?sE3$-JNXL ziih_;7YTfeez@JAa2`h=v`Xv(gA$-$KC`NKiJMdO+zX)~|CUkk{`NYpS zx9;DkcA$O{8Z5T$Zucd*P7-~@I11%sBl&q8tpM=A-zKB;Pz<0o5Fj9W!vs`KaFIfs z*-#V%5Y({}zw3wF_gRCGx1R#XqTl6gDHk;|40>@50Oi}?`)1YcIE7dPf%JA`2{y0Kjj zQKnd~eHFX7o4JE=`z2#@F0JsCu&&#eZA&h)n}VhH;C3>!_o5H@HYzM0_YX-w_3OTj zH*UwCL=Z7&rzNa21P{axcNSPY(nOCm^ndX<l!Ol| zK}t`dX5onh1eu}M4O9Z|1*oF`@e9DcoXp+OV-mnspc>rSpf9vK{{mXoe@)OA%1U^F z8)D}=>-$mS0l=IqC!1+I(22 zCnYSrexcMKA7F5G>X8riX|k5Z8E*@cQxiysE0lS2Pp5ke&3bDEyu##o8$Fjuj_)V% zwsX3}V5CAx*}AoF{)wWU$F&^Jwxipc?;F=Kaz=)ZY*d4ykSygBBX*=&pMsx-HfE1x zE_TXUz~DIDd*>p))?|cRFwF95=H0*^^+xSyT)0qE;!>Mt#!5OG=E?;>n|*P7i_gti z1%1PYvE+JQ>eFQSnJ#Fxh0Lt+tfE40-D`DoZ=agjZDi^bVC z&=9_jVy?~1t7{h46Mc5>YxU`g5`BYqThi4>mNtWm`Z+!?i|urrolY;}VspAwf68QA z2Cdn`EjQ79i?Yu#%ue))1Vea!l1(<`u~`eGUhO0AYb(1`XYs((U84_qt3%_miVk`2 zUm5lMJVIu>h9%M7c00^`v*p{vHSk-byL~~Q9V^_+-*ajzWIWe8u26Bk4&$4mM9cHEk;H0URBNmo4_tK&Jy z;U`%^7>wZ-;d=ZW|63_P$w-Ey~$U|u8iZRkRl@9Np=(x9KeAKqS(oOow4@!M>$ z-=SwAZ|kR>S_biVxmJa}IWx-A3EW20H=2)@UOgBj+1IKY>fXV~Uw?#pUjJUy^?DXL zOGQN*i-HHKdWZoiV)GI{!8ztXuHy_+##sqcQV*ci2xhmd7v8j^$U$q)@EP1kWWgma zdrNSv+t|PELbv5V?PFbVKzv;gi-g3)&hy~Le1=>d@z)_a|nd!3%BM^#Q-+&SDl-R{xo)?lMA7|{H^>J znE{_v*YU!Yt_-iGE}&n3mqkk_GH2dmnK$6kwnBhwaUC~W#VR}|nl{OAw)^G$P|0mN zpYVdYAuieEdZbd5r|)>oB44_UEo3`mi42cpsbS{Q`Y*yLDJ9K4rn7M~29w>{3Y7@f zRN!7w_e5{omg@M-hdCw1V$7fR6>gSZdG9GD#3eK+r}sw;bcw@Wmxw zNFeXkLsvI`?99+Eq+u#Zb!f^UxR(B_T0*7)c8UPx(^ztj0w^u zH8!iC;pSDAx;Pjve4c&Bf$%}etW@b)dy8)nja9z1K&6uGsCEUxhlT##<}o~c{ceq3 zSV{r=5ccozI;Op>{?Fi^2hjQ>#yj2&zk!^1ueqj*94}VD=N)Ppjr+S*jS=x}Gx7UH zUpz&x89e_i0aCm~nAIq6UGML977FnxsDQKtNao%pb06%-Z}Lc}_?;j!egc^u*r-{g<$ zrG;OWrEG}bClrpCD}UgV5ifV5y&<^UEi1MOmt`Vw#pab~aqfCiJ21NxPPvjRiMeWA zX}2)RsPzzM(%9U@@#~Cao{V=WF7g<0FI2A0^-x!@y%oWdOz&qR>4(f;Cn<}_wX`az z**fd0kH3=_vSWmhUPiGTa~+?aEwO{N5Zw0;wRDmp=K74zk0}C?xl#`ESS1m*&3$0B3$N zk0{TXSa?qPC;qH!0br-o?lNs9+0Z$F>V6)ey1y#4#nWwB;tZQjI%At9Ezhd8R4-t6*U z*hEPXMjJ1GO5#HiHOf&cbyFM?h9%=6NU>=tX(g+%)K3i@LP@;V*e5g#N4N`2VXkPt5IepkL;CKNM|x*;RlI^%FOS(N&7HZgCV@S1cOMyOmcy@dVeW$CERfZ@7DRE9n zzpD=!j~;uXV6XZGbJH_V3m?-Mtn|Ku>d;54qf|vzxL!B8j|E4*Vh0-!q4xS4deaK6 zes|>at{<~{7b(Z^_9EC7YaWv0kU!q=0q-FMWPxf4X4ZEv5^QooIsDR4t=;F!^8Vby!Iz*g>2)RUICH@|xK z9c2%_F)LwHMrTVfTzUty&4gnNQ?Iz4cXfCJm9$*f@7n!f%$%vn)H_Y_VOPbKhq45O z2WPGw2v4warcJK4j%>cdMT>E{ZD8xaFxES-85lOiuV0+1s>#LaM@?hc8=&rnypkSW zUHaUo-WA^`M-q?2GKdu;4MwDlYPL3Dx6X|`ahk)XXl_m1zLh}-QD1uQ36j0-dUGiL z>nAC~?i8!zI2CW>S8h6gtTFlWJV|?5{x#BV&Kh~7`EcXAff1KS>{a)_bOsag=47dA z8M&7!Gixu6E6;E_7<=!H@i|NIgx=065S)?CwUy`jr**}tMaH6S`aCYot#Vv}s@ zJ6gPa&t0{ol4`>pZ^4cBT3?~zg2%A&)SW;kkFSS&Wuv(JAi(q5=v5{*y(}M(T!xK|NUR&`@<{o{a2eQ_L z8+HaBYs8)OP0bGmo44h0)#A0WROL4av=Qhh8rTLM+BfZ}He8V@61~hzp5BfPpSCGk zI852$3i0$Txm35*;M4tV-12Jk!=%`7zVFi;yw-@g z=;zHhr@|V4wy*%!^XY{}PC*(1Pg}KxEErD9DE-!*m!LBR2o54iSv!E6hy6e}guPno zQYC55Vw(G8dPNI#I`zGUFFE){2*=k=5?OYy2w}#px5D?T5go25;;LP{D!xaSllSg1 z;=XbQDNfRJJ%d8kBw8yC#m&p;p)4+Lojn9)l*JAt`HhQ%E}@M+w(}^qm*iz@tO|A$ zr(Qh9Ws*kRESZEU3bles?gvf3PnX#*w{2^)o}|ck+;6B|M>MRGQ8@4ZFPO*320Bwq zEC&G50A++@huB)e500pkXgob3I*W7tZAB)la6Np4SStfXJOV6!x~zzeR2H887zuFS zn(~Q zlAYTZ6y|C|4fm%nO$Kgas5d8lmw!Hf??DYg)o81|$Dm2PbC_t2vQqp+&@MM*&BK6Q z_9Q>=8C)=rkPUPuk5EqW1fd&BSuCtKv*(!A*R*>)blHwKA!->Z{IM7==n4yOi)&;8 zCieoBKVjb&%bqo~tX#|4vbQ@Tro#D`NM{EO$(Xb9o5=I3eU0yOR#A?SZpE8|D6Yw; z2h19fgI>UOf(+=j`J_GH^F3xL|f3Kq_<=;!IUGl92{=Y>%#7SndT$WEUH{KVJ9V zq^6KXYmK=G8CiM0$}>c=#X(#3;qxXVI8 z9-twlTSV*>R=0+6Dd}rE2rs9%0ne*>AK&AS`+)jUwB$%Z-R8ywUt>@T6d~k4Gr%dng+n4- zg2N$e>5wZ{mOXhbX&fajBVK14-T4KEON(7sWmAI;K2vW{qd2;q2QWJPw4$gPUTYbpt#%n@=iJHR!dXuV*8!sKpuH%ZT zw#}9zHg&J`KfFx%5$+~oHTm}EtNyHwns(H38$0u`3#`Lxi;R5&?W4`bc@Diej8&g% zt~T1DbJp6t8w!QLOTm(JeCe$tip32tkA{Je1}E@_cH7Y>W7ZTq>mbloSm(0#MS*~L z6d0-;k5>V>Ev6})%{ffw#CziIWsXKT**(UCw3$l$>xLU`!eZIYsQC*I^f5|tuvpp+ zsf$S-eZ~hh5#{K4fo};o(<$O~6Kfi{9&SEX(x77(BTUM!-_DWq>hb=LKiNx^-T!1S z>>6rtDq=Ru+V<`CN46#<4Vt{LlU(Kg%q@?uJ$6OaFBmtpCWvOTD2qd!YMmc;daTAy zSa3K-D%4A$RgTr=oJZp))siG^O~t;_6}cbt@FN%tfBW7@vk5#VAQ22_vMj6iMl2FI zq!e0XkRHFwsXxAVcbjcx_NXLcg1!MGQlQQ5)2)osn};>g^vr%%jyf1M!`)0~xXH0= zmQ*}?Rw3l*!DCyFfrH^zCN~7|Zt7$SQRc6|LO{*9geFcALwL&v&3r~NTD3ujjkr!#h~`1XT~iLB97R06wP_lYO3Ug%>(7OJ$du2)*^B_W+X1k=s= zO?s9*|9jHx#7wXCm+^Qu7GA|Qh?|5d5%!T;8&-x7j`i($ilKR-N%ASG-N+w%`(RSv zjz)J8A-U=%dp>;qR_&r_#li%|CpLMnecdnIC-kq%y7vvtXOc zm~AtqGCl54mE`AAz4j0_(llC?k4XGF$48m4JRE(F?yX)&xkdU0re(Ur7uM*Uy*%A| z1%!Jo;68e61@vY0(D-Ru{$;S6PfMi1ssr(t0#p0k_p-a4Picm*-aXr_HAtvp`9_o9 zJshW7=LBouP0=Vcr~QC4M|%D5-vvlt{%s)snZ6D5-RWD_j+BLDoPj#x% zZDy|bBQiHBZC7VS5;)T(B9 zzYEqLcOPaNgFQ&to3c#^azRV3O0wU24$rnWVvAO}d1EDti>!qa-u9a~Wq|vR_n2<) zh`CG3l>@kYqkMWOc=T{sVfROjNW|_T#&$>v&?8Qq!HC{5<#;<6-Hvv~m-k&i2lkIcYce z?vuaEiP4Lx?H!oU-mr6E`%H-NAr!0_t1P<4AW-%>gUE}zp)@a}n}I98W_+eFDZ|vZhH!ojBdc_{wf8o@u)bS{Z#nxEO2sX~$t`18iC!sKX&J|aW z%8{_xaCg_g4{=^YFVt!C<>b_1Xxkq00p`K-0p`*=k^QoNa=1$b%iB9^qm3!Ct9_Fi zu@w&3bA=AIy3rv2gnG+yHAb7kuPc60ekIeQF@A>won{|=Co$0E6O#VE?}4fEZzK9& zkU9l`4W9;C;QA*Lr@*Ygl@$MnB~Ae(sSp*M2TF$x2p7rkfwx0A7^uL{uyFD2QXZJKljIX{;E$708QZht8)OM7oh#XJ9%=PgXa%$5umvw zkYp3c(*!{iF-{~P4Tc+{0iQ^~o67wI7Xk7_0HTv0$ABjTymo(+0DJ@JOaT2J@T5Rc zKPglKdK<*^+aw0W2itxC2`4ZxYa>AgE#t{T|Q* zC)z1g0@`t+BA!TKK>Cqd1M~nOLG+k2$uwZr3%b(&TaszOK#um`R<=La)z0`vRqn_- z1G?JiJa%pUEScdJkNoEyClG)!CcONGJppOtGNsQ50SA5=VkgMm=!&1BGfmpGtzav^5j2x^sjO)%y$y=0%sx zwJh1DuHdC@eQe`>?X;O1x6gt>~@jpaHEHEM5&-#mD+zK~MFeL#4zS$7FBabw(>myBv#Q^#!ZR z=MLQz$XpUHbOjTdw)W8Oh9)`T%x_cXH7OBQ9QRn;Nw+X=*Nh=XL8TL;KGLEay+yw*ZDR z5f9S%vC_h2jRs1;t@5-qyn(&RXCg9j4)6gUs2IYyngy7~0<@p$;F%|uNRCKXcM3+n z9{W7j)zq!L^A>O3AEiZ!>T9D%q3<11WL@`fU&HI&8teE4-r+Uf|KjF6?am`$_J!6x z(I}&mN>xTvFfuHYX@8JV5wX<({;udvNipFiIEszEawDdxc(}&fBsX6zTNR1*%OuTH z^#( zn)nQ3bJ&R9cHGPJQya^$7`pcqNhTtFpYL5*V-3fu3*U3=`*s9gP`YteIVo6>k=)giPPv+IM?auumGeCK9 zfuA^7M^ZoDwUL}Xmguf~E9%1AO zqpWZy5hg8?PWmh1M%d;lpJ&`^N>f>P7>_*liE3Y<@=%gomJl##v?3>&U4Q@(LHo1%T zwlDX8QU6dJv_<$~TW&a;dA`xAM(=^5kuHDcdx_R*C5{3{)Ei}QEvfX;$u?Gz)vDpU zK4&|$=%7h@#9@p_lfa`@42?K{z%JLaTB2{=r10e6YTCIgn61nM%U4FSTjg2Oy^E5U zA53&!XP@#<{n-EjpyW`todlPPe-R-zClY@u}3WjZ=Rf zvSQa!Hx!_@&;0&HoTdhe_6GcUoq9p<(@z7q6WTtw4--oFy`>VZH%Ac30t+6#ry?cG zaGz(Xj<*T4l?y=fZNPRD#viHh#lie0F3S0FsZp^FPV+DXqJge> zrA*}$E(I4OnL||`o!bfkuggwuXkZ*1?oOO6wC$}lI zFUs zRo)p88+?wSh`RNc$O#D*@<)$kbd|JApL&At_eK`^eY&u z5kV!O?O-T)>WL%lnCxfS%jFbLvEW=1Jt-gZ51l-M&D`xOVhFD(RdMK~)v#oSUk?iu$$x2U*-`WS9`2;MZH2tr zz6^HsW2Qwm%PxbJRne;Xq8Qcf=BI(;M%4GK{DZGteb1AFMv}wJ*V%>t9RFo^7kUNN2v*3;5CwLmLb;IKsl_7}*q079sVg6U zLJMWnPZzo-gBzwi!H+;vdR@0P#`nn~tX)swa|DdBXs5)Rxxt(!9*3-zYxCXsV+{}V zGZ7oDE94V1*{Vzjc>{KZG5d59%PV*hLmGx>>VCdk)u*`DP!#0B=g_Xy2T3aaYiWh zei9dOto0)ZP=>TJ;Wg7j+AX1;aH6V$|`8w8A6X5M>o z8a+qu-w*-J1-x8OHZKmzG) zTJ=w*IR!{HDX_ocLND(hH+$%tJ?{TeC5tVYB39h-Ng~0LMbfuKG|G>a-Cp^yn4|Fx zEe9Pvh4TpLrNsB*<0SD!kY5aD-H8Y#B6KjOawbf$SMd&z#|V(S;-$K`2=AAyZbWC= z5GwL!HnSfoOQna(u-Ml?YK&**K>r|R_r*}0?B0euYG(k^r}$Ua9wfMtB!1O)Oxk@p zQNI4PNkvB2im*R5#L96B6^VR3K#O%Wn6T-txPHiS1cHx+y@hX0?J;!#vp>jqP_*m$Rvug{i+oYIn|f07JBd z3(OZ__Or^eLX|}`E9UYJ&Rv~Au_D7poJLG$9@_{+yG@v)H_mW{ltvZnlcrQ8cv57b zre)OP_ub=4^H!I?6W6SQ8qAWOl<#v`JcglJQdjXQDE3_@U<0u| zn6MMu-!7O@dRN8kU_bF)RU;pyIBK7&$cFa@{hk`Cppn{xI#D;r^8)eE=-O{R_q%5N zpUq$n{uhT@+1O8O^FXNJpU~!^BNR{yeu}k%q5Ti+|0gNTf7XuwDj850AtuxLf9H~y zpMUCKwHLx!{{N!h{|G1kyOrC2_3s0TOBd6nPdAMHXVd2N!T$~0+<#yVKvi*w=FSFa z>OW$KKo_3qqmTnY*MLDkECC?Y35MFJXBGO>)6<`Mzy50!dMH7}1yvpKxO%~C5ZA&* zCe5=LPQSQ%FUHji2C{cA)W82>gt#XJj~5sh|51dvCwz_b4?M~8dcPiqoMbz(nCS_Y&7Bzde{XL8K$*ogXS%bG!<8Oy@UF+Ly(+HYGpmgW#`j#`34>im#wvO&Z2-;)1SkgTTcfVitu%vf?pMdRp{^mU1 zbw}Usa{?Iu;{24qfT8tdJp3ab?LYPf|2r94S^p#HSx@>3NQL;>3;#E6*7JMw7cuK! z>7RxqMV)n@|Dt6*+aG__+icwbQw-rODeE6y=e+%YM9B(@qSaqSs~06&h2SY%$O`#IwEDlB zXcY`iVZOk0`HN`ve=X4}nC*g2v|mK4zlc^LQw5m)f<8j_vxPn?Kq2TaqSgPSM5|yZ zpU?$XnqNe#zs2C7l|vWkuwO*0zlc`|*=wQC883~CTlp6HzHq8br-B2h;Zx44)%a_fZ&SgI z!nfM7@I!YqhIDFn*hFWI+hRuqxkh^|zEj&Ih)Q`T!Eq^rwUp)W>=}qEHrrhh?~8_m zKOB;sCcQ54b=*?XD4r-}#z8TXe3Hj05m8VhueUxdl*OCv{f@&_@eKWX)ZCKn)R?^k zvWH!DcmKrjYQ2V|WwmKm&LC&$WJ&i@q`mjvnt~!v3}0PDxljK&@sU%pr{Aj-kXe+R z3>;l;jY(M`JAVU2lhb#!H@{DMmzkB7709px10a?sb*oH2?*S6ZCCu#|9L3D^?MVS9 z%3J!sZU4ke3BY0g6B+-|p#XRQFP!mzzDNH*Yws^w%8NXYA8eBsIrkSM<;5Zj=S2ek z|J?VBkrMoik@9b#d>0vMe;ps?>0kTj*eJo!@De2c1R2L*Xlw>50nhZrseU4H0n(3h z76_B*iH{9>3}B-?aiBmYfQ|CRGCq-jqIam-2bBOG&J*1kDglg>Cu-w~1UVD3{+^9~ zK@lS^qF1t=0XCBB7C~<&MH23*0eH`;g?-ySsiT-pR_k8lq^#C921OL2kUkZmx^}t9 zb?=e+?ruho$9uF#9nziWDJZ8BQGeh_ zpMoy_7l3<%0Tjgx#Y+Ac!ANsJR@e&+qn`lue!)opIgB(A_`X=To_#yD6`-kJ)k_S! zPj5WJp2xi0h#86CvEWnr?UIIO)gyH*%Y&)9d`f?8xA0+o;tu9I_Mz4hq$8hd8<6O| zB(lV#TLdXh4ZNlK_3A_f1*NcONAM0_h5v zc3q=PmhRmq_x67gHaIqBG`GMcWud0Vp2<&6asWn0CP`Zim;pSe-XpuN|S zDbZ@@_0VD757$2UcG-p2{yY=qR9Vlzz(n~g@SmZN8JMO2S6L_*(Yen*=VyrL^Uu22 z-e1_xKP66`R%!k(Z2vim^S`SH{%8DVFjO;xsBVxu0JHy44k6z>(J-LLAlS#y#rLdQ z$8ido;Y14pk#ci!fdS{rpB2IZT;`l-xRh3EP&|~xKMDytd`=u2rLOr9E6UPuU+7IK z7sct-n=a=jeNQ@JJC*kTW9%(}DrvfG(Z*@Cad-E|T^bs9cZbGZ4({#_jk~)Y;Gm7W zyEbkIcfH@tKl3E!zxTa}jI7McReM*iy)!B+D=W72yvai?E|{9oRp=^8}f~IDFVe z)eLm2Xku`!xkUGAi>Iy5Y%agPv_$`XhLW1Lyd<_Ce|hEykf->-7IgX&b>|jLuEkHn zt8c{3MB?!22zEzq59@tBi5KxSh5IQ*oZJd0?*iVSEsndqWXB z_f&>wzHWj0fdSh?!?wk>doB`ntgg^=bVA#>LV?_Wp}uVe5B&uQzTl6KkT^kvp!`^s z;6pS47J;6U*8hozESf7)SGadf1n(Oq4?VW4sf_JMV!eLn%dr1VfGBW#asQju0A3s1 zW;jU@zNT0fgewt@&_PJ1VX>mk9{-Fl?HA*?#(R-Z?_;!Pm>hASYF@!FcoLD} z0AlPxiS^#H8KBa<&?!PC@(oe&+Jy*U=l&QIVHSRz^eB9SjvmL_7uqWz0cUeC+r<9p z3F3q9kU{E)Wv!)L3hh*wfuqlQa9%H8C4Lvefo74HGw0VNz!fZC7BS^NK~*uytCVoO zB6+D~ey$I$T7t@vQRl0kY&e&NtrHAF9}A}Q6 zhrD>&8wt9>bVOVC>jtbj1ZF{~1R>Gc8H7=e~yC`!W^L>pbjQu^$Y0jkaxx=bTH?QxnbZY3IXL zJ3mUpBF39YDK#zD0?@OXSV43yE{BKC+lO+xzru&%TyKRao9*rAL{Lxh`TqjDS#A84 zRo7hWTPh-L^!paYZtrdFgYY}3ZH{+s0uZQ4Rb$q2+w&&s>|8l640V%Qd&-OHm>3x5 zJ3bxl_CETlthD9j6ByhxRj!s@OXcPW;W&m3P$0~kv5l0K*ZdZmJzehT_-#JdCbQ0CU>#VNa;`S@uw%+==Hh3FjUJgAn}X!alF7Hj;^xrE=cuw` zb8BOvn zFG-40mQp>5)W3^rlhoHh!pv)xvvGT@JE81Pi-uqNn?n>;6!9SOV0(r-tAAYkH(m7C zCLZmLrMC1Gl{V+6ap+~SF$Z;7_2sG$OqSToxE_{| z?YnC8rX{Lr#cp=--zw?O7Go52dw;0({%!i4?o>(r%vw$&(PRDyiAO(TZZB)uS!+Ak z&PV#{e+OX9t~`3$rqrw(`@!_gu|m3#n60<#URrO!5|q# z|0O6+F*&N6MVDGStv3*FJm6uVi_Qd)pWsNJXia=X z^Kzn@84kQevZfW?esvap&XJm$*X~_JK{-vjxsN47$o|P2_LC(zm?z8z$^}BcSy=}P zT;5m!w@QBlLS4ZiiP*;gC<{%==XT6revdEWND0(%xfgrJr7eTxGnbkI1RtJ+cI1=Y z-IxW>d=IN9{Nt4D2l>Qyl}vq08O#DJ97=)mwlMm{n|$p;b6eJf#DVS`lzW`q=#ie2 zfXqCeFvy=Rf42}kxvkgoTG@17w&imEN(AEX0e6-Kn_rW&yfQ6u>}hX%^q3P{Tn-sL zTN(6OXx^XyWa`oR)@W)>L6zc4E%24tKNiW$b^diyvJQbS6(VOWK*3z3j4fXk zZ>9q2SPj;d9!gpx^Po2tX-5LqkpikM5n^LRM6MZ#H?xkE4-rNZ=DG?x{xY$OnfqII zMszDa80*jTt=fFuTy7GU-cjC9q2Kst)wMSO zO$~rOA*g`1*-UyXFqT_rY)W-r^B_lfU0Zc=Q(ygHo2Rvbw$Y>Eq8FK9_$um@*@Sw*i$M7b449 z=QLTseXGwWf?KckMotaIZI!)6O+moXfo9cwW2)Ksfb`~6oLrViAIrz=lT&;R^l)eW ziHqUt;am=C38io==}~2+`I!Oz=fPr;znpZOY}^lAhpruJG z`p~~{0$0V2+|e@9pi8L&t)z`~(EsmZQbmh|(F0nBFD*SR8qNJrH5C&d=Les$1QJ6L ze6DK49^=R38em)$tU}Yc&~t|V%P7v%2;AjfkwrjdRbV9yj;i?WpLjz9XDx%9iUX?f z{~rksJ&^tyQ~gEzS~@a?XC2pJr0i-@KpYgvHQS5l{`^YPI6nObBm6pR^~U(PYt73x z1DK1az_N~18N70)qgOB$@nP$H5?A&lL1ZHkOVi3A(}OD&-91UL<-=qrQc(yCXvJlm z3iJ)quE)jsPTxfz-kr?Ur?|3W)7rGWa+Pe`fV>SN+Lm4Gd3hc2bpPsi@HVpDqvK25 z^M{#tdUiclV}AXZKYyNqxg`Df1|Ls{IZM&;*1(r8OeBKzK#?F^Hl-ce_IDAOb#kBs zUZdK=xZXUfWAO~2eyOV(i)14j^-sBa<7-8o6Aec)pmX)_rkUL3{9Olej{DvT)pVym0S?Ch%`DCV$qv zb2(=?zg^c*&9ndV(SyX_73+!j%jY3QA*_f!ILi&)k0#2R0{$0)B_M`Oa(a@NFgm@F zdpGc?!Rf8N%XOF}*YjyZ*Sp{ftYpe{4)2msWQmrU+#ivVvam|Y%Opu@!YH%&&RP`7 zZ^a=)Ruc24@owK!#(7om;A!@nJscGZQ6wsFl;8XC98r06QZDj*zq8bncc#8C0)glE zqyr#~p+PAA&jP20Y%L*PjkZg2 z$rFtJmW5(1A0e0jpUGbN?^y@|TcPEc$MU%HNp^`Bk$700;!Wt^ zq~+xE>f~r{+0aYyq5}*_pIX>O2q)&)%K~@U$5^^U^+$^bHy&jBA3(hcaYec=%v?S_ zl8PAel>8SapD*&SXJET&uSbEmD==WA;h$I6_H+N~D^FK5avM>=y;xvzBc}NdE_yJ0WJ5qL6S4XCcI7B6 zwiM<9ai^$plhnPvIws-dVTaVRcBQxd4=EBjY?7l^F(pRzmttFkVx`Nb=qm#4ywB_S zVHz$rm*Gzt1H$F);jK_#MW^g_lx$rjyJ>Mv%*mob7mN5Pb=?$hs< z0)mGt>x6dNKT+W>0UO4q3Sd(Q<)H>cAHT47diL8vfz1zZfN*v))JDWuM4d=e2U7Vs zwncH=7@I_Dqv5;3Z{P|yr`9Cr3e;ci%cfgp!I3J=|(pO1mB7yMVCZGmPIo(>p^)L5@$cXsmrh#8!5C; z*O)rDJ47r&Nmd3@IH$yNl(*5+hzCmGL`clz#1CD{ZMPWJafv&v9-K(0mkdkKC(j)w z-nCUDF(Iwt`7Q7m$jQ51j6!F(M?mnZzMuoKX^uY4za={+I?K4(ZaA%n5db!ZgnBO0 z>I{XR@0rfrxB45)6q|4^Ds=b-8Xr0q`)w*ri?X;c@i#k89$u;?YrUWLN5kv9DH+b% zy>o@%XRkMh8vw@^!UXu*pt!8p5`oa+S~BpShn0 z6l^?^anB8NY)F0XO=o;$`o_Q8Jl8BtRTyWlK#RpTIaW@`n_GOwm7M?L>a{l$!`pb| z?D_j3^`f(-u&CE58x#OTCfi>d+6NaYP=cw~;q8p`fy``{w@2v9#6N~o4|L|_uK$$x zMdoyQX!$UC<*Sr;)PY$hw@`A1rCSP!p~2Bz&4)0f`Bv*U0~yz~h29VRQvtcWmzRim zP76FFnC0@yAP$LYirU4?gcOVtpnQqNRkbAR3dqXz++gJOhUIXeOASHJASU*Y;F<1G zv`8#l?LSW(zUuP6>e_hXG~pMNQ@HAs>i!ALIQ3Q=-f7K};ES%>eS3X6H?Rh8k*JWH zz=z_#ki8K!MCeFS#3dbAl+sCmH;TjTM&G4W6dp5Z3hz2(^?_+lgR;5!VGuMn0~Y_4 z5{$;kOCMe^xbu@2NlexyLJkt;Xe0vZAb?rIjMR9r$U_)jOM)IP`bn|0I7WgR4k~mV zgJWDVKJPbr9lD=yRMDe2+1KjT@ImOkRPiSBd=uO7XlgBm*`GgT2gyf~Jus-WBQ;Et z2CIG;-KY|kT?st^ws-buylJ{mghtI2XZ8zR+C88^_}7`jg~2lNuaZSp(rf$NADPL9KUT`0+MD=-_zCQzOO#6+0M7JpW z=RNhG+QD%tYc|^X*5*4*H~C*};M$>#B*|fJvM14SW0(tANX~Usde@loii&OF&cb^%~XeCLV z;S>=C6vq96JjVNi>R#~1)+WedcSlH}(g*gQTl8Heh2Qjj!+7gc3GiS+`Q3?gkg)z+D~bM*%x2mnfIP`Fj`D7GmjezcgZ6sa}0+Zy&<3OK+3nMx7bt7@7OJe z=J;_=%2v5wWcaj7-vSRyF%}9jXsEZo9Lf=vq~(p5bZ0C5I2$f-L)-RJP!5WjU!}Y& zkLZJ;)Q>?ZMyT(*G2&9B-}hXj^9!!{?Ut?A_kor*6mNv_DrxlceD(k?S}qtBB091m zVj4@@m!dNI65%3ppZ*<_>=^C0)SsE3Atgp9R#+R(%EWO96YQSJ!<_Sl%AziX4Js28 zCP`Gczn{@5%!~BYf{T?;izG6PN3E?GVXPBzOQER_*_ezL3iYmiMpaZ4WW-B$tE8aj zX{|B2rQXa!kRp?RWI#I98z4j_WgJcJY*VACTr}C95)lUTm9NuW5N}K5v^zkq#B2p@ zg*>UArI5o6lYdu=L^+5~=_D`U^_9r#T^Do6&R2+g%}_m3xMqK0qt!$JwL!K>N^*q> zdql+TFN_S`6nO=F7H9oEAOz@5oIo@JXol0|a+SxQ*}uRRoFpS~_^JeYtfnWxjPWD! z+{vc;;$>K1mPb3mtvUc2wh4_4aubxdbLZJs_YBbv+8+b{gh-DoMx7R7jy?S z9HTiR;*?H*3U`(MRUBOeYsOlRUU?3kEtT5Um-`#nJJ|Et2g3I)J59*1`-_aYk*~qz znZqb8i1r5`zNAF5`w&>>hbKZ2Ez4HIAK3;vH~HwY2_vN$bY_HPd4kK87N@;hey;A9 z&4B^ncC4b0q;J^xwe17O1FL(?J9OEubA7~e5S0(bR>X3nO@W(2skjGSW`J|F%2hdf zAZe`T7$s(GMrVd7mQ4C7M*CMjV$NHUO+Ro9wqbKi0Gi~HD9QE1H1OlC*3Op7Z=bMq z1@TDOKX+EH)AcM2ng7A(Zp!;p@cK*H<6AD zmED3{I#tL>jKB0Bma3j97D05x%uMWx9Gl*$N$51uEQfF{`U1Mn7yNOkS*-%xmD#%r z_@T?Y1cgXO;j6$^ee-aieUL2gpzl3uHt*ML=`RG`Rps9n2*@!GzGZ&r<&bA2$EQNW zFNYPol;E8#|0ON{Q$o#Bfibb6;A7a}GeRRWO;(_Ryxw;@+<()&b<<(~GHa<^tb>Yx(jDx|_0~nVt;#O6pfqkea z@_52wpy6UI`*<6xfvYHuWU1{j-Ygx}2SIvte|fi<#bQOE*T_xIO=M0~G0|3#kmwMn zR-Ddvn!CifRK!#NyIu5pGMk-KsgF2seiOsNnwj@2YLO~R{}i54Jb&Mp3a;{ZNycTwiD z>CB)TT*=65q=6()=qCmoZhjr8q!lXc9-vItjp zm{n!9eoI0BAeBJkhBihok!qKAm4_Y8XE(zyEO0Sr7(wVzT9V^rzcG9EIdx7FGn`^f z*_UzIzr}*JremFv{cWC9GUwB-tQ>#p#J(tnZ^`YFdQ9J-I!9`kQm57|%ZJ%-7C@Ya zGSXyhQh8^U0cyvoSsiY@^uF%{4{!OO4%{lTmQAH6cohEPc)Y?EY-Frc2xH)m78 z;Su!Hxrfphc@{%?cZ4Tl{xWW3u564XHm;fh<3Xl9Fph^JaOKm4!eOr|AFEU9 zgobf1Px*{yDZ$L%UQRm}eL<~xKioH!^y{)_WN46IkclWRel%KFwzTB zjrwDKbToNz^iMXoBlknk;4dcx8Ppcjx2KTw4ubMF#PYV#<*XWU7s}aN@{DBcuK)v% zF)8>z&9ITpY#-QnO{K0x!`?;Fp5 z#9kldb2rTAtDbSBy2R6i-?DQmq#`kjBvl7=_nKPGgg60b=Ha`5l7SseK28m4l@znN z!eRy%I8_p8R|A9Qta|;pyc_|b%5M*Qo9IJp_U7MfjNr~qOYSXsa2hymo8Ja-bCI%H z0kRmb&BmO}-eGeb6J%*ddLA-g>5T>-k22A$my2n>?WR*~0)<0J{C!q0mp@)YZ$F<< zw(IvY`!!pLY&V?Gqn>}K2(2uyJ)Q=Nr(&EvJk|QXYz0qGbQ!N?2)7yq&i@7m&%@=0 z<#I)#7nHdqv|74HiORGNNdu|DQT!?HG%MZLC3FahmaTTocZ@9Y>C3b8+*p7oE;kdH z1(OfkB!AiO{y$V&Wfa}F+Wv-3W;v`l?hQzu@~`K9j$6?9fYM@Ol{1&O{f-uFF1zKC zc?tTyJtw_0cvlzL6FkzG;1WT2IS1TUNU3t@Hc%bbquWUf3fM3x+ouxm%58YSps)Vw zhgZ~YlX+kSgmDE&atuoHmF*rWxM%yq9T}}TNxD79rS|YTUiGO1v*g>`+rH8KQTJ$n zvAUf@HSjy>H`jFb(MEP1fRY}gHN;Kf@wTzu)flZ^5qnZdk_)aitc*%eck=lQ`=-+E zsDUc%tZZkwH0UrbeY>)fq~DE^=lkJv_79P<1YEYOjmG`4`;_W$b?ob>_*ZP%&|ywL zpcwq$!2m=4`>A!+J4gSsbm%*|+-hYJLW?|=ZoCt0=|qdb`Idg_^Pcl%A&rL;Zp|lC>t&O%8h#bq1gIJ-cZH+yb-(tz6MUezh}q zxL0E#81;}i4gSQ)mz&$;@i^5VFui5aDX4lXCZYSvXj@SiF<62WMd$-kV-Y61`;lYI zX(XXUH1USmWyZvGJtNUaJgLy&>B0F+)b?9lufEYV`p&p6`xwDcm&Xr-l(zBNtwvux zF$McNV(*9}SY3%>#oFn{LYRuEyJi>_^d2hXG*_FK>KZ3#4GXW0#L#KIZc?mB%#y;V zr)oB>Y)P778PAuy+2cQIxx0+E^QaasqU85o8m=&Ivr5zicg3E29kJYYJ=YzvriMN` z?PXIS&{HeHBJh#${q44Blb_Qx<)nmr%j2X3xbXlR5AWsczk|i9r{O3JcQf zBd3><4}4v_)4^nF+5sopvn4CYr~E^Z&uiYaCwGNbCcDi+eLS3SxjW!tDXNWy-z4FE z&c$$IU47$q%Ti0|*-P`9?b8l^a+-?Ql5F-CYxJ%!;`reBx8nl&@IUKJorFDs-b3C_ zsIO#GxCBLY#LEub3bnpm=_+K%^VdDLr>@P z(@cLY)}o~Up#c*!_`8$tVCN1s1*+}DYB6Aab|8CcA5$59l#xDEIuWFM0y1ks!9#@LG@|ricGsnjog+?&4u;H zs?$1aAHvCQ$F_PapJxv>yuT|QZ+F@s2c#A*kipS1EzN1OQOzbbKN%Lsns2b0G$`w& zv=ci_9-^ADTr?*16T8!@OP1Me#BBy`sBB7X%xwqoYEAVEQb zx_$0)?TV+u!P|FwnO%&E*F!fATO4*2oAJ&`O zqE*7VZE-q}>Lf?=RT@l&##sjAz0oAK2Nu5!#>*`9#-%@_Tjc7z0(8ePKfBvv(*55m zLM?3s)+%@I(5!s)zD6o`u!U}?bqlw&9W$*4MQS{oa{~C?pUUQ;6H|TG1jOAKzTx((bi?o`sv2J z;6}$z2!Hkp5yx$BSBULpC~f{D zB_mU`D#m{P=20yX*QwUDRu?aa$6<(8YbnQGrngbQW^hsqee%;wI=LU07$fn)EcdbY z9SulCVrhSy#Gy7Q(d=UP*z6o{({@A{5{F)36iF;3-{6r)-8H zDa8O%5-F@!76K0mAx-`57gp+|F$UA3#UpR45sTIrsfN0f-oxhI3Q5xRZLbuG4BvX2 zarAB1Y|1Mhi}nXIH{EPB4Bxc|OYJNSD!iMk-XmWU3Az)tE}!dwc@A4*S;X3n1IiY4 zBXK#t%b9|3O--nHNKEoJZfdqeac6$l>x{=IUOS+Wc7j7tEiF}A;y|KgB7!i5ry6yl zEL{fV*wczE;GNwfo#a4+i~FSg{R5q>sBrD7>zs4>r@_lriSI`1^v0x6lds96zX#x) z_eafITwV48^8B%lU;U8#=oLGHy{~$&=-#r{U8lzlqtWU20Mou^9j?!jjGoJv$#~(~ zbpoF&h_^4mkv1v`@2_h<34%}m!gN;PjuVYaMTC^%(T5k7B8g;!y%BU9z;vyj4*98z z$#Bz`xqL8RbTGBl%~JchsFKHw@*t~lMv@@q3(YQYNqN1H5(ouhrMi!CcYgZ=kQ^Vj zNuYj$KP&dWRL5 zOG#vnlr!lvYyU$}m%g@-g#av8UL$o-X;#iDj=eDJD6YAIunxgXdJAth?B-EOlW`hG zs*np~6ibsmVAE95WE(d(Z=}}7SsZm8(NJAZG=Aap3VB9db!)`jSzoAtI8;C;7HoVy zdL;Ht5d?ldqF%zmqNt=rikZznZ>p^i5N(ueK+4K@|U@=C&@Ns z5-3%(u^(x@!UV>0L}uCuBdggI3J>2w7!jfu(1o<4QV3KUDr8oqu}K`bC zk?|wDk(>)J*TD-fk?WS1PftqJ)syDKhVI2`=Rey}IM<-Q$2;DsnlGAh|Duu~b+147 zpdzxcbK88IaLLlwwVO%pZB0yct14jddVEOkG04m2>Lg*Xw_fSq6Lf!jA!yy+yxs5y zI=>u*vOh7rn4Z?kcE9}W43k09mX5sZxza=xf)|wkdlQ)l#l!{PkJ= zx88;RHIP|B`}!3TuSXeQ;2|d_0+0$TcH-yS7Vu11=$J(UzCb`i!+*OK{O^N?j@k#$S-60>N7L))TKuY=>@Ax%@#Qd+2e&54}^QhICAv|HS+Qej5FrY)_W+km`&l%JyaV5)T4z zU$u{<#HoUPz(7UszjTuB(Dci7gxaoY%AavL{{W)>*X3_LUp|}clk*cODB(!qM=W5? zl;mLiQ-(^WzWtBR-dG9KQNtGC{)dvlZp9~M?16s=W1aVt(PI>C_yQfc@*4tgw=_st zW>3W;qN}*=Ulfyy^eTq^c8y-K5|%IdDfM;d!$$1jzi8h5H$cRLW?^oF&;*14()`T- z0|^D059}Bi-(TI-~6ZH z?|lEnH~LxNZ9`W&Ra~Dn*E>8)k0Jt z;2|s6p?veMiWnngbIAUOF;&Xmsay_8IpF?7VE!eBUkc2>v(jL;l`xASQGx6K2hk_< z-K-3V1&sePI$PQQj^}DC{}c07cyp-#=Yb$1Y+36}ID7wtct_uo2Y!;?1&1?7eD5&6$Nfu0jlNSBD{cHAW3adNro>DNCy zwD@l{y~@c>+BI16y+b%WpCwM zr?0D^HoiK(1}@9?`5%3{V*j@T{rBknzlQ1mqwx0m^==f=!f)-RJmu4z@36bM0FxHy zIT2o2D$>2cBP0XX3D)Gv!MU`1oNhPjsl$7nroW`X(u2b68B|tRM?hu`U(nXSHDymC zdL4Qdqa$#dme66Y1->o!tvOvXaI=xxT&l1?(**PqDFa>>a>(2)v^I$}9zzxcjolo8 z9jF)e9m4r2&x-{RZf)E1r4P+;i^M%^7&ifkRe}`O4h$QDJESYM52EFU;LxG`Q4Kd) z?^kgqZ`igaU;Q0XgBBJ@<$egaD(*sin8FUv4oE_)8e=zxpD3^kc6I_eIh&`gUnK2y zp*QL~HjC^ZE7n1a;DR4+-ex~Hh4L@1dId)S@YZ?fYOiKJ9cxghwYk_W+fYCpz4$fM z<|p01?G)t?fXa8jmL7r&gBFsgS24RCzJ-q;Zoo4AO|Uwkzm5cVVf?zB9}@889~Isr zn|lyxEe}=RYdVX9U`IdzV!8L*Y3`P^`-6yux8M)AI?c_qVEzHtHXOFchd-MI7qKD} zj&*!9MJOTEyd*K@uigvqT^rx-;MNx2uLR)#`Fyp2dGk;qR`VaSD#XBV-`%RjW`4oj z<8qBGOve&l(IUklzb+a-fi=VXPp^!Wh=@;=CEu)fwnU0G1&^)Nq58ui!KcQ!w740G zBMY5-G*?2dkW69<%vv>eD4w#nV(NnjFZi0tgIp^cIwnrX419zSCb}Y;C-AG*Q7vGs zT9Nz#JhpTmWcP5;w4UbqG46%+2?{s97g1O1T3`Km&Al74Mss^}YtmYuOmfQv&&pis z$qhklKa+Xd2A9RX(ScDxQ71ESaq;wu!~5kvFzp51LfjM}p1ZE2t*mSTuCkh}qn%=^ zu5^##IX7NbiY@780Z8pNN+3)w27he5S=UW#Y}mQ%AHeDa1*c95cH`$L&(EmYUVgaBE#i)wz2SQIv10n>ZmNZe3b#=H^TRYA#?OK0KTf zW!Cqp9L#1&M#;C|f9yK;bPuQ)>Y!Z%wW^h3w|-gGa1wO0pVm-O!o@qUJ!e0St^!Fw!saYjRxlm2X2S7N3`ND|FVE|4 z85|H=S#e+k)p-%4T0F7(3&?`Sgl*aQD<`Is=%cC3Xx)}x7>HWLp218xX@Akg`U3h5 zHB~LAfs!2r@4=o#tx-b(+r+eSZTEHu?ax0H6~jTrjK}qO3}Oa`If|E+^DDkNb|vjO z#`^IF^Va1kTq&@B-igfbgXPWNTXQSZf}(z<*LIBAHa)gjhZ8dEOSZ|VZV=d?w(>GC zxe3X8y5%j-VKP^hmjiz54|ZLY{<1Sj9|tcj@|=-Rb&{nQ&DkL?VZMm=T`-3);yPlr z>D=1+K4?e+{0sr%Oo05h;ROTY17LaR`8Gx zfJbFb6MGJ1LqxIGitTIhwHgo9e_Bs4eo9Uxpd)vjhp?TD#*rWrHS8d3pR z+f?5pnWdYLcT5j|_^2tKS!sL!kz};0G-5$=^Uo6DImLZT!CS#vt)6C&;j%$(#TZjF zYAaR-f^&_67^tYbu(fhmXj*5yR8Q8#WM6j<4%v#l7bBAMBTdH0Pa>#CvK`RDPqA1*8S4%KS?&+NLTsiGx+ zT-nsl#u*9q4BSHWF_u)&=cfu9*VnyVKJY%fdDU-4?^@MMK0`Wdo_fihTM`Y|K5FD8 zhz<{$dSI(^C@wCZoVB!Vio*pFQia}|>&WZU+A>>Ja=c1(AllG(g;E9Az}o5i6>V;Q zYgAt$d?%t%=&>v2FuRyvO2@=s)3JqtsxA0Cl(1>Unq|uwS30*b6jf;=nN{FnG;=Y} zb-_ zNTGrXVv@1_b#?S~izo{joSUJE%w4Q=Y#EwGL|n1W0aE32N64|wn*68yNWn0D=*d09 zH(PZ+CC5U}`kBu=X)-zl(NTuu^N z%BolRQyebvDp^_{TNk)twnUuifRiN&4Dfa&P@03q1d-lfp#OA+?cHERs3CvyP2Z0d4eX6sH`J5j#eg2k79qxxQb~ zo+E21!W@un^eQ-3R`6?C{J!Qy{TDm=!=+8loI1B*!Jd8jt5)F3COlCgeJj$H<8lVP zB@I{xzA;_BoIbB%DTkcRg>!e{AzW7r7~j<1IDJd#y!o*8$hw1o;8tFYxvFl;rWRSE z<1Fgz&fY>yTeW@Cv;m06B!6KK>`&pZcHvlkJtY#8V?OGKMpoVP2<)qidiIyqh~h|# zwe{ViI49N|p+SpGWHn{LmI-BZcz0LMm3}I>)(&Qy+9m-dYh3$TeEu^yNsex#s%5J> zT4ddI-%w7$wWZxt(us670AGdrF0n2DgMZ$D%&o?C z7sh0_iRN!*$~1Ok>b-l)@u@oQ0z`NEuS!VvM1d@me-i1jor>13bbk2x%Hr{AA3 z+3zL5=qxOTo^w9uu8E`%E^a_FRM&0m>Z>Zv!M#l(@^mhCadoWkbR1{x$U zomkzX0zx;j^)HOtCY0AOY#aEaeKX%o!iDv4uK*dkw)bS?BHtljX=i5j@MZ$wn^(o` zwx*QYK;%Zp&fHua`}bHNdI=qCn{b3ptS#oOn(Dr#8Et;8WA_LAtj&*(DIo{nZr|_I zxV7acj#lAff2DK*1+D!%tZRfqruG(pmAv)ym@T6{UT#F){pD-@J@N%J4AC5)tz%j^ z!D#P}lCoBYe)UtvNRMzfr_Ci^;j{cIGqZ%{b;q@pEMS6a8zwwftt!x(ZCaF9)u!Q& zEnUsy-3npH;c0$VW%~v?VZ}K6Px3>1P?39mweWF+XHaWF0FV{fHfd2cJ$=t_OElQ` zBYo)v;PnbF0sF3DgElp0=gg{^odQ41o#R4+a_!@rs>$kVL4&pnnm7UU#}^F9FF8|2 zZOlgFRkz{c`kdwkN&tQNoMqmPXqY>g6eXlplIXI)rZBw=nv4cuB>!)bIw#a{M8 zq=30lu+TnI%5PwrHj8xyJ^QVY-vBzfvB=WZ%( z;XN>;iT<#F^i0ld>bvS_CB>g|dF)`laL4tU9m`3otMe|QHJ_N9Db=-IiprxG>YJJ~ zSo;wd70_K$($)iM$qYv2;#Af z2gxVdHF&`FxOV#od1)tLb#)8-CT#tzhA1t~plQ;UkpBMEXUQ)Iw6w8UQD=kX9ufl! zl2ttcsE6_E4|Tu1^aY|c(O90g@VmYDT{(73SlH5n%OCNx#y6qJG!27?HOrbfK;BtC zp%gjaW%%rJmMxsDWxTRCiR>V1>Kdl4-7x#xIQ|iNI!<4mX>3?i(q6=Bg!&SE*yokk zU5D|MetBYdjpd4DMWhkEb%u(1x2WSLVfTG`-mZcFEsak%wqj+LC;RfBS*U$Q)gll! znA5<{Emv&Wgf;6nNFlg~d_zl_V9s(<+J(gt*;-B4QIEV#S`ig!+J`Bc#W zyB3KF5eRN!Ys6Y!yy8JKv!>RTv%IGW)l)%S4krl|(?+A<)2h>Jj(9a7sPE4SVmfwO zie`Gid4*kNYp*q}{nrI8*q3lQ{Mb4Zv*J?Qa$s49swr*NGLpwwX4XqESTNr>wOuAl zZ<62t{7##pe6UX9;e4I- zdGgDT%J+4iWnBTu?H3ptv=`C#{{AG1uTiYu`q&$tX|YZ+PHg*mOk8&w6`r?2PVnJ9 za(6+!@I2nI{?KC#e_0`-cqh2;V{;GUyi7`$^LFn@k0G6HH$^<$4Qj7kwtty?>38D( zV7~=G^{m`G9|zJ1FQN{;XTR~9f^{3GsbYuMjw?N&v6;qL;%EC4I;^k?PUYmqmx zH4mo0&#=)_Sp4#Z{!29PR^x`vGE2K1h3}6BF0p6(>u+v(duv_%+j@Y(vR%$q$>^S^ zd4D@EyLSrSLw{UZzojkr&GtYG9WP>*?w83`s=Ceddt&`t^Ldq{&6Fgb=k*1)aK6NesM<&MvS-7JS^Rh@0fgO)*kL*i%~!K4;?4u@Cl z+1H9@uFKhnDF=pHe`n40b?Le-z)>d3c&^RhiCr2ZAZ}WEJFnezHEaLFlcZSV^Es!h zE@q4R-E6O6^0tR}U*=ZLe>tJl(?!whWE^#ay}Iq?S~oXROP@w@$liwM{2S$!i^S9KeTy~XRpw<9PO16C=A~6@y+iC({y^UUn1D4EM(N>K5A61|n~Bsho?UA;9LBr^9&C zMD%>yPt#HWPtz9r%skey-E^}DAM+$6@0}istOXC|g~R<_dC_792*jSFm-{UN%Nk5# zdC&Csbz<+&bb#t{xc_Iw3~tiCkV4I%H0pLo66Rh8J!)pYy6%g!hU;(({U8?=1PIost&YZ_r0;;K=yt`{Xc& zP}r`iqcVo9R+?Nj*&ZYBbdrnbz9HPG@N&Xs_vV<-aMC5p{I=ixfDb73pY1~*hvzK*>mu zC`!&rkR(Wu^!4E0XX`$keK_~tbMJTG_ho)Ct9y0N>gv_2tDdTQO5m{L6X_Dw^1Dx@ zUmqlUwZ*gTydw@c?GxYb)fKNe z_^yMQXZ;q{i-oti@wkFg<^ueZ0#fvwn{>Vwou16@`;X|B^pooo#HOaCr#zrx?h*8M zwh2(Xn{iwpV?U+Dt8UrfWpIz8FMmU0YGB^U1aXX~XRBOz{GM^?G0%cx<+})GcjH?; zFX$igG{rA#J9=TeN7MV7byhO-xJqIw7``|ALvo|edA`mOSLhCjEG6>hfYSo5(kEKx!-g3CFSKtX z=#S<*?X2e7Bw8W!oe_eB{h@tnS3N_zbibFN&Jn>ZPIl=6tymmP39g${OM!nB+ z^Q~ED1M?xWtm||_*O$FG&kYU8enEU)(!w&tkyv&kNMggRlLBn^X{YLPf z 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 N1e%hQ^Qw~c{{zkZ^1J{5 literal 0 HcmV?d00001 From 8e770b5355c3b37b328c36b4499909dda3dad5b8 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 19:35:46 -0700 Subject: [PATCH 44/78] Update JNI error codes to match new scheme --- src/java/ZeroTier.java | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/java/ZeroTier.java b/src/java/ZeroTier.java index a123910..7015c73 100644 --- a/src/java/ZeroTier.java +++ b/src/java/ZeroTier.java @@ -1,28 +1,15 @@ /* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (c)2013-2020 ZeroTier, Inc. * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * Change Date: 2024-01-01 * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2.0 of the Apache License. */ +/****/ package com.zerotier.libzt; @@ -37,7 +24,7 @@ public class ZeroTier // Everything is ok public static int ZTS_ERR_OK = 0; // Error - public static int ZTS_ERR_OK = -1; + public static int ZTS_ERR = -1; // A argument provided by the user application is invalid (e.g. out of range, NULL, etc) public static int ZTS_ERR_INVALID_ARG = -2; // The service isn't initialized or is for some reason currently unavailable. Try again. From 3b0d8e81a0d9f2c6f7608446c08f5249b08b4496 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 19:36:48 -0700 Subject: [PATCH 45/78] Update distribution script to output new directory/documentation structure --- dist.sh | 65 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/dist.sh b/dist.sh index c7dfa17..3a4208f 100755 --- a/dist.sh +++ b/dist.sh @@ -11,6 +11,7 @@ # # (1) On packaging platform, build most targets (including android and ios): # (1a) make all +# (1b) make wrap # (2) On other supported platforms, build remaining supported targets # and copy them into a directory structure that is expected by a later stage: # (2a) make all @@ -19,7 +20,7 @@ # of packaging platform. For instance: # # libzt -# ├── API.md +# ├── README.md # ├── products # ├── linux-x86_64_products # ├── linux-armv7l_products @@ -29,8 +30,10 @@ # └── ... # # (4) Merge all builds into single `products` directory and package: +# (4a) make clean # (4a) make dist +CMAKE=cmake BUILD_CONCURRENCY= #"-j 2" OSNAME=$(uname | tr '[A-Z]' '[a-z]') @@ -106,7 +109,7 @@ generate_projects() if [ ! -d "$XCODE_IOS_PROJ_DIR" ]; then mkdir -p $XCODE_IOS_PROJ_DIR cd $XCODE_IOS_PROJ_DIR - cmake -G Xcode ../../ -DIOS_FRAMEWORK=1 -DIOS_ARM64=1 + $CMAKE -G Xcode ../../ -DIOS_FRAMEWORK=1 -DIOS_ARM64=1 # Manually replace arch strings in project file sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj cd - @@ -115,7 +118,7 @@ generate_projects() if [ ! -d "$XCODE_IOS_SIMULATOR_PROJ_DIR" ]; then mkdir -p $XCODE_IOS_SIMULATOR_PROJ_DIR cd $XCODE_IOS_SIMULATOR_PROJ_DIR - cmake -G Xcode ../../ -DIOS_FRAMEWORK=1 + $CMAKE -G Xcode ../../ -DIOS_FRAMEWORK=1 # Manually replace arch strings in project file #sed -i '' 's/x86_64/$(CURRENT_ARCH)/g' zt.xcodeproj/project.pbxproj cd - @@ -125,7 +128,7 @@ generate_projects() if [ ! -d "$XCODE_MACOS_PROJ_DIR" ]; then mkdir -p $XCODE_MACOS_PROJ_DIR cd $XCODE_MACOS_PROJ_DIR - cmake -G Xcode ../../ -DMACOS_FRAMEWORK=1 + $CMAKE -G Xcode ../../ -DMACOS_FRAMEWORK=1 cd - fi fi @@ -209,8 +212,8 @@ host_jar() # Build dynamic library BUILD_DIR=$(pwd)/tmp/${NORMALIZED_OSNAME}-$(uname -m)-jni-$1 UPPERCASE_CONFIG="$(tr '[:lower:]' '[:upper:]' <<< ${1:0:1})${1:1}" - cmake -H. -B$BUILD_DIR -DCMAKE_BUILD_TYPE=$UPPERCASE_CONFIG -DSDK_JNI=ON "-DSDK_JNI=1" - cmake --build $BUILD_DIR $BUILD_CONCURRENCY + $CMAKE -H. -B$BUILD_DIR -DCMAKE_BUILD_TYPE=$UPPERCASE_CONFIG -DSDK_JNI=ON "-DSDK_JNI=1" + $CMAKE --build $BUILD_DIR $BUILD_CONCURRENCY # Copy dynamic library from previous build step # And, remove any lib that may exist prior. We don't want accidental successes cd $(pwd)/ports/java @@ -259,8 +262,8 @@ host() mkdir -p $LIB_OUTPUT_DIR rm -rf $LIB_OUTPUT_DIR/libzt.a $LIB_OUTPUT_DIR/$DYNAMIC_LIB_NAME $LIB_OUTPUT_DIR/libztcore.a # Build - cmake -H. -B$BUILD_DIR -DCMAKE_BUILD_TYPE=$1 - cmake --build $BUILD_DIR $BUILD_CONCURRENCY + $CMAKE -H. -B$BUILD_DIR -DCMAKE_BUILD_TYPE=$1 + $CMAKE --build $BUILD_DIR $BUILD_CONCURRENCY # Move and clean up mv $BUILD_DIR/bin/* $BIN_OUTPUT_DIR mv $BUILD_DIR/lib/* $LIB_OUTPUT_DIR @@ -333,7 +336,7 @@ clean() rm -rf tmp lib bin products rm -f *.o *.s *.exp *.lib *.core core # Generally search for and remove object files, libraries, etc - find . -type f \( -name '*.dylib' -o -name '*.so' -o -name \ + find . -path './*_products' -prune -type f \( -name '*.dylib' -o -name '*.so' -o -name \ '*.a' -o -name '*.o' -o -name '*.o.d' -o -name \ '*.out' -o -name '*.log' -o -name '*.dSYM' -o -name '*.class' \) -delete # Remove any sources copied to project directories @@ -451,11 +454,11 @@ display() # Merge all remotely-built targets. This is used before dist() merge() { - #if [ -d "darwin-x86_64_products" ]; then - # rsync -a darwin-x86_64_products/ products/ - #else - # echo "Warning: darwin-x86_64_products is missing" - #fi + if [ -d "darwin-x86_64_products" ]; then + rsync -a darwin-x86_64_products/ products/ + else + echo "Warning: darwin-x86_64_products is missing" + fi # x86_64 64-bit linux REMOTE_PRODUCTS_DIR=linux-x86_64_products if [ -d "$REMOTE_PRODUCTS_DIR" ]; then @@ -515,8 +518,8 @@ package_licenses() cp $CURR_DIR/include/net/ROUTE_H-LICENSE $DEST_DIR/ROUTE_H-LICENSE } -# Copies binaries, documentation, licenses, etc into a products -# dir and then tarballs everything together +# Copies binaries, documentation, licenses, source, etc into a products +# directory and then tarballs everything together package_everything() { echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")" @@ -526,27 +529,27 @@ package_everything() # Make products directory # Licenses package_licenses $(pwd) $PROD_DIR/licenses + # Examples + mkdir -p $PROD_DIR/examples + cp examples/cpp/* $PROD_DIR/examples + # Source + mkdir -p $PROD_DIR/src + cp src/*.cpp src/*.hpp src/*.c src/*.h $PROD_DIR/src # Documentation - mkdir -p $PROD_DIR/doc + mkdir -p $PROD_DIR/reference # Copy the errno header from lwIP for customer reference - cp ext/lwip/src/include/lwip/errno.h $PROD_DIR/doc - cp $(pwd)/API.pdf $PROD_DIR/API.pdf + cp ext/lwip/src/include/lwip/errno.h $PROD_DIR/reference + cp $(pwd)/README.pdf $PROD_DIR/README.pdf # Header(s) mkdir -p $PROD_DIR/include cp $(pwd)/include/*.h $PROD_DIR/include cp $(pwd)/ext/ZeroTierOne/include/ZeroTierOne.h $PROD_DIR/include # Libraries mkdir -p $PROD_DIR/lib - cp -r $(pwd)/lib/$1/* $PROD_DIR/lib + cp -r $(pwd)/products/$1/* $PROD_DIR/lib + rm -rf $(pwd)/products/$1 # Clean find $PROD_DIR -type f \( -name '*.DS_Store' -o -name 'thumbs.db' \) -delete - # Emit a README file - echo 'See API.md for more information on how to use the SDK -- ZeroTier Manual: https://www.zerotier.com/manual.shtml -- libzt Manual: https://www.zerotier.com/manual.shtml#5 -- libzt Repo: https://github.com/zerotier/libzt -- ZeroTierOne Repo: https://github.com/zerotier/ZeroTierOne -- Downloads: https://www.zerotier.com/download.shtml' > $PROD_DIR/README # Record the version (and each submodule's version) echo "$(git describe)" > $PROD_DIR/VERSION echo -e "$(git submodule status | awk '{$1=$1};1')" >> $PROD_DIR/VERSION @@ -566,10 +569,10 @@ package_everything() tree $PROD_DIR cat $PROD_DIR/VERSION # Final check. Display warnings if anything is missing - FILES="README - VERSION - API.pdf - doc/errno.h + FILES="VERSION + README.md + README.pdf + reference/errno.h licenses/LWIP-LICENSE.BSD licenses/CONCURRENTQUEUE-LICENSE.BSD licenses/ZEROTIER-LICENSE.BSL-1.1 From 18fd449c84c3659cf45fddf4b601966dc739ff1a Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 17 Apr 2020 21:41:24 -0700 Subject: [PATCH 46/78] Update lwIP to forked and patched version supporting 6PLANE addressing --- ext/lwip | 2 +- ext/lwip-contrib.patch | 32 ---------------- ext/lwip.patch | 87 ------------------------------------------ 3 files changed, 1 insertion(+), 120 deletions(-) delete mode 100644 ext/lwip-contrib.patch delete mode 100644 ext/lwip.patch diff --git a/ext/lwip b/ext/lwip index 3bb5b07..95d6387 160000 --- a/ext/lwip +++ b/ext/lwip @@ -1 +1 @@ -Subproject commit 3bb5b0722ea01edd8dee439ba62eeee788d2894e +Subproject commit 95d6387123171d812552cd6213034c823a7e8631 diff --git a/ext/lwip-contrib.patch b/ext/lwip-contrib.patch deleted file mode 100644 index a606f45..0000000 --- a/ext/lwip-contrib.patch +++ /dev/null @@ -1,32 +0,0 @@ -diff --git a/ports/unix/port/include/arch/cc.h b/ports/unix/port/include/arch/cc.h -index 80b37d8..ed219f3 100644 ---- a/ports/unix/port/include/arch/cc.h -+++ b/ports/unix/port/include/arch/cc.h -@@ -32,6 +32,8 @@ - #ifndef LWIP_ARCH_CC_H - #define LWIP_ARCH_CC_H - -+#include "Debug.hpp" // libzt -+ - /* see https://sourceforge.net/p/predef/wiki/OperatingSystems/ */ - #if defined __ANDROID__ - #define LWIP_UNIX_ANDROID -@@ -65,7 +67,7 @@ - #endif - - #if defined(LWIP_UNIX_ANDROID) && defined(FD_SET) --typedef __kernel_fd_set fd_set; -+//typedef __kernel_fd_set fd_set; - #endif - - #if defined(LWIP_UNIX_MACH) -@@ -76,6 +78,9 @@ typedef __kernel_fd_set fd_set; - #define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS - #endif - -+// Comment out the following line to use lwIP's default diagnostic printing routine -+#define LWIP_PLATFORM_DIAG(x) do {DEBUG_INFO x;} while(0) -+ - struct sio_status_s; - typedef struct sio_status_s sio_status_t; - #define sio_fd_t sio_status_t* diff --git a/ext/lwip.patch b/ext/lwip.patch deleted file mode 100644 index d0ef7c8..0000000 --- a/ext/lwip.patch +++ /dev/null @@ -1,87 +0,0 @@ -diff --git a/src/api/sockets.c b/src/api/sockets.c -index 2d231739..a0c24829 100644 ---- a/src/api/sockets.c -+++ b/src/api/sockets.c -@@ -38,6 +38,7 @@ - */ - - #include "lwip/opt.h" -+#include "ZeroTierConstants.h" - - #if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */ - -@@ -281,6 +282,7 @@ static struct lwip_select_cb *select_cb_list; - #define sock_set_errno(sk, e) do { \ - const int sockerr = (e); \ - set_errno(sockerr); \ -+ zts_errno = sockerr; \ - } while (0) - - /* Forward declaration of some functions */ -diff --git a/src/core/ipv6/nd6.c b/src/core/ipv6/nd6.c -index 039b7963..6647c90c 100644 ---- a/src/core/ipv6/nd6.c -+++ b/src/core/ipv6/nd6.c -@@ -1644,9 +1644,27 @@ nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif) - * addresses (from autoconfiguration) have no implied subnet assignment, and - * are thus effectively /128 assignments. See RFC 5942 for more on this. */ - for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) { -- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && -+ /*if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && - netif_ip6_addr_isstatic(netif, i) && -- ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) { -+ ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {*/ -+ int prefix_match = 0; -+ if (ip6_addr_is_zt_6plane(ip6addr)) { -+ prefix_match = ip6_addr_6plane_cmp(ip6addr, netif_ip6_addr(netif, i)); -+ DEBUG_INFO("6plane, prefix_match=%d", prefix_match); -+ } -+ if (ip6_addr_is_zt_adhoc(ip6addr)) { -+ prefix_match = ip6_addr_adhoc_cmp(ip6addr, netif_ip6_addr(netif, i)); -+ DEBUG_INFO("adhoc, prefix_match=%d", prefix_match); -+ } -+ if (ip6_addr_is_zt_rfc4193(ip6addr)) { -+ prefix_match = ip6_addr_rfc4193_cmp(ip6addr, netif_ip6_addr(netif, i)); -+ DEBUG_INFO("rfc4193, prefix_match=%d", prefix_match); -+ } -+ if (!prefix_match) { -+ prefix_match = ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i)); -+ DEBUG_INFO("traditional match, prefix_match=%d", prefix_match); -+ } -+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) && prefix_match) { - return 1; - } - } -diff --git a/src/include/lwip/ip6_addr.h b/src/include/lwip/ip6_addr.h -index 29c2a34d..205b0e1f 100644 ---- a/src/include/lwip/ip6_addr.h -+++ b/src/include/lwip/ip6_addr.h -@@ -160,6 +160,28 @@ typedef struct ip6_addr ip6_addr_t; - #define ip6_addr_netcmp(addr1, addr2) (ip6_addr_netcmp_zoneless((addr1), (addr2)) && \ - ip6_addr_cmp_zone((addr1), (addr2))) - -+/* Determine if an address *could* be a ZeroTier 6PLANE address */ -+#define ip6_addr_is_zt_6plane(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xfc000000UL)) -+ -+/* Compare first 40 bits of address (ff + first 32 bits of nwid XOR'd with second 32 bits of nwid) */ -+#define ip6_addr_6plane_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ -+ ((addr1)->addr[1] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[1] & PP_HTONL(0xff000000UL))) -+ -+/* Determine if an address *could* be a ZeroTier Ad-hoc address */ -+#define ip6_addr_is_zt_adhoc(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xff000000UL)) -+ -+/* Compare first 40 bits of address (ff + first 32 bits of nwid XOR'd with second 32 bits of nwid) */ -+#define ip6_addr_adhoc_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ -+ ((addr1)->addr[1] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[1] & PP_HTONL(0xff000000UL))) -+ -+/* Determine if an address *could* be an RFC4193 address */ -+#define ip6_addr_is_zt_rfc4193(addr1) (((addr1)->addr[0] & PP_HTONL(0xff000000UL)) == PP_HTONL(0xfd000000UL)) -+ -+/* Compare first 72 bits of address (fd + 64 bits of nwid) */ -+#define ip6_addr_rfc4193_cmp(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \ -+ ((addr1)->addr[1] == (addr2)->addr[1]) && \ -+ ((addr1)->addr[2] & PP_HTONL(0xff000000UL)) == ((addr2)->addr[2] & PP_HTONL(0xff000000UL))) -+ - /* Exact-host comparison *after* ip6_addr_netcmp() succeeded, for efficiency. */ - #define ip6_addr_nethostcmp(addr1, addr2) (((addr1)->addr[2] == (addr2)->addr[2]) && \ - ((addr1)->addr[3] == (addr2)->addr[3])) From 925ee3a144bdd84d0b4d0f9cc890e98deb6ab0de Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 20 Apr 2020 23:48:22 -0700 Subject: [PATCH 47/78] Update cmake generator script for Windows (visual studio 16 2019) --- dist.bat | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/dist.bat b/dist.bat index 5041cfa..e538328 100644 --- a/dist.bat +++ b/dist.bat @@ -13,36 +13,53 @@ REM final output directories set WinReleaseOutputDir=lib\release set WinDebugOutputDir=lib\debug +mkdir %WinReleaseOutputDir%\win-x86 +mkdir %WinReleaseOutputDir%\win-x86_64 +mkdir %WinDebugOutputDir%\win-x86 +mkdir %WinDebugOutputDir%\win-x86_64 + mkdir %WinReleaseOutputDir% mkdir %WinDebugOutputDir% pushd %Win32ReleaseBuildDir% -cmake -G "Visual Studio 15 2017" ../../../ +cmake -G "Visual Studio 16 2019" ../../../ cmake --build . --config Release popd -copy %Win32ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\libzt32.lib -copy %Win32ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\libzt32.dll +copy %Win32ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\win-x86\libzt32.lib +copy %Win32ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\win-x86\libzt32.dll pushd %Win32DebugBuildDir% -cmake -G "Visual Studio 15 2017" ../../../ +cmake -G "Visual Studio 16 2019" ../../../ cmake --build . --config Debug popd -copy %Win32DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\libzt32d.lib -copy %Win32DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\libzt32d.dll +copy %Win32DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\win-x86\libzt32d.lib +copy %Win32DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\win-x86\libzt32d.dll pushd %Win64ReleaseBuildDir% -cmake -G "Visual Studio 15 2017 Win64" ../../../ +cmake -G "Visual Studio 16 2019" -A x64 ../../../ cmake --build . --config Release popd -copy %Win64ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\libzt64.lib -copy %Win64ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\libzt64.dll +copy %Win64ReleaseBuildDir%\Release\zt.lib %WinReleaseOutputDir%\win-x86_64\libzt64.lib +copy %Win64ReleaseBuildDir%\Release\zt-shared.dll %WinReleaseOutputDir%\win-x86_64\libzt64.dll pushd %Win64DebugBuildDir% -cmake -G "Visual Studio 15 2017 Win64" ../../../ +cmake -G "Visual Studio 16 2019" -A x64 ../../../ cmake --build . --config Debug popd -copy %Win64DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\libzt64d.lib -copy %Win64DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\libzt64d.dll +copy %Win64DebugBuildDir%\Debug\zt.lib %WinDebugOutputDir%\win-x86_64\libzt64d.lib +copy %Win64DebugBuildDir%\Debug\zt-shared.dll %WinDebugOutputDir%\win-x86_64\libzt64d.dll + +REM Copy example binaries + +mkdir bin\debug\win-x86\ +copy %Win32DebugBuildDir%\Debug\*.exe bin\debug\win-x86\ +mkdir bin\debug\win-x86_64\ +copy %Win64DebugBuildDir%\Debug\*.exe bin\debug\win-x86_64\ + +mkdir bin\release\win-x86\ +copy %Win32ReleaseBuildDir%\Release\*.exe bin\release\win-x86\ +mkdir bin\release\win-x86_64\ +copy %Win64ReleaseBuildDir%\Release\*.exe bin\release\win-x86_64\ exit 0 @@ -51,10 +68,10 @@ rd /S /Q bin # Build with JNI mkdir WinBuild32 & pushd WinBuild32 -cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017" ../ +cmake -D JNI:BOOL=ON -G "Visual Studio 16 2019" ../ popd mkdir WinBuild64 & pushd WinBuild64 -cmake -D JNI:BOOL=ON -G "Visual Studio 15 2017 Win64" ../ +cmake -D JNI:BOOL=ON -G "Visual Studio 16 2019" -A x64 ../ popd cmake --build WinBuild32 --config Release From 894d3bf41566078491dfd087d8edfb9299a4ad65 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 20 Apr 2020 23:50:21 -0700 Subject: [PATCH 48/78] Fix (some) Windows compiler warnings --- include/ZeroTier.h | 4 ++-- src/Controls.cpp | 16 ++++++++-------- src/Service.cpp | 14 +++++++++----- src/Service.hpp | 2 +- src/VirtualTap.cpp | 2 +- src/lwipDriver.cpp | 9 ++++++--- src/lwipDriver.hpp | 3 +-- 7 files changed, 28 insertions(+), 22 deletions(-) diff --git a/include/ZeroTier.h b/include/ZeroTier.h index 60aa17e..464d56c 100644 --- a/include/ZeroTier.h +++ b/include/ZeroTier.h @@ -555,7 +555,7 @@ ZT_SOCKET_API int ZTCALL zts_core_running(); * @usage Call this after zts_start(), zts_startjoin() and/or zts_join() * @return Number of networks joined by this node */ -ZT_SOCKET_API int ZTCALL zts_get_num_joined_networks(); +ZT_SOCKET_API size_t ZTCALL zts_get_num_joined_networks(); /** * @brief Populates a structure with details for a given network @@ -749,7 +749,7 @@ ZT_SOCKET_API int zts_get_peer_count(); * @usage Call this after zts_start() has succeeded * @return */ -ZT_SOCKET_API int zts_get_peers(struct zts_peer_details *pds, int *num); +ZT_SOCKET_API int zts_get_peers(struct zts_peer_details *pds, unsigned int *num); /** * @brief Return details of a given peer. diff --git a/src/Controls.cpp b/src/Controls.cpp index f3befdc..d2c00f5 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -298,8 +298,8 @@ void *_zts_run_callbacks(void *thread_id) while (_run_callbacks || _callbackMsgQueue.size_approx() > 0) { struct zts_callback_msg *msg; - int sz = _callbackMsgQueue.size_approx(); - for (int j = 0; j < sz; j++) { + size_t sz = _callbackMsgQueue.size_approx(); + for (size_t j = 0; j < sz; j++) { if (_callbackMsgQueue.try_dequeue(msg)) { _process_callback_event(msg); delete msg; @@ -695,7 +695,6 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( int zts_free() { Mutex::Lock _l(_service_lock); - int retval = 0; if (_freeHasBeenCalled) { return ZTS_ERR_INVALID_OP; } @@ -735,6 +734,7 @@ int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, cons InetAddress _6planeAddr = InetAddress::makeIpv66plane(nwid,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _6planeAddr.rawIpData(), sizeof(struct in6_addr)); + return ZTS_ERR_OK; } int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) @@ -745,6 +745,7 @@ int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, con InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(nwid,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr)); + return ZTS_ERR_OK; } uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange) @@ -774,7 +775,7 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1count( } #endif -int zts_get_peers(struct zts_peer_details *pds, int *num) +int zts_get_peers(struct zts_peer_details *pds, unsigned int *num) { Mutex::Lock _l(_service_lock); if (!pds || !num) { @@ -792,7 +793,7 @@ int zts_get_peers(struct zts_peer_details *pds, int *num) *num = pl->peerCount; for(unsigned long i=0;ipeerCount;++i) { memcpy(&(pds[i]), &(pl->peers[i]), sizeof(struct zts_peer_details)); - for (int j=0; jpeers[i].pathCount; j++) { + for (unsigned int j=0; jpeers[i].pathCount; j++) { memcpy(&(pds[i].paths[j].address), &(pl->peers[i].paths[j].address), sizeof(struct sockaddr_storage)); } @@ -819,7 +820,7 @@ int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) for(unsigned long i=0;ipeerCount;++i) { if (pl->peers[i].address == peerId) { memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - for (int j=0; jpeers[i].pathCount; j++) { + for (unsigned int j=0; jpeers[i].pathCount; j++) { memcpy(&(pd->paths[j].address), &(pl->peers[i].paths[j].address), sizeof(struct sockaddr_storage)); } @@ -838,10 +839,9 @@ int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) // Networks // ////////////////////////////////////////////////////////////////////////////// -int zts_get_num_joined_networks() +size_t zts_get_num_joined_networks() { Mutex::Lock _l(_service_lock); - int retval = ZTS_ERR_OK; if (!_zts_can_perform_service_operation()) { return ZTS_ERR_SERVICE; } diff --git a/src/Service.cpp b/src/Service.cpp index b3bc011..e9e587a 100644 --- a/src/Service.cpp +++ b/src/Service.cpp @@ -439,11 +439,9 @@ public: _lastRestart = clockShouldBe; int64_t lastTapMulticastGroupCheck = 0; int64_t lastBindRefresh = 0; - int64_t lastUpdateCheck = clockShouldBe; int64_t lastMultipathModeUpdate = 0; int64_t lastCleanedPeersDb = 0; int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle - int64_t lastLocalConfFileCheck = OSUtils::now(); for(;;) { _run_m.lock(); if (!_run) { @@ -891,12 +889,12 @@ public: } // Previously known peer, update status else { - if (peerCache[pl->peers[i].address] == 0 && pl->peers[i].pathCount > 0) { + if ((peerCache[pl->peers[i].address] == false) && pl->peers[i].pathCount > 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); postEvent(ZTS_EVENT_PEER_P2P, (void*)pd); } - if (peerCache[pl->peers[i].address] > 0 && pl->peers[i].pathCount == 0) { + if ((peerCache[pl->peers[i].address] == true) && pl->peers[i].pathCount == 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); postEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); @@ -909,7 +907,7 @@ public: _node->freeQueryResult((void *)pl); } - inline int networkCount() + inline size_t networkCount() { Mutex::Lock _l(_nets_m); return _nets.size(); @@ -974,6 +972,9 @@ public: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]); secure = true; } + else { + return; + } break; case ZT_STATE_OBJECT_PEER: if (_peer_caching_enabled) { @@ -1034,6 +1035,9 @@ public: if (_network_caching_enabled) { OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]); } + else { + return -1; + } break; case ZT_STATE_OBJECT_PEER: if (_peer_caching_enabled) { diff --git a/src/Service.hpp b/src/Service.hpp index cc44b61..b111d5f 100644 --- a/src/Service.hpp +++ b/src/Service.hpp @@ -152,7 +152,7 @@ public: */ virtual void getRoutes(uint64_t nwid, void *routeArray, unsigned int *numRoutes) = 0; - virtual int networkCount() = 0; + virtual size_t networkCount() = 0; virtual void leaveAll() = 0; virtual void join(uint64_t nwid) = 0; virtual void leave(uint64_t nwid) = 0; diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index b4272d9..88b6861 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -156,7 +156,7 @@ bool VirtualTap::addIp(const InetAddress &ip) return false; } if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { - lwip_init_interface((void*)this, this->_mac, ip); + lwip_init_interface((void*)this, ip); // TODO: Add ZTS_EVENT_ADDR_NEW ? _ips.push_back(ip); // Send callback message diff --git a/src/lwipDriver.cpp b/src/lwipDriver.cpp index 454b602..b8fa9e2 100644 --- a/src/lwipDriver.cpp +++ b/src/lwipDriver.cpp @@ -36,7 +36,10 @@ #include "lwip/ip_addr.h" #include "lwip/nd6.h" #include "lwip/netifapi.h" + +#ifdef LWIP_STATS #include "lwip/stats.h" +#endif #include "VirtualTap.hpp" #include "lwipDriver.hpp" @@ -143,7 +146,7 @@ void lwip_driver_init() #if defined(_WIN32) sys_init(); // Required for win32 init of critical sections #endif - void *st = (void*)sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, main_lwip_driver_loop, + sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, main_lwip_driver_loop, NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); } @@ -255,7 +258,7 @@ void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int e */ } - p = pbuf_alloc(PBUF_RAW, len+sizeof(struct eth_hdr), PBUF_RAM); + p = pbuf_alloc(PBUF_RAW, (uint16_t)len+sizeof(struct eth_hdr), PBUF_RAM); if (!p) { DEBUG_ERROR("dropped packet: unable to allocate memory for pbuf"); return; @@ -461,7 +464,7 @@ static err_t netif_init6(struct netif *n) return ERR_OK; } -void lwip_init_interface(void *tapref, const MAC &mac, const InetAddress &ip) +void lwip_init_interface(void *tapref, const InetAddress &ip) { char ipbuf[INET6_ADDRSTRLEN]; char macbuf[ZTS_MAC_ADDRSTRLEN]; diff --git a/src/lwipDriver.hpp b/src/lwipDriver.hpp index 7467c7e..c2bcd9a 100644 --- a/src/lwipDriver.hpp +++ b/src/lwipDriver.hpp @@ -138,11 +138,10 @@ static void netif_link_callback(struct netif *netif); * * @param * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data - * @param mac Virtual hardware address for this ZeroTier VirtualTap interface * @param ip Virtual IP address for this ZeroTier VirtualTap interface * @return */ -void lwip_init_interface(void *tapref, const MAC &mac, const InetAddress &ip); +void lwip_init_interface(void *tapref, const InetAddress &ip); /** * @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier virtual wire here. From a207ab1f6a4e9b0ba882d437b386319fb1c97dcd Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 20 Apr 2020 23:51:43 -0700 Subject: [PATCH 49/78] Update examples for Windows compatibility (plus some extra tidbits) --- examples/cpp/adhoc.cpp | 35 ++++++++--- examples/cpp/client.cpp | 42 ++++++++----- examples/cpp/comprehensive.cpp | 47 ++++++++++----- examples/cpp/ip6.cpp | 106 +++++++++++++++++++++++++++++++++ examples/cpp/server.cpp | 38 +++++++----- 5 files changed, 215 insertions(+), 53 deletions(-) create mode 100644 examples/cpp/ip6.cpp diff --git a/examples/cpp/adhoc.cpp b/examples/cpp/adhoc.cpp index 989f498..ac4e621 100644 --- a/examples/cpp/adhoc.cpp +++ b/examples/cpp/adhoc.cpp @@ -97,12 +97,31 @@ * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ -#include #include #include -#include -#include #include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +void delay_ms(long ms) +{ +#if defined(_WIN32) + Sleep(ms); +#else + usleep(ms*1000); +#endif +} #include "ZeroTier.h" @@ -209,12 +228,12 @@ int main(int argc, char **argv) { if (argc != 5) { printf("\nlibzt example\n"); - printf("server \n"); + printf("adhoc \n"); exit(0); } int adhocStartPort = atoi(argv[2]); // Start of port range your application will use int adhocEndPort = atoi(argv[3]); // End of port range your application will use - int ztServicePort = atoi(argv[4]); // Port the library uses to send encapsulated and encrypted UDP packets to peers + int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); int err = ZTS_ERR_OK; @@ -226,7 +245,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { usleep(50000); } + while (!nodeReady) { delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(adhoc_nwid)) != ZTS_ERR_OK) { @@ -234,12 +253,12 @@ int main(int argc, char **argv) exit(1); } printf("Joining network %llx\n", adhoc_nwid); - while (!networkReady) { usleep(50000); } + while (!networkReady) { delay_ms(50); } // Idle and just show callback events, stack statistics, etc printf("Node will now idle...\n"); - while (true) { sleep(1); } + while (true) { delay_ms(1000); } // Shut down service and stack threads diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index f23dffe..c9ae9fe 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -2,10 +2,8 @@ * libzt API example */ -#include #include #include -#include #include #include @@ -20,6 +18,15 @@ #include #endif +void delay_ms(long ms) +{ +#if defined(_WIN32) + Sleep(ms); +#else + usleep(ms*1000); +#endif +} + #include "ZeroTier.h" bool nodeReady = false; @@ -202,31 +209,35 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) int main(int argc, char **argv) { - if (argc != 5) { + if (argc != 6) { printf("\nlibzt example client\n"); - printf("client \n"); + printf("client \n"); exit(0); } - uint64_t nwid = strtoull(argv[2],NULL,16); - std::string remoteAddr = argv[3]; - int remotePort = atoi(argv[4]); - int defaultServicePort = 9998; + uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join + std::string remoteAddr = argv[3]; // Remote application's virtual ZT address + int remotePort = atoi(argv[4]); // Port the application will try to connect to the server on + int ztServicePort = atoi(argv[5]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) struct zts_sockaddr_in in4; in4.sin_port = htons(remotePort); - in4.sin_addr.s_addr = inet_addr(remoteAddr.c_str()); +#if defined(_WIN32) + in4.sin_addr.S_addr = inet_addr(remoteAddr.c_str());; +#else + in4.sin_addr.s_addr = inet_addr(remoteAddr.c_str());; +#endif in4.sin_family = ZTS_AF_INET; // Bring up ZeroTier service and join network int err = ZTS_ERR_OK; - if((err = zts_start(argv[1], &myZeroTierEventCallback, defaultServicePort)) != ZTS_ERR_OK) { + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { usleep(50000); } + while (!nodeReady) { delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -235,7 +246,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { usleep(50000); } + while (!networkReady) { delay_ms(50); } // Socket-like API example @@ -249,19 +260,18 @@ int main(int argc, char **argv) exit(1); } // Retries are often required since ZT uses transport-triggered links (explained above) - int delay = 1; // second for (;;) { printf("Connecting to remote host...\n"); if ((err = zts_connect(fd, (const struct sockaddr *)&in4, sizeof(in4))) < 0) { - printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again in %ds\n", - fd, err, zts_errno, delay); + printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", + fd, err, zts_errno); zts_close(fd); printf("Creating socket...\n"); if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); exit(1); } - sleep(delay); + delay_ms(250); } else { printf("Connected.\n"); diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index 6e61bfc..b7c6318 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -97,12 +97,31 @@ * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ -#include #include #include -#include -#include #include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +void delay_ms(long ms) +{ +#if defined(_WIN32) + Sleep(ms); +#else + usleep(ms*1000); +#endif +} #include "ZeroTier.h" @@ -218,7 +237,7 @@ void printPeerDetails(struct zts_peer_details *pd) pd->versionRev, pd->pathCount); // Print all known paths for each peer - for (int j=0; jpathCount; j++) { + for (unsigned int j=0; jpathCount; j++) { char ipstr[INET6_ADDRSTRLEN]; int port = 0; struct sockaddr *sa = (struct sockaddr *)&(pd->paths[j].address); @@ -269,7 +288,7 @@ void getAllPeerDetails() expensive for large numbers of peers. Consider using get_peer(struct zts_peer_details *pds, uint64_t peerId) instead */ - int num = 128; + unsigned int num = 128; int err; if ((err = zts_get_peers(pd, &num)) < 0) { printf("error (%d)\n", err); @@ -277,7 +296,7 @@ void getAllPeerDetails() } if (num) { printf("num=%d\n", num); - for (int i=0; i \n"); + printf("comprehensive \n"); exit(0); } - uint64_t nwid = strtoull(argv[2],NULL,16); - int ztServicePort = atoi(argv[3]); + uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join + int ztServicePort = atoi(argv[3]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) // Bring up ZeroTier service and join network @@ -329,8 +348,8 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { usleep(50000); } - printf("This node ID is %lx\n", zts_get_node_id()); + while (!nodeReady) { delay_ms(50); } + printf("This node ID is %llx\n", zts_get_node_id()); printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -339,7 +358,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { usleep(50000); } + while (!networkReady) { delay_ms(50); } // Get multiple peer's details getAllPeerDetails(); @@ -360,7 +379,7 @@ int main(int argc, char **argv) // Idle and just show callback events, stack statistics, etc while (true) { - usleep(50000); + delay_ms(1000); status = zts_get_node_status(); printf("zts_get_node_status()=%d\n", status); display_stack_stats(); diff --git a/examples/cpp/ip6.cpp b/examples/cpp/ip6.cpp new file mode 100644 index 0000000..59e6404 --- /dev/null +++ b/examples/cpp/ip6.cpp @@ -0,0 +1,106 @@ +/* + * ZeroTier SDK - Network Virtualization Everywhere + * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "libzt.h" + +int main(int argc, char **argv) +{ + if (argc != 4) { + printf("\nlibzt example server\n"); + printf("server [config_file_path] [nwid] [bind_port]\n"); + exit(0); + } + std::string path = argv[1]; + std::string nwidstr = argv[2]; + int bind_port = atoi(argv[3]); + int w=0, r=0, err=0, sockfd, accfd; + char rbuf[32]; + memset(rbuf, 0, sizeof rbuf); + + struct sockaddr_in6 in6, acc_in6; + in6.sin6_port = htons(bind_port); + in6.sin6_family = AF_INET6; + in6.sin6_addr = in6addr_any; + + // --- BEGIN EXAMPLE CODE + + printf("Waiting for libzt to come online...\n"); + uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); + printf("nwid=%llx\n", (unsigned long long)nwid); + zts_startjoin(path.c_str(), nwid); + uint64_t nodeId = zts_get_node_id(); + printf("I am %llx\n", (unsigned long long)nodeId); + + if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { + printf("error creating ZeroTier socket\n"); + } + + if ((err = zts_bind(sockfd, (struct sockaddr *)&in6, sizeof(struct sockaddr_in6)) < 0)) { + printf("error binding to interface (%d)\n", err); + } + + if ((err = zts_listen(sockfd, 100)) < 0) { + printf("error placing socket in LISTENING state (%d)\n", err); + } + + socklen_t client_addrlen = sizeof(sockaddr_in6); + if ((accfd = zts_accept(sockfd, (struct sockaddr *)&acc_in6, &client_addrlen)) < 0) { + printf("error accepting connection (%d)\n", err); + } + + socklen_t peer_addrlen = sizeof(struct sockaddr_storage); + zts_getpeername(accfd, (struct sockaddr*)&acc_in6, &peer_addrlen); + //DEBUG_INFO("accepted connection from %s : %d", inet_ntoa(acc_in6.sin6_addr), ntohs(acc_in6.sin6_port)); + + printf("reading from client...\n"); + r = zts_read(accfd, rbuf, sizeof rbuf); + + printf("sending to client...\n"); + w = zts_write(accfd, rbuf, strlen(rbuf)); + + printf("Received : %s\n", rbuf); + + err = zts_close(sockfd); + err = zts_close(accfd); + + return err; +} diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp index 98f4d9f..41f6db9 100644 --- a/examples/cpp/server.cpp +++ b/examples/cpp/server.cpp @@ -2,10 +2,8 @@ * libzt API example */ -#include #include #include -#include #include #include @@ -20,6 +18,15 @@ #include #endif +void delay_ms(long ms) +{ +#if defined(_WIN32) + Sleep(ms); +#else + usleep(ms*1000); +#endif +} + #include "ZeroTier.h" bool nodeReady = false; @@ -204,32 +211,33 @@ int main(int argc, char **argv) { if (argc != 5) { printf("\nlibzt example server\n"); - printf("server <4|6> \n"); + printf("server \n"); exit(0); } - uint64_t nwid = strtoull(argv[2],NULL,16); - int ipVersion = atoi(argv[3]); - int bindPort = atoi(argv[4]); - int defaultServicePort = 9998; + uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join + int serverBindPort = atoi(argv[3]); // Port the application should bind to + int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) struct zts_sockaddr_in in4, acc_in4; - if (ipVersion == 4) { - in4.sin_port = htons(bindPort); - in4.sin_addr.s_addr = INADDR_ANY; - in4.sin_family = ZTS_AF_INET; - } + in4.sin_port = htons(serverBindPort); +#if defined(_WIN32) + in4.sin_addr.S_addr = INADDR_ANY; +#else + in4.sin_addr.s_addr = INADDR_ANY; +#endif + in4.sin_family = ZTS_AF_INET; // Bring up ZeroTier service and join network int fd, accfd; int err = ZTS_ERR_OK; - if((err = zts_start(argv[1], &myZeroTierEventCallback, defaultServicePort)) != ZTS_ERR_OK) { + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { usleep(50000); } + while (!nodeReady) { delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -238,7 +246,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { usleep(50000); } + while (!networkReady) { delay_ms(50); } // Socket-like API example From 2c709277b9632bd8e3af8b66f51d3f5a53f84e8e Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Mon, 20 Apr 2020 23:54:48 -0700 Subject: [PATCH 50/78] Remove erroneously added old example --- examples/cpp/ip6.cpp | 106 ------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 examples/cpp/ip6.cpp diff --git a/examples/cpp/ip6.cpp b/examples/cpp/ip6.cpp deleted file mode 100644 index 59e6404..0000000 --- a/examples/cpp/ip6.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * ZeroTier SDK - Network Virtualization Everywhere - * Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/ - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -#include "libzt.h" - -int main(int argc, char **argv) -{ - if (argc != 4) { - printf("\nlibzt example server\n"); - printf("server [config_file_path] [nwid] [bind_port]\n"); - exit(0); - } - std::string path = argv[1]; - std::string nwidstr = argv[2]; - int bind_port = atoi(argv[3]); - int w=0, r=0, err=0, sockfd, accfd; - char rbuf[32]; - memset(rbuf, 0, sizeof rbuf); - - struct sockaddr_in6 in6, acc_in6; - in6.sin6_port = htons(bind_port); - in6.sin6_family = AF_INET6; - in6.sin6_addr = in6addr_any; - - // --- BEGIN EXAMPLE CODE - - printf("Waiting for libzt to come online...\n"); - uint64_t nwid = strtoull(nwidstr.c_str(),NULL,16); - printf("nwid=%llx\n", (unsigned long long)nwid); - zts_startjoin(path.c_str(), nwid); - uint64_t nodeId = zts_get_node_id(); - printf("I am %llx\n", (unsigned long long)nodeId); - - if ((sockfd = zts_socket(AF_INET6, SOCK_STREAM, 0)) < 0) { - printf("error creating ZeroTier socket\n"); - } - - if ((err = zts_bind(sockfd, (struct sockaddr *)&in6, sizeof(struct sockaddr_in6)) < 0)) { - printf("error binding to interface (%d)\n", err); - } - - if ((err = zts_listen(sockfd, 100)) < 0) { - printf("error placing socket in LISTENING state (%d)\n", err); - } - - socklen_t client_addrlen = sizeof(sockaddr_in6); - if ((accfd = zts_accept(sockfd, (struct sockaddr *)&acc_in6, &client_addrlen)) < 0) { - printf("error accepting connection (%d)\n", err); - } - - socklen_t peer_addrlen = sizeof(struct sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in6, &peer_addrlen); - //DEBUG_INFO("accepted connection from %s : %d", inet_ntoa(acc_in6.sin6_addr), ntohs(acc_in6.sin6_port)); - - printf("reading from client...\n"); - r = zts_read(accfd, rbuf, sizeof rbuf); - - printf("sending to client...\n"); - w = zts_write(accfd, rbuf, strlen(rbuf)); - - printf("Received : %s\n", rbuf); - - err = zts_close(sockfd); - err = zts_close(accfd); - - return err; -} From a0b50530d37d9c13d30a68bf1d4686485be36327 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 1 May 2020 19:15:38 -0700 Subject: [PATCH 51/78] Add portability and consistency fixes for C API, remove cruft, slight internal restructuring --- CMakeLists.txt | 64 +- README.md | 51 +- README.pdf | Bin 262885 -> 261397 bytes dist.sh | 5 - examples/cpp/adhoc.cpp | 95 +- examples/cpp/client.cpp | 109 +- examples/cpp/comprehensive.cpp | 128 +- examples/cpp/earthtest.cpp | 188 +++ examples/cpp/server.cpp | 121 +- examples/objective-c/adhoc.m | 247 ++++ examples/swift/main.swift | 143 +++ ext/lwip | 2 +- include/README.md | 4 + include/Xcode-Bridging-Header.h | 69 -- include/ZeroTier.h | 1277 ------------------- include/ZeroTierConstants.h | 251 ---- include/ZeroTierSockets.h | 1688 ++++++++++++++++++++++++++ src/Controls.cpp | 1080 +++++----------- src/Controls.hpp | 107 -- src/Events.cpp | 245 ++++ src/Events.hpp | 106 ++ src/{Service.cpp => NodeService.cpp} | 324 ++--- src/{Service.hpp => NodeService.hpp} | 67 +- src/Options.h | 80 -- src/Sockets.cpp | 574 ++++++--- src/VirtualTap.cpp | 541 ++++++++- src/VirtualTap.hpp | 195 ++- src/lwipDriver.cpp | 535 -------- src/lwipDriver.hpp | 173 --- 29 files changed, 4359 insertions(+), 4110 deletions(-) create mode 100644 examples/cpp/earthtest.cpp create mode 100644 examples/objective-c/adhoc.m create mode 100644 examples/swift/main.swift create mode 100644 include/README.md delete mode 100644 include/Xcode-Bridging-Header.h delete mode 100644 include/ZeroTier.h delete mode 100644 include/ZeroTierConstants.h create mode 100644 include/ZeroTierSockets.h delete mode 100644 src/Controls.hpp create mode 100644 src/Events.cpp create mode 100644 src/Events.hpp rename src/{Service.cpp => NodeService.cpp} (85%) rename src/{Service.hpp => NodeService.hpp} (75%) delete mode 100644 src/Options.h delete mode 100644 src/lwipDriver.cpp delete mode 100644 src/lwipDriver.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 27e50f7..14062b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,10 +29,6 @@ endif () if (${CMAKE_GENERATOR} STREQUAL "Xcode") set (IN_XCODE TRUE) set(XCODE_EMIT_EFFECTIVE_PLATFORM_NAME ON) - #set_target_properties (${STATIC_LIB_NAME} - # PROPERTIES XCODE_ATTRIBUTE_MY_BUILD_ONLY_ACTIVE_ARCH YES) - #set_target_properties (${STATIC_LIB_NAME} - # PROPERTIES XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "${MY_BUILD_ONLY_ACTIVE_ARCH}) endif () if (BUILDING_WIN32 OR BUILDING_WIN64 OR MSVC) set (BUILDING_WIN TRUE) @@ -98,8 +94,6 @@ set (SILENCE "${SILENCE} -Wno-sign-compare") set (SILENCE "${SILENCE} -Wno-unused-variable") set (SILENCE "${SILENCE} -Wno-missing-field-initializers") set (SILENCE "${SILENCE} -Wno-unused-parameter") -#set (SILENCE "${SILENCE} -Wno-nullability-completeness") -#set (SILENCE "${SILENCE} -Wno-expansion-to-defined") set (ZT_FLAGS "${ZT_FLAGS} -DZT_USE_MINIUPNPC=1") set (ZT_FLAGS "${ZT_FLAGS} -DZT_SOFTWARE_UPDATE_DEFAULT=0") @@ -109,8 +103,8 @@ set (ZT_FLAGS "${ZT_FLAGS} -DZT_SDK=1") if (DEBUG_BUILD) set (LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_DBG_TYPES_ON=128") set (LWIP_FLAGS "${LWIP_FLAGS} -DSOCKETS_DEBUG=128") - set (LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_STATS_LARGE=1") - set (LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_STATS=1") + #set (LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_STATS_LARGE=1") + #set (LWIP_FLAGS "${LWIP_FLAGS} -DLWIP_STATS=1") set (LWIP_FLAGS "${LWIP_FLAGS} -DLINK_STATS=1") set (LWIP_FLAGS "${LWIP_FLAGS} -DETHARP_STATS=1") set (LWIP_FLAGS "${LWIP_FLAGS} -DIPFRAG_STATS=1") @@ -250,31 +244,6 @@ set (LWIP_SRC_DIR "${PROJ_DIR}/ext/lwip/src") set (ZTO_SRC_DIR "${PROJ_DIR}/ext/ZeroTierOne") set (LIBZT_SRC_DIR "${PROJ_DIR}/src") -if (SWIG_CSHARP) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/csharp/*.cxx) -endif () -if (SWIG_PYTHON) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/python/*.cxx) -endif () -if (SWIG_LUA) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/lua/*.cxx) -endif () -if (SWIG_GO32) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/go32/*.cxx) -endif () -if (SWIG_GO64) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/go64/*.cxx) -endif () -if (SWIG_JS_JSC) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/jsc/*.cxx) -endif () -if (SWIG_JS_V8) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/v8/*.cxx) -endif () -if (SWIG_JS_NODE) -set (libztSwigWrapperSrc ${LIBZT_SRC_DIR}/js/node/*.cxx) -endif () - file (GLOB ztcoreSrcGlob ${ZTO_SRC_DIR}/node/*.cpp ${ZTO_SRC_DIR}/osdep/OSUtils.cpp @@ -301,7 +270,7 @@ file (GLOB libminiupnpcSrcGlob ${ZTO_SRC_DIR}/ext/miniupnpc/upnperrors.c ${ZTO_SRC_DIR}/ext/miniupnpc/upnpreplyparse.c) -file (GLOB libztSrcGlob ${LIBZT_SRC_DIR}/*.cpp ${libztSwigWrapperSrc}) +file (GLOB libztSrcGlob ${LIBZT_SRC_DIR}/*.cpp) if (UNIX) set (LWIP_PORT_DIR ${PROJ_DIR}/ext/lwip-contrib/ports/unix/port) @@ -321,8 +290,7 @@ file (GLOB lwipSrcGlob list(REMOVE_ITEM lwipSrcGlob ${LWIP_SRC_DIR}/netif/slipif.c) # header globs for xcode frameworks -file (GLOB frameworkPrivateHeaderGlob include/ZeroTier.h include/ZeroTierConstants.h) -file (GLOB frameworkPublicHeaderGlob include/Xcode-Bridging-Header.h) +file (GLOB frameworkPublicHeaderGlob include/ZeroTierSockets.h) file (GLOB frameworkHeaderGlob ${frameworkPublicHeaderGlob} ${frameworkPrivateHeaderGlob}) # ----------------------------------------------------------------------------- @@ -457,24 +425,6 @@ set_target_properties (ztcore PROPERTIES OUTPUT_NAME ztcore LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) -# libnatpmp.a -#add_library (natpmp STATIC $) -#set_target_properties (natpmp PROPERTIES -# OUTPUT_NAME natpmp -# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) - -# libminiupnpc.a -#add_library (miniupnpc STATIC $) -#set_target_properties (miniupnpc PROPERTIES -# OUTPUT_NAME miniupnpc -# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) - -# liblwip.a -#add_library (lwip STATIC $) -#set_target_properties (lwip PROPERTIES -# OUTPUT_NAME lwip -# LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) - # libzt.a add_library (${STATIC_LIB_NAME} STATIC $ $ @@ -540,6 +490,8 @@ endif () # ----------------------------------------------------------------------------- if (SHOULD_BUILD_TESTS) + add_executable (earthtest ${PROJ_DIR}/examples/cpp/earthtest.cpp) + target_link_libraries(earthtest ${STATIC_LIB_NAME}) add_executable (adhoc ${PROJ_DIR}/examples/cpp/adhoc.cpp) target_link_libraries(adhoc ${STATIC_LIB_NAME}) add_executable (comprehensive ${PROJ_DIR}/examples/cpp/comprehensive.cpp) @@ -554,9 +506,7 @@ endif () # | INSTALL | # ----------------------------------------------------------------------------- -set (PUBLIC_ZT_HEADERS - ${PROJECT_SOURCE_DIR}/include/ZeroTier.h - ${PROJECT_SOURCE_DIR}/include/ZeroTierConstants.h) +set (PUBLIC_ZT_HEADERS ${PROJECT_SOURCE_DIR}/include/ZeroTierSockets.h) set_target_properties(${STATIC_LIB_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_ZT_HEADERS}") install (TARGETS ${STATIC_LIB_NAME} diff --git a/README.md b/README.md index 12f3126..db8b845 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +--- +title: README +created: '2020-04-17T17:19:44.552Z' +modified: '2020-05-02T00:31:45.563Z' +--- + # ZeroTier SDK Connect physical devices, virtual devices, and application instances as if everything is on a single LAN. *** @@ -12,15 +18,6 @@ The ZeroTier SDK brings your network into userspace. We've paired our network hy ### Downloads and examples: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) -### Homebrew - -``` -brew install libzt -clang++ -o yourApp yourApp.cpp -lzt; ./yourApp -``` - -### - ### Building from source To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `./lib`, test and example programs will be placed in `./bin`: @@ -67,7 +64,7 @@ The next few sections explain how to use the network control interface portion o 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: ``` -#include "ZeroTier.h" +#include "ZeroTierSockets.h" ... @@ -102,8 +99,8 @@ For more complete examples see `./examples/` After calling `zts_start()` you will receive one or more of the following events: ``` -ZTS_EVENT_NODE_OFFLINE ZTS_EVENT_NODE_ONLINE +ZTS_EVENT_NODE_OFFLINE ZTS_EVENT_NODE_DOWN ZTS_EVENT_NODE_IDENTITY_COLLISION ZTS_EVENT_NODE_UNRECOVERABLE_ERROR @@ -127,7 +124,7 @@ Joining a ZeroTier virtual network is as easy as calling `zts_join(uint64_t netw ``` ZTS_EVENT_NETWORK_NOT_FOUND ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQUESTING_CONFIG +ZTS_EVENT_NETWORK_REQ_CONFIG ZTS_EVENT_NETWORK_OK ZTS_EVENT_NETWORK_ACCESS_DENIED ZTS_EVENT_NETWORK_READY_IP4 @@ -153,7 +150,7 @@ One can use `zts_get_peer_status(uint64_t peerId)` to query the current reachabi # Event handling -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/ZeroTier.h`: +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`: ``` struct zts_callback_msg @@ -194,9 +191,9 @@ ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 ZTS_EVENT_NODE_ONLINE, node=c4c7ba3cf ZTS_EVENT_NETWORK_READY_IP4 --- network=a09acf023be465c1 ZTS_EVENT_NETWORK_READY_IP6 --- network=a09acf023be465c1 -ZTS_EVENT_PEER_P2P --- node=74d0f5e89d -ZTS_EVENT_PEER_P2P --- node=9d219039f3 -ZTS_EVENT_PEER_P2P --- node=a09acf0233 +ZTS_EVENT_PEER_DIRECT --- node=74d0f5e89d +ZTS_EVENT_PEER_DIRECT --- node=9d219039f3 +ZTS_EVENT_PEER_DIRECT --- node=a09acf0233 ``` ## Node Events @@ -219,7 +216,7 @@ These events pertain to the state of the indicated network. This event type will ``` ZTS_EVENT_NETWORK_NOT_FOUND ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQUESTING_CONFIG +ZTS_EVENT_NETWORK_REQ_CONFIG ZTS_EVENT_NETWORK_OK ZTS_EVENT_NETWORK_ACCESS_DENIED ZTS_EVENT_NETWORK_READY_IP4 @@ -234,7 +231,7 @@ ZTS_EVENT_NETWORK_DOWN These events are triggered when the reachability status of a peer has changed, this can happen at any time. This event type will arrive with a `zts_peer_details` object for additional context. Additionally, one can query the status of the network with `zts_get_peer_status(uint64_t peerId)`, this will **return** the status as an integer value only. ``` -ZTS_EVENT_PEER_P2P +ZTS_EVENT_PEER_DIRECT ZTS_EVENT_PEER_RELAY ZTS_EVENT_PEER_UNREACHABLE ``` @@ -273,13 +270,12 @@ ZTS_EVENT_ADDR_REMOVED_IP6 # Error handling -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 `./ext/lwip/src/include/lwip/errno.h`. +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`. - `ZTS_ERR_OK`: No error - - `ZTS_ERR`: Error (see `zts_errno`for more information) - - `ZTS_ERR_INVALID_ARG`: An argument provided is invalid. - - `ZTS_ERR_SERVICE`: ZT is not yet initialized. Try again. - - `ZTS_ERR_INVALID_OP`: Operation is not permitted (Doesn't make sense in this state). + - `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. @@ -326,8 +322,9 @@ 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!): ``` -inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) -inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) +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: @@ -361,7 +358,7 @@ If the information in those sections hasn't helped, there are a couple of ways t 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/ZeroTier.h` that is guarded by `LWIP_STATS`. The protocol constants are defined in `include/ZeroTierConstants.h`. An example usage is as follows: +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`. ``` struct zts_stats_proto stats; @@ -402,7 +399,7 @@ ZTS_EVENT_NETIF_LINK_DOWN # Network controller mode -The library form of ZeroTier can act as a network controller and in `libzt` this is controlled via the `zts_controller_*` API calls specified in `include/ZeroTier.h`. Currently controller mode is not available in the `iOS` and `macOS` framework builds. +The library form of ZeroTier can act as a network controller and in `libzt` this is controlled via the `zts_controller_*` API calls specified in `include/ZeroTierSockets.h`. Currently controller mode is not available in the `iOS` and `macOS` framework builds. # Multipath diff --git a/README.pdf b/README.pdf index 3548a3b829456816309b86a0e95b190e139fd491..d736e25bf9b328241b73ee7546845ae5163f8a6f 100644 GIT binary patch delta 55036 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 QH9p`t6h0LdqwSmi2OXi`-T(jq delta 55605 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 S{RJysin6_addr), ...); (CORRECT) - * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) - * - * 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: - * - * struct zts_sockaddr_in6 in6; - * ... - * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ #include -#include -#include -#include #include -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -void delay_ms(long ms) -{ -#if defined(_WIN32) - Sleep(ms); -#else - usleep(ms*1000); -#endif -} - -#include "ZeroTier.h" +#include "ZeroTierSockets.h" bool nodeReady = false; bool networkReady = false; // Example callbacks -void myZeroTierEventCallback(struct zts_callback_msg *msg) +void myZeroTierEventCallback(void *msgPtr) { + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); @@ -146,8 +114,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", msg->network->nwid); } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); } if (msg->eventCode == 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", @@ -174,23 +142,22 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", msg->netif->nwid, msg->netif->mac); - networkReady = true; } // Address events if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + 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->nwid, ipstr); } // Peer events - // If you don't recognize the peer ID, don't panic, this is most likely one of our root servers - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - printf("ZTS_EVENT_PEER_P2P --- There is now a direct path to peer %llx\n", + // Don't worry if you don't recognize a peer ID, it's most likely our infrastructure + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- There is now a direct path to peer %llx\n", msg->peer->address); } if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { @@ -238,14 +205,14 @@ int main(int argc, char **argv) uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); int err = ZTS_ERR_OK; - zts_set_network_caching(false); + zts_allow_network_caching(false); if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { delay_ms(50); } + while (!nodeReady) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(adhoc_nwid)) != ZTS_ERR_OK) { @@ -253,12 +220,12 @@ int main(int argc, char **argv) exit(1); } printf("Joining network %llx\n", adhoc_nwid); - while (!networkReady) { delay_ms(50); } + while (!networkReady) { zts_delay_ms(50); } // Idle and just show callback events, stack statistics, etc printf("Node will now idle...\n"); - while (true) { delay_ms(1000); } + while (true) { zts_delay_ms(1000); } // Shut down service and stack threads diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index c9ae9fe..405d539 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -3,38 +3,20 @@ */ #include +#include #include #include -#include -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -void delay_ms(long ms) -{ -#if defined(_WIN32) - Sleep(ms); -#else - usleep(ms*1000); -#endif -} - -#include "ZeroTier.h" +#include "ZeroTierSockets.h" bool nodeReady = false; bool networkReady = false; // Example callbacks -void myZeroTierEventCallback(struct zts_callback_msg *msg) +void myZeroTierEventCallback(void *msgPtr) { + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); @@ -53,8 +35,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", msg->network->nwid); } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); } if (msg->eventCode == 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", @@ -76,36 +58,36 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) // Address events if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); // A direct path is known for nodeId } if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { @@ -127,8 +109,9 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api * - * - An exception to the above rule is if you are using an Ad-hoc network, it has no - * controller and therefore requires no authorization. + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) * * * ESTABLISHING A CONNECTION: @@ -160,28 +143,27 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * [ 0] ZTS_ERR_OK - No error. - * [-1] ZTS_ERR - Error (see zts_errno for more information). - * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. - * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. - * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). - * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. - * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With * the added possibility of zts_errno being set. Much like standard * errno this will provide a more specific reason for an error's occurrence. - * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h - * and closely map to standard Linux error values. + * See ZeroTierSockets.h for values. * * * API COMPATIBILITY WITH HOST OS: * - * - Since libzt re-implements a socket API probably very similar to your host OS's own - * API 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, so here are a few - * guidelines: + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: * @@ -192,19 +174,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * * struct zts_sockaddr_in in4; <------ Note the zts_* prefix * ... - * zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) * - * If you are calling a host OS function, use your host OS's constants (and structures!): - * - * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) - * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) - * - * 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: - * - * struct zts_sockaddr_in6 in6; - * ... - * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ int main(int argc, char **argv) @@ -220,11 +191,11 @@ int main(int argc, char **argv) int ztServicePort = atoi(argv[5]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) struct zts_sockaddr_in in4; - in4.sin_port = htons(remotePort); + in4.sin_port = zts_htons(remotePort); #if defined(_WIN32) - in4.sin_addr.S_addr = inet_addr(remoteAddr.c_str());; + in4.sin_addr.S_addr = zts_inet_addr(remoteAddr.c_str()); #else - in4.sin_addr.s_addr = inet_addr(remoteAddr.c_str());; + in4.sin_addr.s_addr = zts_inet_addr(remoteAddr.c_str()); #endif in4.sin_family = ZTS_AF_INET; @@ -237,7 +208,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { delay_ms(50); } + while (!nodeReady) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -246,7 +217,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { delay_ms(50); } + while (!networkReady) { zts_delay_ms(50); } // Socket-like API example @@ -262,7 +233,7 @@ int main(int argc, char **argv) // Retries are often required since ZT uses transport-triggered links (explained above) for (;;) { printf("Connecting to remote host...\n"); - if ((err = zts_connect(fd, (const struct sockaddr *)&in4, sizeof(in4))) < 0) { + if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", fd, err, zts_errno); zts_close(fd); @@ -271,7 +242,7 @@ int main(int argc, char **argv) printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); exit(1); } - delay_ms(250); + zts_delay_ms(250); } else { printf("Connected.\n"); diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index b7c6318..2f1f57a 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -17,8 +17,9 @@ * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api * - * - An exception to the above rule is if you are using an Ad-hoc network, it has no - * controller and therefore requires no authorization. + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) * * * ESTABLISHING A CONNECTION: @@ -50,28 +51,27 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * [ 0] ZTS_ERR_OK - No error. - * [-1] ZTS_ERR - Error (see zts_errno for more information). - * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. - * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. - * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). - * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. - * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With * the added possibility of zts_errno being set. Much like standard * errno this will provide a more specific reason for an error's occurrence. - * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h - * and closely map to standard Linux error values. + * See ZeroTierSockets.h for values. * * * API COMPATIBILITY WITH HOST OS: * - * - Since libzt re-implements a socket API probably very similar to your host OS's own - * API 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, so here are a few - * guidelines: + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: * @@ -82,55 +82,23 @@ * * struct zts_sockaddr_in in4; <------ Note the zts_* prefix * ... - * zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) * - * If you are calling a host OS function, use your host OS's constants (and structures!): - * - * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) - * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) - * - * 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: - * - * struct zts_sockaddr_in6 in6; - * ... - * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ #include -#include -#include -#include #include -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -void delay_ms(long ms) -{ -#if defined(_WIN32) - Sleep(ms); -#else - usleep(ms*1000); -#endif -} - -#include "ZeroTier.h" +#include "ZeroTierSockets.h" bool nodeReady = false; bool networkReady = false; // Example callbacks -void myZeroTierEventCallback(struct zts_callback_msg *msg) +void myZeroTierEventCallback(void *msgPtr) { + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); @@ -149,8 +117,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", msg->network->nwid); } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); } if (msg->eventCode == 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", @@ -182,43 +150,42 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", msg->netif->nwid, msg->netif->mac); - networkReady = true; } // Address events if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); // A direct path is known for nodeId } if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { @@ -238,17 +205,17 @@ void printPeerDetails(struct zts_peer_details *pd) pd->pathCount); // Print all known paths for each peer for (unsigned int j=0; jpathCount; j++) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; int port = 0; - struct sockaddr *sa = (struct sockaddr *)&(pd->paths[j].address); - if (sa->sa_family == AF_INET) { + struct zts_sockaddr *sa = (struct zts_sockaddr *)&(pd->paths[j].address); + if (sa->sa_family == ZTS_AF_INET) { // TODO: Probably broken struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa; - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - port = ntohs(in4->sin_port); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + port = zts_ntohs(in4->sin_port); } - if (sa->sa_family == AF_INET6) { + if (sa->sa_family == ZTS_AF_INET6) { struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa; - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); } printf("\tpath[%d]=%s, port=%d\n", j, ipstr, port); } @@ -261,22 +228,17 @@ void getSinglePeerDetails(uint64_t peerId) if (err == ZTS_ERR_OK) { printf("(%d) call succeeded\n", err); - } if (err == ZTS_ERR_INVALID_ARG) { + printPeerDetails(&pd); + } if (err == ZTS_ERR_ARG) { printf("(%d) invalid argument\n", err); return; } if (err == ZTS_ERR_SERVICE) { - printf("(%d) error: service is unavailable\n", err); - return; - } if (err == ZTS_ERR_INVALID_OP) { - printf("(%d) error: invalid API operation\n", err); + printf("(%d) error: invalid API operation or service error\n", err); return; } if (err == ZTS_ERR_NO_RESULT) { printf("(%d) error: object or result not found\n", err); return; } - if (err == 0) { // ZTS_ERR_OK - printPeerDetails(&pd); - } } // Similar to "zerotier-cli listpeers" @@ -348,7 +310,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { delay_ms(50); } + while (!nodeReady) { zts_delay_ms(50); } printf("This node ID is %llx\n", zts_get_node_id()); printf("This node's identity is stored in %s\n", argv[1]); @@ -358,7 +320,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { delay_ms(50); } + while (!networkReady) { zts_delay_ms(50); } // Get multiple peer's details getAllPeerDetails(); @@ -379,7 +341,7 @@ int main(int argc, char **argv) // Idle and just show callback events, stack statistics, etc while (true) { - delay_ms(1000); + zts_delay_ms(1000); status = zts_get_node_status(); printf("zts_get_node_status()=%d\n", status); display_stack_stats(); diff --git a/examples/cpp/earthtest.cpp b/examples/cpp/earthtest.cpp new file mode 100644 index 0000000..0e29549 --- /dev/null +++ b/examples/cpp/earthtest.cpp @@ -0,0 +1,188 @@ +/** + * libzt API example + * + * Pingable node joined to public ZT network "earth" + */ + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * See ZeroTierSockets.h for values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * struct zts_sockaddr_in in4; <------ Note the zts_* prefix + * ... + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * + */ + +#include +#include + +#include "ZeroTierSockets.h" + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(void *msgPtr) +{ + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == 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->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- Join %llx and ping me at %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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->nwid, ipstr); + } + // Don't worry if you don't recognize a peer ID, it's most likely our infrastructure + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- There is now a direct path to peer %llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to peer %llx\n", + msg->peer->address); + } +} + +int main(int argc, char **argv) +{ + if (argc != 3) { + printf("\nlibzt example\n"); + printf("earthtest \n"); + exit(0); + } + int ztServicePort = atoi(argv[2]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) + + int err = ZTS_ERR_OK; + zts_allow_network_caching(false); + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!nodeReady) { zts_delay_ms(50); } + printf("This node's identity is stored in %s\n", argv[1]); + + uint64_t nwid = 0x8056c2e21c000001; + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + while (!networkReady) { zts_delay_ms(50); } + + // Idle and just show callback events, stack statistics, etc + + printf("Node will now idle...\n"); + while (true) { zts_delay_ms(1000); } + + // Shut down service and stack threads + + zts_stop(); + return 0; +} diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp index 41f6db9..ff7c5a7 100644 --- a/examples/cpp/server.cpp +++ b/examples/cpp/server.cpp @@ -3,38 +3,20 @@ */ #include +#include #include #include -#include -#if defined(_WIN32) -#include -#include -#else -#include -#include -#include -#include -#include -#endif - -void delay_ms(long ms) -{ -#if defined(_WIN32) - Sleep(ms); -#else - usleep(ms*1000); -#endif -} - -#include "ZeroTier.h" +#include "ZeroTierSockets.h" bool nodeReady = false; bool networkReady = false; // Example callbacks -void myZeroTierEventCallback(struct zts_callback_msg *msg) +void myZeroTierEventCallback(void *msgPtr) { + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); @@ -53,8 +35,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", msg->network->nwid); } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); } if (msg->eventCode == 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", @@ -76,36 +58,36 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) // Address events if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[INET6_ADDRSTRLEN]; + char ipstr[ZTS_INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); - inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - printf("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg->peer->address); + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); // A direct path is known for nodeId } if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { @@ -127,8 +109,9 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api * - * - An exception to the above rule is if you are using an Ad-hoc network, it has no - * controller and therefore requires no authorization. + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) * * * ESTABLISHING A CONNECTION: @@ -160,28 +143,27 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * [ 0] ZTS_ERR_OK - No error. - * [-1] ZTS_ERR - Error (see zts_errno for more information). - * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. - * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. - * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). - * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. - * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With * the added possibility of zts_errno being set. Much like standard * errno this will provide a more specific reason for an error's occurrence. - * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h - * and closely map to standard Linux error values. + * See ZeroTierSockets.h for values. * * * API COMPATIBILITY WITH HOST OS: * - * - Since libzt re-implements a socket API probably very similar to your host OS's own - * API 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, so here are a few - * guidelines: + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: * @@ -192,19 +174,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) * * struct zts_sockaddr_in in4; <------ Note the zts_* prefix * ... - * zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) * - * If you are calling a host OS function, use your host OS's constants (and structures!): - * - * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) - * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) - * - * 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: - * - * struct zts_sockaddr_in6 in6; - * ... - * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ int main(int argc, char **argv) @@ -219,11 +190,11 @@ int main(int argc, char **argv) int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) struct zts_sockaddr_in in4, acc_in4; - in4.sin_port = htons(serverBindPort); + in4.sin_port = zts_htons(serverBindPort); #if defined(_WIN32) - in4.sin_addr.S_addr = INADDR_ANY; + in4.sin_addr.S_addr = ZTS_INADDR_ANY; #else - in4.sin_addr.s_addr = INADDR_ANY; + in4.sin_addr.s_addr = ZTS_INADDR_ANY; #endif in4.sin_family = ZTS_AF_INET; @@ -237,7 +208,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { delay_ms(50); } + while (!nodeReady) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -246,7 +217,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { delay_ms(50); } + while (!networkReady) { zts_delay_ms(50); } // Socket-like API example @@ -256,7 +227,7 @@ int main(int argc, char **argv) exit(1); } printf("Binding...\n"); - if ((err = zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0)) { + if ((err = zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0)) { printf("Error binding to interface (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); exit(1); } @@ -272,17 +243,17 @@ int main(int argc, char **argv) memset(recvBuf, 0, sizeof(recvBuf)); while (true) { - socklen_t client_addrlen = sizeof(zts_sockaddr_in); - if ((accfd = zts_accept(fd, (struct sockaddr *)&acc_in4, &client_addrlen)) < 0) { + zts_socklen_t client_addrlen = sizeof(zts_sockaddr_in); + if ((accfd = zts_accept(fd, (struct zts_sockaddr *)&acc_in4, &client_addrlen)) < 0) { printf("Error accepting connection (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); } - socklen_t peer_addrlen = sizeof(struct zts_sockaddr_storage); - zts_getpeername(accfd, (struct sockaddr*)&acc_in4, &peer_addrlen); + zts_socklen_t peer_addrlen = sizeof(struct zts_sockaddr_storage); + zts_getpeername(accfd, (struct zts_sockaddr*)&acc_in4, &peer_addrlen); - char ipstr[INET_ADDRSTRLEN]; + char ipstr[ZTS_INET_ADDRSTRLEN]; memset(ipstr, 0, sizeof(ipstr)); - inet_ntop(AF_INET, &(acc_in4.sin_addr), ipstr, INET_ADDRSTRLEN); - printf("Accepted connection from %s:%d\n", ipstr, ntohs(acc_in4.sin_port)); + zts_inet_ntop(ZTS_AF_INET, &(acc_in4.sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("Accepted connection from %s:%d\n", ipstr, zts_ntohs(acc_in4.sin_port)); printf("Reading message string from client...\n"); if((bytes = zts_read(accfd, recvBuf, sizeof(recvBuf))) < 0) { diff --git a/examples/objective-c/adhoc.m b/examples/objective-c/adhoc.m new file mode 100644 index 0000000..26323e3 --- /dev/null +++ b/examples/objective-c/adhoc.m @@ -0,0 +1,247 @@ +/** + * libzt API example + * + * Specify location of zt.framework and link to standard C++ library: + * + * clang -lc++ -framework Foundation -F . -framework zt adhoc.m -o adhoc; + * + * Pingable node joined to controller-less adhoc network with a 6PLANE addressing scheme + */ + +#import + +#import + +#include + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * [ 0] ZTS_ERR_OK - No error. + * [-1] ZTS_ERR - Error (see zts_errno for more information). + * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. + * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. + * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). + * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. + * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h + * and closely map to standard Linux error values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - Since libzt re-implements a socket API probably very similar to your host OS's own + * API 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, so here are a few + * guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * 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!): + * + * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) + * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) + * + * 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: + * + * struct zts_sockaddr_in6 in6; + * ... + * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); + */ + +void delay_ms(long ms) { usleep(ms*1000); } + +bool nodeReady = false; +bool networkReady = false; + +// Example callbacks +void myZeroTierEventCallback(struct zts_callback_msg *msg) +{ + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + NSLog(@"ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + nodeReady = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + NSLog(@"ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + nodeReady = false; + } + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + NSLog(@"ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { + NSLog(@"ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) { + NSLog(@"ZTS_EVENT_NETWORK_ACCESS_DENIED --- Access to virtual network %llx has been denied. Did you authorize the node yet?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) { + NSLog(@"ZTS_EVENT_NETWORK_READY_IP6 --- Network config received. IPv6 traffic can now be sent over network %llx\n", + msg->network->nwid); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + NSLog(@"ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + // Network stack events + if (msg->eventCode == ZTS_EVENT_NETIF_UP) { + NSLog(@"ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + msg->netif->nwid, + msg->netif->mac, + msg->netif->mtu); + networkReady = true; + } + if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { + NSLog(@"ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + msg->netif->nwid, + msg->netif->mac); + + networkReady = true; + } + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + NSLog(@"ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", + msg->addr->nwid, ipstr); + } + // Peer events + // If you don't recognize the peer ID, don't panic, this is most likely one of our root servers + if (msg->eventCode == ZTS_EVENT_PEER_P2P) { + NSLog(@"ZTS_EVENT_PEER_P2P --- There is now a direct path to peer %llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + NSLog(@"ZTS_EVENT_PEER_RELAY --- No direct path to peer %llx\n", + msg->peer->address); + } +} + +/* + +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 != 5) { + NSLog(@"\nlibzt example\n"); + NSLog(@"adhoc \n"); + exit(0); + } + int adhocStartPort = atoi(argv[2]); // Start of port range your application will use + int adhocEndPort = atoi(argv[3]); // End of port range your application will use + int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) + + uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); + int err = ZTS_ERR_OK; + + zts_set_network_caching(false); + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + NSLog(@"Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + NSLog(@"Waiting for node to come online...\n"); + while (!nodeReady) { delay_ms(50); } + NSLog(@"This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(adhoc_nwid)) != ZTS_ERR_OK) { + NSLog(@"Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + NSLog(@"Joining network %llx\n", adhoc_nwid); + while (!networkReady) { delay_ms(50); } + + // Idle and just show callback events, stack statistics, etc + + NSLog(@"Node will now idle...\n"); + while (true) { delay_ms(1000); } + + // Shut down service and stack threads + + zts_stop(); + return 0; +} diff --git a/examples/swift/main.swift b/examples/swift/main.swift new file mode 100644 index 0000000..36dbc92 --- /dev/null +++ b/examples/swift/main.swift @@ -0,0 +1,143 @@ +/** + * libzt Swift example + * + * swiftc -lc++ -import-objc-header ../../include/ZeroTierSockets.h -L. -lzt main.swift -o main; + * ./main + */ + +import Swift +import Foundation + +var nodeReady:Bool = false +var networkReady:Bool = false + +let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void = +{ + (msgPtr) -> Void in + let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) + + var eventCode = msg!.pointee.eventCode + + let node = msg?.pointee.node; + let network = msg?.pointee.network; + + switch Int32(eventCode) + { + case ZTS_EVENT_NODE_ONLINE: + let nodeId:UInt64 = node!.pointee.address + print(String(format: "ZTS_EVENT_NODE_ONLINE (%llx)", nodeId)) + nodeReady = true; + + case ZTS_EVENT_NODE_OFFLINE: + print("ZTS_EVENT_NODE_OFFLINE\n") + nodeReady = false; + + case ZTS_EVENT_NODE_NORMAL_TERMINATION: + print("ZTS_EVENT_NODE_NORMAL_TERMINATION\n") + + case ZTS_EVENT_NETWORK_NOT_FOUND: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_NOT_FOUND (%llx)", networkId)) + + case ZTS_EVENT_NETWORK_REQUESTING_CONFIG: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_REQUESTING_CONFIG (%llx)", networkId)) + + case ZTS_EVENT_NETWORK_ACCESS_DENIED: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_ACCESS_DENIED (%llx)", networkId)) + + case ZTS_EVENT_NETWORK_READY_IP4: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_READY_IP4 (%llx)", networkId)) + networkReady = true; + + case ZTS_EVENT_NETWORK_READY_IP6: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_READY_IP6 (%llx)", networkId)) + networkReady = true; + + case ZTS_EVENT_NETWORK_DOWN: + let networkId:UInt64 = network!.pointee.nwid + print(String(format: "ZTS_EVENT_NETWORK_DOWN (%llx)", networkId)) + +/* + // Network stack events + case ZTS_EVENT_NETIF_UP: + print("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + msg.netif->nwid, + msg.netif->mac, + msg.netif->mtu) + //networkReady = true; + + case ZTS_EVENT_NETIF_DOWN: + print("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + msg.netif->nwid, + msg.netif->mac) + //networkReady = true; + + // Address events + case ZTS_EVENT_ADDR_ADDED_IP4: + print("ZTS_EVENT_ADDR_ADDED_IP4") + /* + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg.addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + print("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg.addr->nwid, ipstr) + */ + + case ZTS_EVENT_ADDR_ADDED_IP6: + print("ZTS_EVENT_ADDR_ADDED_IP6") + /* + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg.addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + print("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", + msg.addr->nwid, ipstr) + */ + + case ZTS_EVENT_ADDR_REMOVED_IP4: + print("ZTS_EVENT_ADDR_REMOVED_IP4") + /* + char ipstr[INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg.addr->addr); + inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); + print("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg.addr->nwid) + */ + + case ZTS_EVENT_ADDR_REMOVED_IP6: + print("ZTS_EVENT_ADDR_REMOVED_IP6") + /* + char ipstr[INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg.addr->addr); + inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); + print("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg.addr->nwid) + */ + // Peer events + case ZTS_EVENT_PEER_P2P: + print("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg.peer->address) + case ZTS_EVENT_PEER_RELAY: + print("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg.peer->address) + + +*/ + default: + print("UNKNOWN_EVENT") + } +} + +func main() +{ + print("waiting for node to come online...") + zts_start("../../config_path_a", myZeroTierEventCallback, 0) + while(!nodeReady) { + sleep(1) + } + print("Joining network") + +} + +main() diff --git a/ext/lwip b/ext/lwip index 95d6387..32708c0 160000 --- a/ext/lwip +++ b/ext/lwip @@ -1 +1 @@ -Subproject commit 95d6387123171d812552cd6213034c823a7e8631 +Subproject commit 32708c0a8b140efb545cc35101ee5fdeca6d6489 diff --git a/include/README.md b/include/README.md new file mode 100644 index 0000000..f2318b7 --- /dev/null +++ b/include/README.md @@ -0,0 +1,4 @@ +ZeroTier Socket API +====== + +This is the externally facing plain C API. It provides a platform-agnostic ZeroTier-based socket interface. diff --git a/include/Xcode-Bridging-Header.h b/include/Xcode-Bridging-Header.h deleted file mode 100644 index 1317ec1..0000000 --- a/include/Xcode-Bridging-Header.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * ZeroTier socket API - */ - -#ifndef LIBZT_BRIDGING_HEADER_H -#define LIBZT_BRIDGING_HEADER_H - -#include -#include "ZeroTier.h" - -////////////////////////////////////////////////////////////////////////////// -// Service Controls // -////////////////////////////////////////////////////////////////////////////// - -int zts_start(const char *path, void *callbackFunc, int port); -void zts_stop(); -int zts_join(uint64_t nwid); -int zts_leave(uint64_t nwid); -uint64_t zts_get_node_id(); -uint64_t zts_get_node_status(); -int get_peer_status(uint64_t peerId); - -////////////////////////////////////////////////////////////////////////////// -// Socket API // -////////////////////////////////////////////////////////////////////////////// - -int zts_socket(int socket_family, int socket_type, int protocol); -int zts_connect(int fd, const struct sockaddr *addr, socklen_t addrlen); -int zts_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); -int zts_listen(int fd, int backlog); -int zts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); -int zts_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen); -int zts_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen); -int zts_read(int fd, void *buf, size_t len); -int zts_write(int fd, const void *buf, size_t len); -ssize_t zts_send(int fd, const void *buf, size_t len, int flags); -ssize_t zts_sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); -ssize_t zts_sendmsg(int fd, const struct msghdr *msg, int flags); -ssize_t zts_recv(int fd, void *buf, size_t len, int flags); -ssize_t zts_recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); -ssize_t zts_recvmsg(int fd, struct msghdr *msg,int flags); -int zts_shutdown(int fd, int how); -int zts_close(int fd); -int zts_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen); -int zts_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen); -int zts_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); -int zts_fcntl(int fd, int cmd, int flags); -int zts_ioctl(int fd, unsigned long request, void *argp); - -#endif // _H - - - - diff --git a/include/ZeroTier.h b/include/ZeroTier.h deleted file mode 100644 index 464d56c..0000000 --- a/include/ZeroTier.h +++ /dev/null @@ -1,1277 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * ZeroTier socket API - */ - -#ifndef ZEROTIER_H -#define ZEROTIER_H - -#include "ZeroTierConstants.h" - -#if defined(_MSC_VER) -#include -typedef int ssize_t; -#endif - -#ifdef _WIN32 - #ifdef ADD_EXPORTS - #define ZT_SOCKET_API __declspec(dllexport) - #else - #define ZT_SOCKET_API __declspec(dllimport) - #endif - #define ZTCALL __cdecl -#else - #define ZT_SOCKET_API - #define ZTCALL -#endif - -#if !defined(_WIN32) && !defined(__ANDROID__) -typedef unsigned int socklen_t; -#else -//typedef int socklen_t; -//#include -#endif - -#if defined(__APPLE__) - #include "TargetConditionals.h" - //#if TARGET_OS_IPHONE - //#ifndef sockaddr_storage - #include - //#endif - //#endif - // TARGET_OS_MAC -#endif - -#if defined(_WIN32) -#include -#include -#include -#include -#endif - -//namespace ZeroTier { - -#ifdef __cplusplus -extern "C" { -#endif - -typedef uint32_t zts_in_addr_t; -typedef uint16_t zts_in_port_t; -typedef uint8_t zts_sa_family_t; - -struct zts_in_addr { -#if defined(_WIN32) - zts_in_addr_t S_addr; -#else - zts_in_addr_t s_addr; -#endif -}; - -struct zts_in6_addr { - union un { - uint32_t u32_addr[4]; - uint8_t u8_addr[16]; - } un; -//#define s6_addr un.u8_addr -}; - -struct zts_sockaddr_in { - uint8_t sin_len; - zts_sa_family_t sin_family; - zts_in_port_t sin_port; - struct zts_in_addr sin_addr; -#define SIN_ZERO_LEN 8 - char sin_zero[SIN_ZERO_LEN]; -}; - -struct zts_sockaddr_in6 { - uint8_t sin6_len; // length of this structure - zts_sa_family_t sin6_family; // AF_INET6 - zts_in_port_t sin6_port; // Transport layer port # - uint32_t sin6_flowinfo; // IPv6 flow information - struct zts_in6_addr sin6_addr; // IPv6 address - uint32_t sin6_scope_id; // Set of interfaces for scope -}; - -struct zts_sockaddr { - uint8_t sa_len; - zts_sa_family_t sa_family; - char sa_data[14]; -}; - -struct zts_sockaddr_storage { - uint8_t s2_len; - zts_sa_family_t ss_family; - char s2_data1[2]; - uint32_t s2_data2[3]; - uint32_t s2_data3[3]; -}; - -#if !defined(zts_iovec) -struct zts_iovec { - void *iov_base; - size_t iov_len; -}; -#endif - -struct zts_msghdr { - void *msg_name; - socklen_t msg_namelen; - struct iovec *msg_iov; - int msg_iovlen; - void *msg_control; - socklen_t msg_controllen; - int msg_flags; -}; - -/* - * Structure used for manipulating linger option. - */ -struct zts_linger { - int l_onoff; // option on/off - int l_linger; // linger time in seconds -}; - -typedef struct zts_fd_set -{ - unsigned char fd_bits [(FD_SETSIZE+7)/8]; -} zts_fd_set; - -#ifdef __cplusplus -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// Subset of: ZeroTierOne.h // -// We redefine a few ZT structures here so that we don't need to drag the // -// entire ZeroTierOne.h file into the user application // -////////////////////////////////////////////////////////////////////////////// - -/** - * Maximum address assignments per network - */ -#define ZTS_MAX_ASSIGNED_ADDRESSES 16 - -/** - * Maximum routes per network - */ -#define ZTS_MAX_NETWORK_ROUTES 32 - -/** - * Maximum number of direct network paths to a given peer - */ -#define ZT_MAX_PEER_NETWORK_PATHS 16 - -/** - * What trust hierarchy role does this peer have? - */ -enum zts_peer_role -{ - ZTS_PEER_ROLE_LEAF = 0, // ordinary node - ZTS_PEER_ROLE_MOON = 1, // moon root - ZTS_PEER_ROLE_PLANET = 2 // planetary root -}; - -/** - * A structure used to convey details about the current node - * to the user application - */ -struct zts_node_details -{ - /** - * The node ID - */ - uint64_t address; - - /** - * The current clock value accord to the node - */ - uint64_t clock; - - /** - * Whether or not this node is online - */ - bool online; - - /** - * Whether port mapping is enabled - */ - bool portMappingEnabled; - - /** - * Whether multipath support is enabled. If true, this node will - * be capable of utilizing multiple physical links simultaneously - * to create higher quality or more robust aggregate links. - * - * See: https://www.zerotier.com/manual.shtml#2_1_5 - */ - bool multipathEnabled; - - /** - * The port used by the service to send and receive - * all encapsulated traffic - */ - uint16_t primaryPort; - - /** - * Planet ID - */ - uint64_t planetWorldId; - uint64_t planetWorldTimestamp; - uint8_t versionMajor; - uint8_t versionMinor; - uint8_t versionRev; -}; - -/** - * A structure used to convey information to a user application via - * a callback function. - */ -struct zts_callback_msg -{ - /** - * Event identifier - */ - 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_physical_path *path; - struct zts_peer_details *peer; - struct zts_addr_details *addr; -}; - -struct zts_addr_details -{ - uint64_t nwid; - struct sockaddr_storage addr; -}; - -/** - * A structure used to convey information about a virtual network - * interface (netif) to a user application. - */ -struct zts_netif_details -{ - /** - * The virtual network that this interface was commissioned for. - */ - uint64_t nwid; - - /** - * The hardware address assigned to this interface - */ - uint64_t mac; - - /** - * The MTU for this interface - */ - int mtu; - - /** - * The IPv4 address assigned to this interface. - */ - //struct sockaddr_in ip4_addr; - - /** - * The IPv6 addresses assigned to this interface. - */ - //struct sockaddr_in6 ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; - - /** - * Number of IPv4 addresses assigned to this interface - */ - //int num_ip4_addr; - - /** - * Number of IPv6 addresses assigned to this interface - */ - //int num_ip6_addr; -}; - -/** - * A structure used to represent a virtual network route - */ -struct zts_virtual_network_route -{ - /** - * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default - */ - struct sockaddr_storage target; - - /** - * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) - */ - struct sockaddr_storage via; - - /** - * Route flags - */ - uint16_t flags; - - /** - * Route metric (not currently used) - */ - uint16_t metric; -}; - -/** - * A structure used to convey network-specific details to the user application - */ -struct zts_network_details -{ - /** - * Network ID - */ - uint64_t nwid; - - /** - * Maximum Transmission Unit size for this network - */ - int mtu; - - /** - * Number of addresses (actually) assigned to the node on this network - */ - short num_addresses; - - /** - * Array of IPv4 and IPv6 addresses assigned to the node on this network - */ - struct sockaddr_storage addr[ZTS_MAX_ASSIGNED_ADDRESSES]; - - /** - * Number of routes - */ - unsigned int num_routes; - - /** - * Array of IPv4 and IPv6 addresses assigned to the node on this network - */ - struct zts_virtual_network_route routes[ZTS_MAX_NETWORK_ROUTES]; -}; - -/** - * Physical network path to a peer - */ -struct zts_physical_path -{ - /** - * Address of endpoint - */ - struct sockaddr_storage address; - - /** - * Time of last send in milliseconds or 0 for never - */ - uint64_t lastSend; - - /** - * Time of last receive in milliseconds or 0 for never - */ - uint64_t lastReceive; - - /** - * Is this a trusted path? If so this will be its nonzero ID. - */ - uint64_t trustedPathId; - - /** - * Is path expired? - */ - int expired; - - /** - * Is path preferred? - */ - int preferred; -}; - -/** - * Peer status result buffer - */ -struct zts_peer_details -{ - /** - * ZeroTier address (40 bits) - */ - uint64_t address; - - /** - * Remote major version or -1 if not known - */ - int versionMajor; - - /** - * Remote minor version or -1 if not known - */ - int versionMinor; - - /** - * Remote revision or -1 if not known - */ - int versionRev; - - /** - * Last measured latency in milliseconds or -1 if unknown - */ - int latency; - - /** - * What trust hierarchy role does this device have? - */ - enum zts_peer_role role; - - /** - * Number of paths (size of paths[]) - */ - unsigned int pathCount; - - /** - * Known network paths to peer - */ - struct zts_physical_path paths[ZT_MAX_PEER_NETWORK_PATHS]; -}; - -/** - * List of peers - */ -struct zts_peer_list -{ - struct zts_peer_details *peers; - unsigned long peerCount; -}; - -////////////////////////////////////////////////////////////////////////////// -// ZeroTier Service Controls // -////////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Enable or disable whether the service will cache network details (enabled by default) - * - * This can potentially shorten (startup) times. This allows the service to nearly instantly - * inform the network stack of an address to use for this peer so that it can - * create an interface. This can be disabled for cases where one may not want network - * config details to be written to storage. This is especially useful for situations where - * address assignments do not change often. - * - * @usage Should be called before zts_start() if you intend on changing its state. - * - * @param enabled Whether or not this feature is enabled - */ -ZT_SOCKET_API void ZTCALL zts_set_network_caching(bool enabled); - -/** - * @brief Enable or disable whether the service will cache peer details (enabled by default) - * - * This can potentially shorten (connection) times. This allows the service to - * re-use previously discovered paths to a peer, this prevents the service from having - * to go through the entire transport-triggered link provisioning process. This is especially - * useful for situations where paths to peers do not change often. This is enabled by default - * and can be disabled for cases where one may not want peer details to be written to storage. - * - * @usage Should be called before zts_start() if you intend on changing its state. - * - * @param enabled Whether or not this feature is enabled - */ -ZT_SOCKET_API void ZTCALL zts_set_peer_caching(bool enabled); - -/** - * @brief Starts the ZeroTier service and notifies user application of events via callback - * - * @usage Should be called at the beginning of your application. Will blocks until all of the following conditions are met: - * - ZeroTier core service has been initialized - * - Cryptographic identity has been generated or loaded from directory specified by `path` - * - Virtual network is successfully joined - * - IP address is assigned by network controller service - * @param path path directory where cryptographic identities and network configuration files are stored and retrieved - * (`identity.public`, `identity.secret`) - * @param userCallbackFunc User-specified callback for ZeroTier events - * @return 0 if successful; or 1 if failed - */ -ZT_SOCKET_API int ZTCALL zts_start(const char *path, void (*userCallbackFunc)(struct zts_callback_msg*), int port); - -/** - * @brief Stops the ZeroTier service, brings down all virtual interfaces in order to stop all traffic processing. - * - * @usage This should be called when the application anticipates not needing any sort of traffic processing for a - * prolonged period of time. The stack driver (with associated timers) will remain active in case future traffic - * processing is required. Note that the application must tolerate a multi-second startup time if zts_start() - * zts_startjoin() is called again. To stop this background thread and free all resources use zts_free() instead. - * @return Returns 0 on success, -1 on failure - */ -ZT_SOCKET_API int ZTCALL zts_stop(); - -/** - * @brief Stops and re-starts the ZeroTier service. - * - * @usage This call will block until the service has been brought offline. Then - * it will return and the user application can then watch for the appropriate - * startup callback events. - * @return Returns ZTS_ERR_OK on success, -1 on failure - */ -ZT_SOCKET_API int ZTCALL zts_restart(); - -/** - * @brief Stops all background services, brings down all interfaces, frees all resources. After calling this function - * an application restart will be required before the library can be used again. This is a blocking call. - * - * @usage This should be called at the end of your program or when you do not anticipate communicating over ZeroTier - * @return Returns 0 on success, -1 on failure - */ -ZT_SOCKET_API int ZTCALL zts_free(); - -/** - * @brief Return whether the ZeroTier service is currently running - * - * @usage Call this after zts_start() - * @return 1 if running, 0 if not running - */ -ZT_SOCKET_API int ZTCALL zts_core_running(); - -/** - * @brief Return the number of networks currently joined by this node - * - * @usage Call this after zts_start(), zts_startjoin() and/or zts_join() - * @return Number of networks joined by this node - */ -ZT_SOCKET_API size_t ZTCALL zts_get_num_joined_networks(); - -/** - * @brief Populates a structure with details for a given network - * - * @usage Call this from the application thread any time after the node has joined a network - * @param nwid A 16-digit hexidecimal virtual network ID - * @param nd Pointer to a zts_network_details structure to populate - * @return ZTS_ERR_SERVICE if failed, 0 if otherwise - */ -ZT_SOCKET_API int ZTCALL zts_get_network_details(uint64_t nwid, struct zts_network_details *nd); - -/** - * @brief Populates an array of structures with details for any given number of networks - * - * @usage Call this from the application thread any time after the node has joined a network - * @param nds Pointer to an array of zts_network_details structures to populate - * @param num Number of zts_network_details structures available to copy data into, will be updated - * to reflect number of structures that were actually populated - * @return ZTS_ERR_SERVICE if failed, 0 if otherwise - */ -ZT_SOCKET_API int ZTCALL zts_get_all_network_details(struct zts_network_details *nds, int *num); - -/** - * @brief Join a network - * - * @usage Call this from application thread. Only after zts_start() has succeeded - * @param nwid A 16-digit hexidecimal virtual network ID - * @return 0 if successful, -1 for any failure - */ -ZT_SOCKET_API int ZTCALL zts_join(const uint64_t nwid); - -/** - * @brief Leave a network - * - * @usage Call this from application thread. Only after zts_start() has succeeded - * @param nwid A 16-digit hexidecimal virtual network ID - * @return 0 if successful, -1 for any failure - */ -ZT_SOCKET_API int ZTCALL zts_leave(const uint64_t nwid); - -/** - * @brief Leaves all networks - * - * @usage Call this from application thread. Only after zts_start() has succeeded - * @return 0 if successful, -1 for any failure - */ -ZT_SOCKET_API int ZTCALL zts_leave_all(); - -/** - * @brief Orbits a given moon (user-defined root server) - * - * @usage Call this from application thread. Only after zts_start() has succeeded - * @param moonWorldId A 16-digit hexidecimal world ID - * @param moonSeed A 16-digit hexidecimal seed ID - * @return ZTS_ERR_OK if successful, ZTS_ERR_SERVICE, ZTS_ERR_INVALID_ARG, ZTS_ERR_INVALID_OP if otherwise - */ -ZT_SOCKET_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed); - -/** - * @brief De-orbits a given moon (user-defined root server) - * - * @usage Call this from application thread. Only after zts_start() has succeeded - * @param moonWorldId A 16-digit hexidecimal world ID - * @return ZTS_ERR_OK if successful, ZTS_ERR_SERVICE, ZTS_ERR_INVALID_ARG, ZTS_ERR_INVALID_OP if otherwise - */ -ZT_SOCKET_API int ZTCALL zts_deorbit(uint64_t moonWorldId); - -/** - * @brief Copies the configuration path used by ZeroTier into the provided buffer - * - * @usage Use this to determine where ZeroTier is storing identity files - * @param homePath Path to ZeroTier configuration files - * @param len Length of destination buffer - * @return 0 if no error, -1 if invalid argument was supplied - */ -ZT_SOCKET_API int ZTCALL zts_get_path(char *homePath, size_t *len); - -/** - * @brief Returns the node ID of this instance - * - * @usage Call this after zts_start() and/or when zts_running() returns true - * @return - */ -ZT_SOCKET_API uint64_t ZTCALL zts_get_node_id(); - -/** - * @brief Returns whether any address has been assigned to the SockTap for this network - * - * @usage This is used as an indicator of readiness for service for the ZeroTier core and stack - * @param nwid Network ID - * @return - */ -ZT_SOCKET_API int ZTCALL zts_has_address(const uint64_t nwid); - - -/** - * @brief Returns the number of addresses assigned to this node for the given nwid - * - * @param nwid Network ID - * @return The number of addresses assigned - */ -ZT_SOCKET_API int ZTCALL zts_get_num_assigned_addresses(const uint64_t nwid); - -/** - * @brief Returns the assigned address located at the given index - * - * @usage The indices of each assigned address are not guaranteed and should only - * be used for iterative purposes. - * @param nwid Network ID - * @param index location of assigned address - * @return The number of addresses assigned - */ -ZT_SOCKET_API int ZTCALL zts_get_address_at_index( - const uint64_t nwid, const int index, struct sockaddr *addr, socklen_t *addrlen); - -/** - * @brief Get IP address for this device on a given network - * - * @usage FIXME: Only returns first address found, good enough for most cases - * @param nwid Network ID - * @param addr Destination structure for address - * @param addrlen size of destination address buffer, will be changed to size of returned address - * @return 0 if an address was successfully found, -1 if failure - */ -ZT_SOCKET_API int ZTCALL zts_get_address( - const uint64_t nwid, struct sockaddr_storage *addr, const int address_family); - -/** - * @brief Computes a 6PLANE IPv6 address for the given Network ID and Node ID - * - * @usage Can call any time - * @param addr Destination structure for address - * @param nwid Network ID - * @param nodeId Node ID - * @return - */ -ZT_SOCKET_API int ZTCALL zts_get_6plane_addr( - struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); - -/** - * @brief Computes a RFC4193 IPv6 address for the given Network ID and Node ID - * - * @usage Can call any time - * @param addr Destination structure for address - * @param nwid Network ID - * @param nodeId Node ID - * @return - */ -ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( - struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); - -/** - * 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. - * - */ -uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange); - -/** - * @brief Return the number of peers - * - * @usage Call this after zts_start() has succeeded - * @return - */ -ZT_SOCKET_API int zts_get_peer_count(); - -/** - * @brief Return details of all peers - * - * @param pds Pointer to array of zts_peer_details structs to be filled out - * @param num Length of destination array, will be filled out with actual number - * of peers that details were available for. - * @usage Call this after zts_start() has succeeded - * @return - */ -ZT_SOCKET_API int zts_get_peers(struct zts_peer_details *pds, unsigned int *num); - -/** - * @brief Return details of a given peer. - * - * @param pds Pointer to zts_peer_details struct to be filled out - * @param peerId ID of peer that the caller wants details of - * @usage Call this after zts_start() has succeeded - * @return - */ -ZT_SOCKET_API int zts_get_peer(struct zts_peer_details *pds, uint64_t peerId); - -/** - * @brief Starts a ZeroTier service in the background - * - * @usage For internal use only. - * @param - * @return - */ -#if defined(_WIN32) -DWORD WINAPI _zts_start_service(LPVOID thread_id); -#else -void *_zts_start_service(void *thread_id); -#endif - -/** - * @brief [Should not be called from user application] This function must be surrounded by - * ZT service locks. It will determine if it is currently safe and allowed to operate on - * the service. - * @usage Can be called at any time - * @return 1 or 0 - */ -int _zts_can_perform_service_operation(); - -////////////////////////////////////////////////////////////////////////////// -// Statistics // -////////////////////////////////////////////////////////////////////////////// - -#ifndef LWIP_STATS - -/** Protocol related stats */ -struct zts_stats_proto { - uint32_t xmit; /* Transmitted packets. */ - uint32_t recv; /* Received packets. */ - uint32_t fw; /* Forwarded packets. */ - uint32_t drop; /* Dropped packets. */ - uint32_t chkerr; /* Checksum error. */ - uint32_t lenerr; /* Invalid length error. */ - uint32_t memerr; /* Out of memory error. */ - uint32_t rterr; /* Routing error. */ - uint32_t proterr; /* Protocol error. */ - uint32_t opterr; /* Error in options. */ - uint32_t err; /* Misc error. */ - uint32_t cachehit; -}; - -/** IGMP stats */ -struct zts_stats_igmp { - uint32_t xmit; /* Transmitted packets. */ - uint32_t recv; /* Received packets. */ - uint32_t drop; /* Dropped packets. */ - uint32_t chkerr; /* Checksum error. */ - uint32_t lenerr; /* Invalid length error. */ - uint32_t memerr; /* Out of memory error. */ - uint32_t proterr; /* Protocol error. */ - uint32_t rx_v1; /* Received v1 frames. */ - uint32_t rx_group; /* Received group-specific queries. */ - uint32_t rx_general; /* Received general queries. */ - uint32_t rx_report; /* Received reports. */ - uint32_t tx_join; /* Sent joins. */ - uint32_t tx_leave; /* Sent leaves. */ - uint32_t tx_report; /* Sent reports. */ -}; - -/** System element stats */ -struct zts_stats_syselem { - uint32_t used; - uint32_t max; - uint32_t err; -}; - -/** System stats */ -struct zts_stats_sys { - struct zts_stats_syselem sem; - struct zts_stats_syselem mutex; - struct zts_stats_syselem mbox; -}; - -/** lwIP stats container */ -struct zts_stats { - /** Link level */ - struct zts_stats_proto link; - /** ARP */ - struct zts_stats_proto etharp; - /** Fragmentation */ - struct zts_stats_proto ip_frag; - /** IP */ - struct zts_stats_proto ip; - /** ICMP */ - struct zts_stats_proto icmp; - /** IGMP */ - struct zts_stats_igmp igmp; - /** UDP */ - struct zts_stats_proto udp; - /** TCP */ - struct zts_stats_proto tcp; - /** System */ - struct zts_stats_sys sys; - /** IPv6 */ - struct zts_stats_proto ip6; - /** ICMP6 */ - struct zts_stats_proto icmp6; - /** IPv6 fragmentation */ - struct zts_stats_proto ip6_frag; - /** Multicast listener discovery */ - struct zts_stats_igmp mld6; - /** Neighbor discovery */ - struct zts_stats_proto nd6; -}; - -#endif - -/** - * @brief Returns all statistical counters for all protocols (inefficient) - * - * @usage This function can only be used in debug builds. It can be called at - * any time after the node has come online - * @return ZTS_ERR_OK if successful, ZTS_ERR_* otherwise - */ -int zts_get_all_stats(struct zts_stats *statsDest); - - -/** - * @brief Populates the given structure with the requested protocol's - * statistical counters (from lwIP) - * - * @usage This function can only be used in debug builds. It can be called at - * any time after the node has come online - * @return ZTS_ERR_OK if successful, ZTS_ERR_* otherwise - */ -int zts_get_protocol_stats(int protocolType, void *protoStatsDest); - -////////////////////////////////////////////////////////////////////////////// -// Status getters // -////////////////////////////////////////////////////////////////////////////// - -/** - * @brief Queries a the status of the core node/service - * - * @usage Can be called at any time - * @return ZTS_EVENT_NODE_ONLINE, ZTS_EVENT_NODE_OFFLINE, or standard ZTS_ errors - */ -ZT_SOCKET_API int zts_get_node_status(); - -/** - * @brief Queries a the status of a network - * - * @usage Can be called at any time - * @return ZTS_NETWORK_ values, or standard ZTS_ errors - */ -ZT_SOCKET_API int zts_get_network_status(uint64_t nwid); - -/** - * @brief Determines whether a peer is reachable via a P2P connection - * or is being relayed via roots. - * - * @usage - * @param peerId The ID of the peer to check - * @return ZTS_PEER_ values, or standard ZTS_ errors - */ -ZT_SOCKET_API int zts_get_peer_status(uint64_t peerId); - -////////////////////////////////////////////////////////////////////////////// -// Socket API // -////////////////////////////////////////////////////////////////////////////// - -/** - * @brief Create a socket - * - * This function will return an integer which can be used in much the same way as a - * typical file descriptor, however it is only valid for use with libzt library calls - * as this is merely a facade which is associated with the internal socket representation - * of both the network stacks and drivers. - * - * @usage Call this after zts_start() has succeeded - * @param socket_family Address family (AF_INET, AF_INET6) - * @param socket_type Type of socket (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW) - * @param protocol Protocols supported on this socket - * @return - */ -ZT_SOCKET_API int ZTCALL zts_socket(int socket_family, int socket_type, int protocol); - -/** - * @brief Connect a socket to a remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Remote host address to connect to - * @param addrlen Length of address - * @return - */ -ZT_SOCKET_API int ZTCALL zts_connect(int fd, const struct sockaddr *addr, socklen_t addrlen); - -/** - * @brief Bind a socket to a virtual interface - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Local interface address to bind to - * @param addrlen Length of address - * @return - */ -ZT_SOCKET_API int ZTCALL zts_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); - -/** - * @brief Listen for incoming connections - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param backlog Number of backlogged connection allowed - * @return - */ -ZT_SOCKET_API int ZTCALL zts_listen(int fd, int backlog); - -/** - * @brief Accept an incoming connection - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Address of remote host for accepted connection - * @param addrlen Length of address - * @return - */ -ZT_SOCKET_API int ZTCALL zts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); - -/** - * @brief Accept an incoming connection - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Address of remote host for accepted connection - * @param addrlen Length of address - * @param flags - * @return - */ -#if defined(__linux__) - int zts_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags); -#endif - -/** - * @brief Set socket options - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param level Protocol level to which option name should apply - * @param optname Option name to set - * @param optval Source of option value to set - * @param optlen Length of option value - * @return - */ -ZT_SOCKET_API int ZTCALL zts_setsockopt( - int fd, int level, int optname, const void *optval, socklen_t optlen); - -/** - * @brief Get socket options - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param level Protocol level to which option name should apply - * @param optname Option name to get - * @param optval Where option value will be stored - * @param optlen Length of value - * @return - */ -ZT_SOCKET_API int ZTCALL zts_getsockopt( - int fd, int level, int optname, void *optval, socklen_t *optlen); - -/** - * @brief Get socket name - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Name associated with this socket - * @param addrlen Length of name - * @return - */ -ZT_SOCKET_API int ZTCALL zts_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen); - -/** - * @brief Get the peer name for the remote end of a connected socket - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param addr Name associated with remote end of this socket - * @param addrlen Length of name - * @return - */ -ZT_SOCKET_API int ZTCALL zts_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen); - -/** - * @brief Gets current hostname - * - * @usage Call this after zts_start() has succeeded - * @param name - * @param len - * @return - */ -ZT_SOCKET_API int ZTCALL zts_gethostname(char *name, size_t len); - -/** - * @brief Sets current hostname - * - * @usage Call this after zts_start() has succeeded - * @param name - * @param len - * @return - */ -ZT_SOCKET_API int ZTCALL zts_sethostname(const char *name, size_t len); - -/** - * @brief Return a pointer to an object with the following structure describing an internet host referenced by name - * - * @usage Call this after zts_start() has succeeded - * @param name - * @return Returns pointer to hostent structure otherwise NULL if failure - */ -ZT_SOCKET_API struct hostent *zts_gethostbyname(const char *name); - -/** - * @brief Close a socket - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @return - */ -ZT_SOCKET_API int ZTCALL zts_close(int fd); - -/** - * @brief Waits for one of a set of file descriptors to become ready to perform I/O. - * - * @usage Call this after zts_start() has succeeded - * @param fds - * @param nfds - * @param timeout - * @return - */ -#if defined(__linux__) -/* -typedef unsigned int nfds_t; -int zts_poll(struct pollfd *fds, nfds_t nfds, int timeout); -*/ -#endif - -/** - * @brief Monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" - * - * @usage Call this after zts_start() has succeeded - * @param nfds - * @param readfds - * @param writefds - * @param exceptfds - * @param timeout - * @return - */ -ZT_SOCKET_API int ZTCALL zts_select( - int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); - -/** - * @brief Issue file control commands on a socket - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param cmd - * @param flags - * @return - */ -#if defined(_WIN32) -#define F_SETFL 0 -#define O_NONBLOCK 0 -#endif -ZT_SOCKET_API int ZTCALL zts_fcntl(int fd, int cmd, int flags); - -/** - * @brief Control a device - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param request - * @param argp - * @return - */ -ZT_SOCKET_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); - -/** - * @brief Send data to remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of data to write - * @param flags - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int flags); - -/** - * @brief Send data to remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of data to write - * @param flags - * @param addr Destination address - * @param addrlen Length of destination address - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_sendto( - int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen); - -/** - * @brief Send message to remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param msg - * @param flags - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_sendmsg(int fd, const struct msghdr *msg, int flags); - -/** - * @brief Receive data from remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of data buffer - * @param flags - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_recv(int fd, void *buf, size_t len, int flags); - -/** - * @brief Receive data from remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of data buffer - * @param flags - * @param addr - * @param addrlen - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_recvfrom( - int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen); - -/** - * @brief Receive a message from remote host - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param msg - * @param flags - * @return - */ -ZT_SOCKET_API ssize_t ZTCALL zts_recvmsg(int fd, struct msghdr *msg,int flags); - -/** - * @brief Read bytes from socket onto buffer - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of data buffer to receive data - * @return - */ -ZT_SOCKET_API int ZTCALL zts_read(int fd, void *buf, size_t len); - -/** - * @brief Write bytes from buffer to socket - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param buf Pointer to data buffer - * @param len Length of buffer to write - * @return - */ -ZT_SOCKET_API int ZTCALL zts_write(int fd, const void *buf, size_t len); - -/** - * @brief Shut down some aspect of a socket (read, write, or both) - * - * @usage Call this after zts_start() has succeeded - * @param fd File descriptor (only valid for use with libzt calls) - * @param how Which aspects of the socket should be shut down - * @return - */ -ZT_SOCKET_API int ZTCALL zts_shutdown(int fd, int how); - -/** - * @brief Adds a DNS nameserver for the network stack to use - * - * @usage Call this after zts_start() has succeeded - * @param addr Address for DNS nameserver - * @return - */ -ZT_SOCKET_API int ZTCALL zts_add_dns_nameserver(struct sockaddr *addr); - -/** - * @brief Removes a DNS nameserver - * - * @usage Call this after zts_start() has succeeded - * @param addr Address for DNS nameserver - * @return - */ -ZT_SOCKET_API int ZTCALL zts_del_dns_nameserver(struct sockaddr *addr); - -#ifdef __cplusplus -} // extern "C" -#endif - -//} // namespace ZeroTier - -#endif // _H diff --git a/include/ZeroTierConstants.h b/include/ZeroTierConstants.h deleted file mode 100644 index 89d1fb2..0000000 --- a/include/ZeroTierConstants.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * Common constants used throughout the SDK - */ - -#ifndef ZEROTIER_CONSTANTS_H -#define ZEROTIER_CONSTANTS_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Custom errno to prevent conflicts with platform's own errno -extern int zts_errno; - -#ifdef __cplusplus -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// Control API error codes // -////////////////////////////////////////////////////////////////////////////// - -// No error. -#define ZTS_ERR_OK 0 -// Error (see zts_errno for more info) -#define ZTS_ERR -1 -// A argument provided is invalid (e.g. out of range, NULL, etc) -#define ZTS_ERR_INVALID_ARG -2 -// The service isn't initialized or is currently unavailable. Try again. -#define ZTS_ERR_SERVICE -3 -// This API operation is not permitted or doesn't make sense at this time. -#define ZTS_ERR_INVALID_OP -4 -// The call succeeded, but no object or relevant result was available. -#define ZTS_ERR_NO_RESULT -5 -// General internal failure. Consider filing a bug report. -#define ZTS_ERR_GENERAL -6 - -/** - * The system port upon which ZT traffic is sent and received - */ -#define ZTS_DEFAULT_PORT 9994 - -////////////////////////////////////////////////////////////////////////////// -// Control API event codes // -////////////////////////////////////////////////////////////////////////////// - -#define ZTS_EVENT_NONE -1 -#define ZTS_EVENT_NODE_UP 0 -// Standard node events -#define ZTS_EVENT_NODE_OFFLINE 1 -#define ZTS_EVENT_NODE_ONLINE 2 -#define ZTS_EVENT_NODE_DOWN 3 -#define ZTS_EVENT_NODE_IDENTITY_COLLISION 4 -#define ZTS_EVENT_NODE_UNRECOVERABLE_ERROR 16 -#define ZTS_EVENT_NODE_NORMAL_TERMINATION 17 -// Network events -#define ZTS_EVENT_NETWORK_NOT_FOUND 32 -#define ZTS_EVENT_NETWORK_CLIENT_TOO_OLD 33 -#define ZTS_EVENT_NETWORK_REQUESTING_CONFIG 34 -#define ZTS_EVENT_NETWORK_OK 35 -#define ZTS_EVENT_NETWORK_ACCESS_DENIED 36 -#define ZTS_EVENT_NETWORK_READY_IP4 37 -#define ZTS_EVENT_NETWORK_READY_IP6 38 -#define ZTS_EVENT_NETWORK_READY_IP4_IP6 39 -#define ZTS_EVENT_NETWORK_DOWN 40 -// Network Stack events -#define ZTS_EVENT_STACK_UP 48 -#define ZTS_EVENT_STACK_DOWN 49 -// lwIP netif events -#define ZTS_EVENT_NETIF_UP 64 -#define ZTS_EVENT_NETIF_DOWN 65 -#define ZTS_EVENT_NETIF_REMOVED 66 -#define ZTS_EVENT_NETIF_LINK_UP 67 -#define ZTS_EVENT_NETIF_LINK_DOWN 68 -// Peer events -#define ZTS_EVENT_PEER_P2P 96 -#define ZTS_EVENT_PEER_RELAY 97 -#define ZTS_EVENT_PEER_UNREACHABLE 98 -// Path events -#define ZTS_EVENT_PATH_DISCOVERED 112 -#define ZTS_EVENT_PATH_ALIVE 113 -#define ZTS_EVENT_PATH_DEAD 114 -// Route events -#define ZTS_EVENT_ROUTE_ADDED 128 -#define ZTS_EVENT_ROUTE_REMOVED 129 -// Address events -#define ZTS_EVENT_ADDR_ADDED_IP4 144 -#define ZTS_EVENT_ADDR_REMOVED_IP4 145 -#define ZTS_EVENT_ADDR_ADDED_IP6 146 -#define ZTS_EVENT_ADDR_REMOVED_IP6 147 - -// Macros for legacy behaviour -#define NODE_EVENT_TYPE(code) code >= ZTS_EVENT_NODE_UP && code <= ZTS_EVENT_NODE_NORMAL_TERMINATION -#define NETWORK_EVENT_TYPE(code) code >= ZTS_EVENT_NETWORK_NOT_FOUND && code <= ZTS_EVENT_NETWORK_DOWN -#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_P2P && code <= ZTS_EVENT_PEER_UNREACHABLE -#define PATH_EVENT_TYPE(code) code >= ZTS_EVENT_PATH_DISCOVERED && code <= ZTS_EVENT_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 - -////////////////////////////////////////////////////////////////////////////// -// Common definitions and structures for interacting with the ZT socket API // -// This is a subset of lwip/sockets.h, lwip/arch.h, and lwip/inet.h // -// // -// These re-definitions exist here so that the user application's usage // -// of the API is internally consistent with the underlying network stack. // -// They have an attached prefix so that they can co-exist with the native // -// platform's own definitions and structures. // -////////////////////////////////////////////////////////////////////////////// - -// Socket protocol types -#define ZTS_SOCK_STREAM 0x0001 -#define ZTS_SOCK_DGRAM 0x0002 -#define ZTS_SOCK_RAW 0x0003 -// Socket family types -#define ZTS_AF_UNSPEC 0x0000 -#define ZTS_AF_INET 0x0002 -#define ZTS_AF_INET6 0x000a -#define ZTS_PF_INET ZTS_AF_INET -#define ZTS_PF_INET6 ZTS_AF_INET6 -#define ZTS_PF_UNSPEC ZTS_AF_UNSPEC -// Protocol command types -#define ZTS_IPPROTO_IP 0x0000 -#define ZTS_IPPROTO_ICMP 0x0001 -#define ZTS_IPPROTO_TCP 0x0006 -#define ZTS_IPPROTO_UDP 0x0011 -#define ZTS_IPPROTO_IPV6 0x0029 -#define ZTS_IPPROTO_ICMPV6 0x003a -#define ZTS_IPPROTO_UDPLITE 0x0088 -#define ZTS_IPPROTO_RAW 0x00ff -// send() and recv() flags -#define ZTS_MSG_PEEK 0x0001 -#define ZTS_MSG_WAITALL 0x0002 // NOT YET SUPPORTED -#define ZTS_MSG_OOB 0x0004 // NOT YET SUPPORTED -#define ZTS_MSG_DONTWAIT 0x0008 -#define ZTS_MSG_MORE 0x0010 -// fnctl() commands -#define ZTS_F_GETFL 0x0003 -#define ZTS_F_SETFL 0x0004 -// fnctl() flags -#define ZTS_O_NONBLOCK 0x0001 -#define ZTS_O_NDELAY 0x0001 -// Shutdown commands -#define ZTS_SHUT_RD 0x0000 -#define ZTS_SHUT_WR 0x0001 -#define ZTS_SHUT_RDWR 0x0002 -// Socket level option number -#define ZTS_SOL_SOCKET 0x0fff -// Socket options -#define ZTS_SO_DEBUG 0x0001 // NOT YET SUPPORTED -#define ZTS_SO_ACCEPTCONN 0x0002 -#define ZTS_SO_REUSEADDR 0x0004 -#define ZTS_SO_KEEPALIVE 0x0008 -#define ZTS_SO_DONTROUTE 0x0010 // NOT YET SUPPORTED -#define ZTS_SO_BROADCAST 0x0020 -#define ZTS_SO_USELOOPBACK 0x0040 // NOT YET SUPPORTED -#define ZTS_SO_LINGER 0x0080 -#define ZTS_SO_DONTLINGER ((int)(~ZTS_SO_LINGER)) -#define ZTS_SO_OOBINLINE 0x0100 // NOT YET SUPPORTED -#define ZTS_SO_REUSEPORT 0x0200 // NOT YET SUPPORTED -#define ZTS_SO_SNDBUF 0x1001 // NOT YET SUPPORTED -#define ZTS_SO_RCVBUF 0x1002 -#define ZTS_SO_SNDLOWAT 0x1003 // NOT YET SUPPORTED -#define ZTS_SO_RCVLOWAT 0x1004 // NOT YET SUPPORTED -#define ZTS_SO_SNDTIMEO 0x1005 -#define ZTS_SO_RCVTIMEO 0x1006 -#define ZTS_SO_ERROR 0x1007 -#define ZTS_SO_TYPE 0x1008 -#define ZTS_SO_CONTIMEO 0x1009 -#define ZTS_SO_NO_CHECK 0x100a -// IPPROTO_IP options -#define ZTS_IP_TOS 0x0001 -#define ZTS_IP_TTL 0x0002 -// IPPROTO_TCP options -#define ZTS_TCP_NODELAY 0x0001 -#define ZTS_TCP_KEEPALIVE 0x0002 -#define ZTS_TCP_KEEPIDLE 0x0003 -#define ZTS_TCP_KEEPINTVL 0x0004 -#define ZTS_TCP_KEEPCNT 0x0005 -// IPPROTO_IPV6 options -#define ZTS_IPV6_CHECKSUM 0x0007 // RFC3542 -#define ZTS_IPV6_V6ONLY 0x001b // RFC3493 -// Macro's for defining ioctl() command values -#define ZTS_IOCPARM_MASK 0x7fU -#define ZTS_IOC_VOID 0x20000000UL -#define ZTS_IOC_OUT 0x40000000UL -#define ZTS_IOC_IN 0x80000000UL -#define ZTS_IOC_INOUT (ZTS_IOC_IN | ZTS_IOC_OUT) -#define ZTS_IO(x,y) (ZTS_IOC_VOID | ((x)<<8)|(y)) -#define ZTS_IOR(x,y,t) (ZTS_IOC_OUT | (((long)sizeof(t) & ZTS_IOCPARM_MASK)<<16) | ((x)<<8) | (y)) -#define ZTS_IOW(x,y,t) (ZTS_IOC_IN | (((long)sizeof(t) & ZTS_IOCPARM_MASK)<<16) | ((x)<<8) | (y)) -// ioctl() commands -#define ZTS_FIONREAD ZTS_IOR('f', 127, unsigned long) -#define ZTS_FIONBIO ZTS_IOW('f', 126, unsigned long) - -/* FD_SET used for lwip_select */ - -#ifndef ZTS_FD_SET -#undef ZTS_FD_SETSIZE -// Make FD_SETSIZE match NUM_SOCKETS in socket.c -#define ZTS_FD_SETSIZE MEMP_NUM_NETCONN -#define ZTS_FDSETSAFESET(n, code) do { \ - if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \ - code; }} while(0) -#define ZTS_FDSETSAFEGET(n, code) (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ?\ - (code) : 0) -#define ZTS_FD_SET(n, p) ZTS_FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) -#define ZTS_FD_CLR(n, p) ZTS_FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) -#define ZTS_FD_ISSET(n,p) ZTS_FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) -#define ZTS_FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p))) - -#elif LWIP_SOCKET_OFFSET -#error LWIP_SOCKET_OFFSET does not work with external FD_SET! -#elif ZTS_FD_SETSIZE < MEMP_NUM_NETCONN -#error "external ZTS_FD_SETSIZE too small for number of sockets" -#endif // FD_SET - -////////////////////////////////////////////////////////////////////////////// -// Statistics // -////////////////////////////////////////////////////////////////////////////// - -#define ZTS_STATS_PROTOCOL_LINK 0 -#define ZTS_STATS_PROTOCOL_ETHARP 1 -#define ZTS_STATS_PROTOCOL_IP 2 -#define ZTS_STATS_PROTOCOL_UDP 3 -#define ZTS_STATS_PROTOCOL_TCP 4 -#define ZTS_STATS_PROTOCOL_ICMP 5 -#define ZTS_STATS_PROTOCOL_IP_FRAG 6 -#define ZTS_STATS_PROTOCOL_IP6 7 -#define ZTS_STATS_PROTOCOL_ICMP6 8 -#define ZTS_STATS_PROTOCOL_IP6_FRAG 9 - -//#if defined(_USING_LWIP_DEFINITIONS_) - -#endif // ZEROTIER_CONSTANTS_H \ No newline at end of file diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h new file mode 100644 index 0000000..9c870ad --- /dev/null +++ b/include/ZeroTierSockets.h @@ -0,0 +1,1688 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-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. + */ +/****/ + +/** + * @file + * + * This defines the external C API for ZeroTier Sockets + */ + +#ifndef ZT_SOCKETS_H +#define ZT_SOCKETS_H + +#include + +#if defined(_MSC_VER) + #ifndef ssize_t + // TODO: Should be SSIZE_T, would require lwIP patch + // #include + // typedef SSIZE_T ssize_t; + typedef int ssize_t; + #endif +#else + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// Event codes // +////////////////////////////////////////////////////////////////////////////// + +// Node events +#define ZTS_EVENT_NODE_UP 200 +#define ZTS_EVENT_NODE_ONLINE 201 +#define ZTS_EVENT_NODE_OFFLINE 202 +#define ZTS_EVENT_NODE_DOWN 203 +#define ZTS_EVENT_NODE_IDENTITY_COLLISION 204 +#define ZTS_EVENT_NODE_UNRECOVERABLE_ERROR 205 +#define ZTS_EVENT_NODE_NORMAL_TERMINATION 206 +// Network events +#define ZTS_EVENT_NETWORK_NOT_FOUND 210 +#define ZTS_EVENT_NETWORK_CLIENT_TOO_OLD 211 +#define ZTS_EVENT_NETWORK_REQ_CONFIG 212 +#define ZTS_EVENT_NETWORK_OK 213 +#define ZTS_EVENT_NETWORK_ACCESS_DENIED 214 +#define ZTS_EVENT_NETWORK_READY_IP4 215 +#define ZTS_EVENT_NETWORK_READY_IP6 216 +#define ZTS_EVENT_NETWORK_READY_IP4_IP6 217 +#define ZTS_EVENT_NETWORK_DOWN 218 +// Network Stack events +#define ZTS_EVENT_STACK_UP 220 +#define ZTS_EVENT_STACK_DOWN 221 +// lwIP netif events +#define ZTS_EVENT_NETIF_UP 230 +#define ZTS_EVENT_NETIF_DOWN 231 +#define ZTS_EVENT_NETIF_REMOVED 232 +#define ZTS_EVENT_NETIF_LINK_UP 233 +#define ZTS_EVENT_NETIF_LINK_DOWN 234 +// Peer events +#define ZTS_EVENT_PEER_DIRECT 240 +#define ZTS_EVENT_PEER_RELAY 241 +#define ZTS_EVENT_PEER_UNREACHABLE 242 +// Path events +#define ZTS_EVENT_PATH_DISCOVERED 250 +#define ZTS_EVENT_PATH_ALIVE 251 +#define ZTS_EVENT_PATH_DEAD 252 +// Route events +#define ZTS_EVENT_ROUTE_ADDED 260 +#define ZTS_EVENT_ROUTE_REMOVED 261 +// Address events +#define ZTS_EVENT_ADDR_ADDED_IP4 270 +#define ZTS_EVENT_ADDR_REMOVED_IP4 271 +#define ZTS_EVENT_ADDR_ADDED_IP6 272 +#define ZTS_EVENT_ADDR_REMOVED_IP6 273 + +////////////////////////////////////////////////////////////////////////////// +// Return Error codes // +////////////////////////////////////////////////////////////////////////////// + +#define ZTS_ERR_OK 0 // No error +#define ZTS_ERR_SOCKET -1 // Socket error, see zts_errno +#define ZTS_ERR_SERVICE -2 // You probably did something at the wrong time +#define ZTS_ERR_ARG -3 // Invalid argument +#define ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) +#define ZTS_ERR_GENERAL -5 // Consider filing a bug report + +////////////////////////////////////////////////////////////////////////////// +// zts_errno Error codes // +////////////////////////////////////////////////////////////////////////////// + +// Error variable set after each zts_* call to provide additional information. +extern int zts_errno; + +#define ZTS_EPERM 1 /* Operation not permitted */ +#define ZTS_ENOENT 2 /* No such file or directory */ +#define ZTS_ESRCH 3 /* No such process */ +#define ZTS_EINTR 4 /* Interrupted system call */ +#define ZTS_EIO 5 /* I/O error */ +#define ZTS_ENXIO 6 /* No such device or address */ +#define ZTS_E2BIG 7 /* Arg list too long */ +#define ZTS_ENOEXEC 8 /* Exec format error */ +#define ZTS_EBADF 9 /* Bad file number */ +#define ZTS_ECHILD 10 /* No child processes */ +#define ZTS_EAGAIN 11 /* Try again */ +#define ZTS_ENOMEM 12 /* Out of memory */ +#define ZTS_EACCES 13 /* Permission denied */ +#define ZTS_EFAULT 14 /* Bad address */ +#define ZTS_ENOTBLK 15 /* Block device required */ +#define ZTS_EBUSY 16 /* Device or resource busy */ +#define ZTS_EEXIST 17 /* File exists */ +#define ZTS_EXDEV 18 /* Cross-device link */ +#define ZTS_ENODEV 19 /* No such device */ +#define ZTS_ENOTDIR 20 /* Not a directory */ +#define ZTS_EISDIR 21 /* Is a directory */ +#define ZTS_EINVAL 22 /* Invalid argument */ +#define ZTS_ENFILE 23 /* File table overflow */ +#define ZTS_EMFILE 24 /* Too many open files */ +#define ZTS_ENOTTY 25 /* Not a typewriter */ +#define ZTS_ETXTBSY 26 /* Text file busy */ +#define ZTS_EFBIG 27 /* File too large */ +#define ZTS_ENOSPC 28 /* No space left on device */ +#define ZTS_ESPIPE 29 /* Illegal seek */ +#define ZTS_EROFS 30 /* Read-only file system */ +#define ZTS_EMLINK 31 /* Too many links */ +#define ZTS_EPIPE 32 /* Broken pipe */ +#define ZTS_EDOM 33 /* Math argument out of domain of func */ +#define ZTS_ERANGE 34 /* Math result not representable */ +#define ZTS_EDEADLK 35 /* Resource deadlock would occur */ +#define ZTS_ENAMETOOLONG 36 /* File name too long */ +#define ZTS_ENOLCK 37 /* No record locks available */ +#define ZTS_ENOSYS 38 /* Function not implemented */ +#define ZTS_ENOTEMPTY 39 /* Directory not empty */ +#define ZTS_ELOOP 40 /* Too many symbolic links encountered */ +#define ZTS_EWOULDBLOCK ZTS_EAGAIN /* Operation would block */ +#define ZTS_ENOMSG 42 /* No message of desired type */ +#define ZTS_EIDRM 43 /* Identifier removed */ +#define ZTS_ECHRNG 44 /* Channel number out of range */ +#define ZTS_EL2NSYNC 45 /* Level 2 not synchronized */ +#define ZTS_EL3HLT 46 /* Level 3 halted */ +#define ZTS_EL3RST 47 /* Level 3 reset */ +#define ZTS_ELNRNG 48 /* Link number out of range */ +#define ZTS_EUNATCH 49 /* Protocol driver not attached */ +#define ZTS_ENOCSI 50 /* No CSI structure available */ +#define ZTS_EL2HLT 51 /* Level 2 halted */ +#define ZTS_EBADE 52 /* Invalid exchange */ +#define ZTS_EBADR 53 /* Invalid request descriptor */ +#define ZTS_EXFULL 54 /* Exchange full */ +#define ZTS_ENOANO 55 /* No anode */ +#define ZTS_EBADRQC 56 /* Invalid request code */ +#define ZTS_EBADSLT 57 /* Invalid slot */ + +#define ZTS_EDEADLOCK ZTS_EDEADLK + +#define ZTS_EBFONT 59 /* Bad font file format */ +#define ZTS_ENOSTR 60 /* Device not a stream */ +#define ZTS_ENODATA 61 /* No data available */ +#define ZTS_ETIME 62 /* Timer expired */ +#define ZTS_ENOSR 63 /* Out of streams resources */ +#define ZTS_ENONET 64 /* Machine is not on the network */ +#define ZTS_ENOPKG 65 /* Package not installed */ +#define ZTS_EREMOTE 66 /* Object is remote */ +#define ZTS_ENOLINK 67 /* Link has been severed */ +#define ZTS_EADV 68 /* Advertise error */ +#define ZTS_ESRMNT 69 /* Srmount error */ +#define ZTS_ECOMM 70 /* Communication error on send */ +#define ZTS_EPROTO 71 /* Protocol error */ +#define ZTS_EMULTIHOP 72 /* Multihop attempted */ +#define ZTS_EDOTDOT 73 /* RFS specific error */ +#define ZTS_EBADMSG 74 /* Not a data message */ +#define ZTS_EOVERFLOW 75 /* Value too large for defined data type */ +#define ZTS_ENOTUNIQ 76 /* Name not unique on network */ +#define ZTS_EBADFD 77 /* File descriptor in bad state */ +#define ZTS_EREMCHG 78 /* Remote address changed */ +#define ZTS_ELIBACC 79 /* Can not access a needed shared library */ +#define ZTS_ELIBBAD 80 /* Accessing a corrupted shared library */ +#define ZTS_ELIBSCN 81 /* .lib section in a.out corrupted */ +#define ZTS_ELIBMAX 82 /* Attempting to link in too many shared libraries */ +#define ZTS_ELIBEXEC 83 /* Cannot exec a shared library directly */ +#define ZTS_EILSEQ 84 /* Illegal byte sequence */ +#define ZTS_ERESTART 85 /* Interrupted system call should be restarted */ +#define ZTS_ESTRPIPE 86 /* Streams pipe error */ +#define ZTS_EUSERS 87 /* Too many users */ +#define ZTS_ENOTSOCK 88 /* Socket operation on non-socket */ +#define ZTS_EDESTADDRREQ 89 /* Destination address required */ +#define ZTS_EMSGSIZE 90 /* Message too long */ +#define ZTS_EPROTOTYPE 91 /* Protocol wrong type for socket */ +#define ZTS_ENOPROTOOPT 92 /* Protocol not available */ +#define ZTS_EPROTONOSUPPORT 93 /* Protocol not supported */ +#define ZTS_ESOCKTNOSUPPORT 94 /* Socket type not supported */ +#define ZTS_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define ZTS_EPFNOSUPPORT 96 /* Protocol family not supported */ +#define ZTS_EAFNOSUPPORT 97 /* Address family not supported by protocol */ +#define ZTS_EADDRINUSE 98 /* Address already in use */ +#define ZTS_EADDRNOTAVAIL 99 /* Cannot assign requested address */ +#define ZTS_ENETDOWN 100 /* Network is down */ +#define ZTS_ENETUNREACH 101 /* Network is unreachable */ +#define ZTS_ENETRESET 102 /* Network dropped connection because of reset */ +#define ZTS_ECONNABORTED 103 /* Software caused connection abort */ +#define ZTS_ECONNRESET 104 /* Connection reset by peer */ +#define ZTS_ENOBUFS 105 /* No buffer space available */ +#define ZTS_EISCONN 106 /* Transport endpoint is already connected */ +#define ZTS_ENOTCONN 107 /* Transport endpoint is not connected */ +#define ZTS_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */ +#define ZTS_ETOOMANYREFS 109 /* Too many references: cannot splice */ +#define ZTS_ETIMEDOUT 110 /* Connection timed out */ +#define ZTS_ECONNREFUSED 111 /* Connection refused */ +#define ZTS_EHOSTDOWN 112 /* Host is down */ +#define ZTS_EHOSTUNREACH 113 /* No route to host */ +#define ZTS_EALREADY 114 /* Operation already in progress */ +#define ZTS_EINPROGRESS 115 /* Operation now in progress */ +#define ZTS_ESTALE 116 /* Stale NFS file handle */ +#define ZTS_EUCLEAN 117 /* Structure needs cleaning */ +#define ZTS_ENOTNAM 118 /* Not a XENIX named type file */ +#define ZTS_ENAVAIL 119 /* No XENIX semaphores available */ +#define ZTS_EISNAM 120 /* Is a named type file */ +#define ZTS_EREMOTEIO 121 /* Remote I/O error */ +#define ZTS_EDQUOT 122 /* Quota exceeded */ + +#define ZTS_ENOMEDIUM 123 /* No medium found */ +#define ZTS_EMEDIUMTYPE 124 /* Wrong medium type */ + +////////////////////////////////////////////////////////////////////////////// +// Common definitions and structures for interoperability between zts_* and // +// lwIP functions. Some of the code in the following section is a borrowed // +// from the lwIP codebase so that the user doesn't need to include headers // +// from that project in addition to the ZeroTier SDK headers. The license // +// applying to this code borrowed from lwIP is produced below and only // +// applies to the portions of code which are merely renamed versions of // +// their lwIP counterparts. The rest of the code in this C API file is // +// governed by the license text provided at the beginning of this file. // +////////////////////////////////////////////////////////////////////////////// + +/* + * Copyright (c) 2001-2004 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ + +#define ZTS_INET_ADDRSTRLEN 16 +#define ZTS_INET6_ADDRSTRLEN 46 + +/** 255.255.255.255 */ +#define ZTS_IPADDR_NONE ((uint32_t)0xffffffffUL) +/** 127.0.0.1 */ +#define ZTS_IPADDR_LOOPBACK ((uint32_t)0x7f000001UL) +/** 0.0.0.0 */ +#define ZTS_IPADDR_ANY ((uint32_t)0x00000000UL) +/** 255.255.255.255 */ +#define ZTS_IPADDR_BROADCAST ((uint32_t)0xffffffffUL) + +/** 255.255.255.255 */ +#define ZTS_INADDR_NONE ZTS_IPADDR_NONE +/** 127.0.0.1 */ +#define ZTS_INADDR_LOOPBACK ZTS_IPADDR_LOOPBACK +/** 0.0.0.0 */ +#define ZTS_INADDR_ANY ZTS_IPADDR_ANY +/** 255.255.255.255 */ +#define ZTS_INADDR_BROADCAST ZTS_IPADDR_BROADCAST + +// Socket protocol types +#define ZTS_SOCK_STREAM 0x0001 +#define ZTS_SOCK_DGRAM 0x0002 +#define ZTS_SOCK_RAW 0x0003 +// Socket family types +#define ZTS_AF_UNSPEC 0x0000 +#define ZTS_AF_INET 0x0002 +#define ZTS_AF_INET6 0x000a +#define ZTS_PF_INET ZTS_AF_INET +#define ZTS_PF_INET6 ZTS_AF_INET6 +#define ZTS_PF_UNSPEC ZTS_AF_UNSPEC +// Protocol command types +#define ZTS_IPPROTO_IP 0x0000 +#define ZTS_IPPROTO_ICMP 0x0001 +#define ZTS_IPPROTO_TCP 0x0006 +#define ZTS_IPPROTO_UDP 0x0011 +#define ZTS_IPPROTO_IPV6 0x0029 +#define ZTS_IPPROTO_ICMPV6 0x003a +#define ZTS_IPPROTO_UDPLITE 0x0088 +#define ZTS_IPPROTO_RAW 0x00ff +// send() and recv() flags +#define ZTS_MSG_PEEK 0x0001 +#define ZTS_MSG_WAITALL 0x0002 // NOT YET SUPPORTED +#define ZTS_MSG_OOB 0x0004 // NOT YET SUPPORTED +#define ZTS_MSG_DONTWAIT 0x0008 +#define ZTS_MSG_MORE 0x0010 + +// Macro's for defining ioctl() command values +#define ZTS_IOCPARM_MASK 0x7fU +#define ZTS_IOC_VOID 0x20000000UL +#define ZTS_IOC_OUT 0x40000000UL +#define ZTS_IOC_IN 0x80000000UL +#define ZTS_IOC_INOUT (ZTS_IOC_IN | ZTS_IOC_OUT) +#define ZTS_IO(x,y) (ZTS_IOC_VOID | ((x)<<8)|(y)) +#define ZTS_IOR(x,y,t) (ZTS_IOC_OUT | (((long)sizeof(t) & ZTS_IOCPARM_MASK)<<16) | ((x)<<8) | (y)) +#define ZTS_IOW(x,y,t) (ZTS_IOC_IN | (((long)sizeof(t) & ZTS_IOCPARM_MASK)<<16) | ((x)<<8) | (y)) +// ioctl() commands +#define ZTS_FIONREAD ZTS_IOR('f', 127, unsigned long) +#define ZTS_FIONBIO ZTS_IOW('f', 126, unsigned long) + +////////////////////////////////////////////////////////////////////////////// +// Custom but still mostly standard socket interface structures // +////////////////////////////////////////////////////////////////////////////// + +typedef uint32_t zts_socklen_t; +typedef uint32_t zts_in_addr_t; +typedef uint16_t zts_in_port_t; +typedef uint8_t zts_sa_family_t; + +struct zts_in_addr { +#if defined(__WINDOWS__) + zts_in_addr_t S_addr; +#else + // A definition in winsock may conflict with s_addr + zts_in_addr_t s_addr; +#endif +}; + +struct zts_in6_addr { + union un { + uint32_t u32_addr[4]; + uint8_t u8_addr[16]; + } un; +//#define s6_addr un.u8_addr +}; + +struct zts_sockaddr_in { + uint8_t sin_len; + zts_sa_family_t sin_family; + zts_in_port_t sin_port; + struct zts_in_addr sin_addr; +#define SIN_ZERO_LEN 8 + char sin_zero[SIN_ZERO_LEN]; +}; + +struct zts_sockaddr_in6 { + uint8_t sin6_len; // length of this structure + zts_sa_family_t sin6_family; // ZTS_AF_INET6 + zts_in_port_t sin6_port; // Transport layer port # + uint32_t sin6_flowinfo; // IPv6 flow information + struct zts_in6_addr sin6_addr; // IPv6 address + uint32_t sin6_scope_id; // Set of interfaces for scope +}; + +struct zts_sockaddr { + uint8_t sa_len; + zts_sa_family_t sa_family; + char sa_data[14]; +}; + +struct zts_sockaddr_storage { + uint8_t s2_len; + zts_sa_family_t ss_family; + char s2_data1[2]; + uint32_t s2_data2[3]; + uint32_t s2_data3[3]; +}; + +////////////////////////////////////////////////////////////////////////////// +// Subset of: ZeroTierOne.h // +// We redefine a few ZT structures here so that we don't need to drag the // +// entire ZeroTierOne.h file into the user application // +////////////////////////////////////////////////////////////////////////////// + +/** + * Maximum address assignments per network + */ +#define ZTS_MAX_ASSIGNED_ADDRESSES 16 + +/** + * Maximum routes per network + */ +#define ZTS_MAX_NETWORK_ROUTES 32 + +/** + * Maximum number of direct network paths to a given peer + */ +#define ZT_MAX_PEER_NETWORK_PATHS 16 + +/** + * What trust hierarchy role does this peer have? + */ +enum zts_peer_role +{ + ZTS_PEER_ROLE_LEAF = 0, // ordinary node + ZTS_PEER_ROLE_MOON = 1, // moon root + ZTS_PEER_ROLE_PLANET = 2 // planetary root +}; + +////////////////////////////////////////////////////////////////////////////// +// Structures used to convey details during various callback events // +////////////////////////////////////////////////////////////////////////////// + +/** + * A structure used to convey details about the current node + * to the user application + */ +struct zts_node_details +{ + /** + * The node ID + */ + uint64_t address; + + /** + * The current clock value accord to the node + */ + uint64_t clock; + + /** + * Whether or not this node is online + */ + uint8_t online; + + /** + * Whether port mapping is enabled + */ + uint8_t portMappingEnabled; + + /** + * Whether multipath support is enabled. If true, this node will + * be capable of utilizing multiple physical links simultaneously + * to create higher quality or more robust aggregate links. + * + * See: https://www.zerotier.com/manual.shtml#2_1_5 + */ + uint8_t multipathEnabled; + + /** + * The port used by the service to send and receive + * all encapsulated traffic + */ + uint16_t primaryPort; + + /** + * Planet ID + */ + uint64_t planetWorldId; + uint64_t planetWorldTimestamp; + uint8_t versionMajor; + uint8_t versionMinor; + uint8_t versionRev; +}; + +/** + * A structure used to convey information to a user application via + * a callback function. + */ +struct zts_callback_msg +{ + /** + * Event identifier + */ + int16_t eventCode; + + struct zts_node_details *node; + struct zts_network_details *network; + struct zts_netif_details *netif; + struct zts_virtual_network_route *route; + struct zts_physical_path *path; + struct zts_peer_details *peer; + struct zts_addr_details *addr; +}; + +struct zts_addr_details +{ + uint64_t nwid; + struct zts_sockaddr_storage addr; +}; + +/** + * A structure used to convey information about a virtual network + * interface (netif) to a user application. + */ +struct zts_netif_details +{ + /** + * The virtual network that this interface was commissioned for. + */ + uint64_t nwid; + + /** + * The hardware address assigned to this interface + */ + uint64_t mac; + + /** + * The MTU for this interface + */ + int mtu; + + /** + * The IPv4 address assigned to this interface. + */ + //struct sockaddr_in ip4_addr; + + /** + * The IPv6 addresses assigned to this interface. + */ + //struct sockaddr_in6 ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; + + /** + * Number of IPv4 addresses assigned to this interface + */ + //int num_ip4_addr; + + /** + * Number of IPv6 addresses assigned to this interface + */ + //int num_ip6_addr; +}; + +/** + * A structure used to represent a virtual network route + */ +struct zts_virtual_network_route +{ + /** + * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default + */ + struct zts_sockaddr_storage target; + + /** + * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) + */ + struct zts_sockaddr_storage via; + + /** + * Route flags + */ + uint16_t flags; + + /** + * Route metric (not currently used) + */ + uint16_t metric; +}; + +/** + * A structure used to convey network-specific details to the user application + */ +struct zts_network_details +{ + /** + * Network ID + */ + uint64_t nwid; + + /** + * Maximum Transmission Unit size for this network + */ + int mtu; + + /** + * Number of addresses (actually) assigned to the node on this network + */ + short num_addresses; + + /** + * Array of IPv4 and IPv6 addresses assigned to the node on this network + */ + struct zts_sockaddr_storage addr[ZTS_MAX_ASSIGNED_ADDRESSES]; + + /** + * Number of routes + */ + unsigned int num_routes; + + /** + * Array of IPv4 and IPv6 addresses assigned to the node on this network + */ + struct zts_virtual_network_route routes[ZTS_MAX_NETWORK_ROUTES]; +}; + +/** + * Physical network path to a peer + */ +struct zts_physical_path +{ + /** + * Address of endpoint + */ + struct zts_sockaddr_storage address; + + /** + * Time of last send in milliseconds or 0 for never + */ + uint64_t lastSend; + + /** + * Time of last receive in milliseconds or 0 for never + */ + uint64_t lastReceive; + + /** + * Is this a trusted path? If so this will be its nonzero ID. + */ + uint64_t trustedPathId; + + /** + * Is path expired? + */ + int expired; + + /** + * Is path preferred? + */ + int preferred; +}; + +/** + * Peer status result buffer + */ +struct zts_peer_details +{ + /** + * ZeroTier address (40 bits) + */ + uint64_t address; + + /** + * Remote major version or -1 if not known + */ + int versionMajor; + + /** + * Remote minor version or -1 if not known + */ + int versionMinor; + + /** + * Remote revision or -1 if not known + */ + int versionRev; + + /** + * Last measured latency in milliseconds or -1 if unknown + */ + int latency; + + /** + * What trust hierarchy role does this device have? + */ + enum zts_peer_role role; + + /** + * Number of paths (size of paths[]) + */ + unsigned int pathCount; + + /** + * Known network paths to peer + */ + struct zts_physical_path paths[ZT_MAX_PEER_NETWORK_PATHS]; +}; + +/** + * List of peers + */ +struct zts_peer_list +{ + struct zts_peer_details *peers; + unsigned long peerCount; +}; + +////////////////////////////////////////////////////////////////////////////// +// ZeroTier Service Controls // +////////////////////////////////////////////////////////////////////////////// + +#if defined(__WINDOWS__) + #ifdef ADD_EXPORTS + #define ZT_SOCKET_API __declspec(dllexport) + #else + #define ZT_SOCKET_API __declspec(dllimport) + #endif + #define ZTCALL __cdecl +#else + #define ZT_SOCKET_API + #define ZTCALL +#endif + +/** + * @brief Enable or disable whether the service will cache network details (enabled by default) + * + * This can potentially shorten (startup) times. This allows the service to nearly instantly + * inform the network stack of an address to use for this peer so that it can + * create an interface. This can be disabled for cases where one may not want network + * config details to be written to storage. This is especially useful for situations where + * address assignments do not change often. + * + * @usage Should be called before zts_start() if you intend on changing its state. + * + * @param enabled Whether or not this feature is enabled + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_allow_network_caching(uint8_t allowed); + +/** + * @brief Enable or disable whether the service will cache peer details (enabled by default) + * + * This can potentially shorten (connection) times. This allows the service to + * re-use previously discovered paths to a peer, this prevents the service from having + * to go through the entire transport-triggered link provisioning process. This is especially + * useful for situations where paths to peers do not change often. This is enabled by default + * and can be disabled for cases where one may not want peer details to be written to storage. + * + * @usage Should be called before zts_start() if you intend on changing its state. + * + * @param enabled Whether or not this feature is enabled + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_allow_peer_caching(uint8_t allowed); + +/** + * @brief Enable or disable whether the service will read from a local.conf + * + * @usage Should be called before zts_start() if you intend on changing its state. + * + * @param enabled Whether or not this feature is enabled + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_allow_local_conf(uint8_t allowed); + +/** + * @brief Starts the ZeroTier service and notifies user application of events via callback + * + * @param path path directory where configuration files are stored + * @param callback User-specified callback for ZTS_EVENT_* events + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure + */ +ZT_SOCKET_API int ZTCALL zts_start(const char *path, void (*callback)(void *), uint16_t port); + +/** + * @brief Stops the ZeroTier service and brings down all virtual network interfaces + * + * @usage While the ZeroTier service will stop, the stack driver (with associated timers) + * will remain active in case future traffic processing is required. To stop all activity + * and free all resources use zts_free() instead. + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_stop(); + +/** + * @brief Restart the ZeroTier service. + * + * @usage This call will block until the service has been brought offline. Then + * it will return and the user application can then watch for the appropriate + * startup callback events. + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_restart(); + +/** + * @brief Stop all background services, bring down all interfaces, free all resources. After + * calling this function an application restart will be required before the library can be + * used again. + * + * @usage This should be called at the end of your program or when you do not anticipate + * communicating over ZeroTier + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_free(); + +/** + * @brief Populate a structure with details for a given network + * + * @param nwid A 16-digit hexadecimal virtual network ID + * @param nd Pointer to a zts_network_details structure to populate + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_network_details(uint64_t nwid, struct zts_network_details *nd); + +/** + * @brief Populate an array of structures with details for any given number of networks + * + * @param nds Pointer to an array of zts_network_details structures to populate + * @param num Number of zts_network_details structures available to copy data into, will be updated + * to reflect number of structures that were actually populated + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_all_network_details(struct zts_network_details *nds, int *num); + +/** + * @brief Join a network + * + * @param nwid A 16-digit hexadecimal virtual network ID + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_join(const uint64_t nwid); + +/** + * @brief Leave a network + * + * @param nwid A 16-digit hexadecimal virtual network ID + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_leave(const uint64_t nwid); + +/** + * @brief Leave all networks + * + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_leave_all(); + +/** + * @brief Orbit a given moon (user-defined root server) + * + * @param moonWorldId A 16-digit hexadecimal world ID + * @param moonSeed A 16-digit hexadecimal seed ID + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed); + +/** + * @brief De-orbit a given moon (user-defined root server) + * + * @param moonWorldId A 16-digit world ID + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_deorbit(uint64_t moonWorldId); + +/** + * @brief Return the node ID of this instance + * + * @return 64-bit node ID + */ +ZT_SOCKET_API uint64_t ZTCALL zts_get_node_id(); + +/** + * @brief Compute a 6PLANE IPv6 address for the given Network ID and Node ID + * + * @param addr Destination structure for address + * @param nwid Network ID + * @param nodeId Node ID + * @return ZTS_ERR_OK on success. ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_6plane_addr( + struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); + +/** + * @brief Compute a RFC4193 IPv6 address for the given Network ID and Node ID + * + * @param addr Destination structure for address + * @param nwid Network ID + * @param nodeId Node ID + * @return ZTS_ERR_OK on success. ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( + struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); + +/** + * @brief Compute a RFC4193 IPv6 address for the given Network ID and Node ID + * + * 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. + * + * + * @param startPortOfRange Start of port allowed port range + * @param endPortOfRange End of allowed port range + * @return An Ad-hoc network ID + */ +ZT_SOCKET_API uint64_t ZTCALL zts_generate_adhoc_nwid_from_range( + uint16_t startPortOfRange, uint16_t endPortOfRange); + +/** + * @brief Return details of all peers + * + * @param pds Pointer to array of zts_peer_details structs to be filled out + * @param num Length of destination array, will be filled out with actual number + * of peers that details were available for. + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_peers(struct zts_peer_details *pds, uint32_t *num); + +/** + * @brief Return details of a given peer. + * + * @param pds Pointer to zts_peer_details struct to be filled out + * @param peerId ID of peer that the caller wants details of + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_peer(struct zts_peer_details *pds, uint64_t peerId); + +#define ZTS_STATE_NODE_OFFLINE ZTS_EVENT_NODE_OFFLINE +#define ZTS_STATE_NODE_ONLINE ZTS_EVENT_NODE_ONLINE + +/** + * @brief Return the state of this node + * + * @return ZTS_STATE_NODE_ONLINE, ZTS_STATE_NODE_OFFLINE + */ +ZT_SOCKET_API int ZTCALL zts_get_node_status(); + +#define ZTS_STATE_NETWORK_NOT_FOUND ZTS_EVENT_NETWORK_NOT_FOUND +#define ZTS_STATE_NETWORK_CLIENT_TOO_OLD ZTS_EVENT_NETWORK_CLIENT_TOO_OLD +#define ZTS_STATE_NETWORK_REQUESTING_CONFIG ZTS_EVENT_NETWORK_REQUESTING_CONFIG +#define ZTS_STATE_NETWORK_OK ZTS_EVENT_NETWORK_OK +#define ZTS_STATE_NETWORK_ACCESS_DENIED ZTS_EVENT_NETWORK_ACCESS_DENIED +#define ZTS_STATE_NETWORK_DOWN ZTS_EVENT_NETWORK_DOWN + +/** + * @brief Return the state of a given network + * + * @param nwid Network ID + * @return ZTS_STATE_NETWORK_OK, ZTS_STATE_NETWORK_REQUESTING_CONFIG, etc + */ +ZT_SOCKET_API int ZTCALL zts_get_network_status(uint64_t nwid); + +#define ZTS_STATE_PEER_DIRECT ZTS_EVENT_PEER_DIRECT +#define ZTS_STATE_PEER_RELAY ZTS_EVENT_PEER_RELAY +#define ZTS_STATE_PEER_UNREACHABLE ZTS_EVENT_PEER_UNREACHABLE + +/** + * @brief Return the reachability state of a given remote peer + * + * @param peerId Remote peer ID + * @return ZTS_STATE_PEER_DIRECT, ZTS_STATE_PEER_RELAY, or ZTS_STATE_PEER_UNREACHABLE + */ +ZT_SOCKET_API int ZTCALL zts_get_peer_status(uint64_t peerId); + +/** + * @brief Platform-agnostic delay (provided for convenience) + * + * @param interval_ms Number of milliseconds to delay + */ +ZT_SOCKET_API void ZTCALL zts_delay_ms(long interval_ms); + +////////////////////////////////////////////////////////////////////////////// +// Statistics // +////////////////////////////////////////////////////////////////////////////// + +#define ZTS_STATS_PROTOCOL_LINK 0 +#define ZTS_STATS_PROTOCOL_ETHARP 1 +#define ZTS_STATS_PROTOCOL_IP 2 +#define ZTS_STATS_PROTOCOL_UDP 3 +#define ZTS_STATS_PROTOCOL_TCP 4 +#define ZTS_STATS_PROTOCOL_ICMP 5 +#define ZTS_STATS_PROTOCOL_IP_FRAG 6 +#define ZTS_STATS_PROTOCOL_IP6 7 +#define ZTS_STATS_PROTOCOL_ICMP6 8 +#define ZTS_STATS_PROTOCOL_IP6_FRAG 9 + +/** Protocol related stats */ +struct zts_stats_proto { + uint32_t xmit; /* Transmitted packets. */ + uint32_t recv; /* Received packets. */ + uint32_t fw; /* Forwarded packets. */ + uint32_t drop; /* Dropped packets. */ + uint32_t chkerr; /* Checksum error. */ + uint32_t lenerr; /* Invalid length error. */ + uint32_t memerr; /* Out of memory error. */ + uint32_t rterr; /* Routing error. */ + uint32_t proterr; /* Protocol error. */ + uint32_t opterr; /* Error in options. */ + uint32_t err; /* Misc error. */ + uint32_t cachehit; +}; + +/** IGMP stats */ +struct zts_stats_igmp { + uint32_t xmit; /* Transmitted packets. */ + uint32_t recv; /* Received packets. */ + uint32_t drop; /* Dropped packets. */ + uint32_t chkerr; /* Checksum error. */ + uint32_t lenerr; /* Invalid length error. */ + uint32_t memerr; /* Out of memory error. */ + uint32_t proterr; /* Protocol error. */ + uint32_t rx_v1; /* Received v1 frames. */ + uint32_t rx_group; /* Received group-specific queries. */ + uint32_t rx_general; /* Received general queries. */ + uint32_t rx_report; /* Received reports. */ + uint32_t tx_join; /* Sent joins. */ + uint32_t tx_leave; /* Sent leaves. */ + uint32_t tx_report; /* Sent reports. */ +}; + +/** System element stats */ +struct zts_stats_syselem { + uint32_t used; + uint32_t max; + uint32_t err; +}; + +/** System stats */ +struct zts_stats_sys { + struct zts_stats_syselem sem; + struct zts_stats_syselem mutex; + struct zts_stats_syselem mbox; +}; + +/** lwIP stats container */ +struct zts_stats { + /** Link level */ + struct zts_stats_proto link; + /** ARP */ + struct zts_stats_proto etharp; + /** Fragmentation */ + struct zts_stats_proto ip_frag; + /** IP */ + struct zts_stats_proto ip; + /** ICMP */ + struct zts_stats_proto icmp; + /** IGMP */ + struct zts_stats_igmp igmp; + /** UDP */ + struct zts_stats_proto udp; + /** TCP */ + struct zts_stats_proto tcp; + /** System */ + struct zts_stats_sys sys; + /** IPv6 */ + struct zts_stats_proto ip6; + /** ICMP6 */ + struct zts_stats_proto icmp6; + /** IPv6 fragmentation */ + struct zts_stats_proto ip6_frag; + /** Multicast listener discovery */ + struct zts_stats_igmp mld6; + /** Neighbor discovery */ + struct zts_stats_proto nd6; +}; + +/** + * @brief Return all statistical counters for all protocols (inefficient) + * + * @usage This function can only be used in debug builds. + * @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_all_stats(struct zts_stats *statsDest); + +/** + * @brief Populate the given structure with the requested protocol's + * statistical counters (from network stack) + * + * @usage This function can only be used in debug builds. + * @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure. + */ +ZT_SOCKET_API int ZTCALL zts_get_protocol_stats(int protocolType, void *protoStatsDest); + +////////////////////////////////////////////////////////////////////////////// +// Socket API // +////////////////////////////////////////////////////////////////////////////// + +/** + * @brief Create a socket (sets zts_errno) + * + * @param socket_family Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param socket_type Type of socket (ZTS_SOCK_STREAM, ZTS_SOCK_DGRAM, ZTS_SOCK_RAW) + * @param protocol Protocols supported on this socket + * @return Numbered file descriptor on success. ZTS_ERR_SERVICE or ZTS_ERR_SOCKET on failure. + */ +ZT_SOCKET_API int ZTCALL zts_socket( + const int socket_family, const int socket_type, const int protocol); + +/** + * @brief Connect a socket to a remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Remote host address to connect to + * @param addrlen Length of address + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_connect( + int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen); + +/** + * @brief Bind a socket to a virtual interface (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Local interface address to bind to + * @param addrlen Length of address + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_bind( + int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen); + +/** + * @brief Listen for incoming connections on socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @param backlog Number of backlogged connections allowed + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_listen(int fd, int backlog); + +/** + * @brief Accept an incoming connection (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Address of remote host for accepted connection + * @param addrlen Length of address + * @return New socket file descriptor on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_accept(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); + +/** + * @brief Accept an incoming connection (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Address of remote host for accepted connection + * @param addrlen Length of address + * @param flags + * @return New socket file descriptor on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +#if defined(__linux__) + int zts_accept4(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen, int flags); +#endif + +// Socket level option number +#define ZTS_SOL_SOCKET 0x0fff +// Socket options +#define ZTS_SO_DEBUG 0x0001 // NOT YET SUPPORTED +#define ZTS_SO_ACCEPTCONN 0x0002 +#define ZTS_SO_REUSEADDR 0x0004 +#define ZTS_SO_KEEPALIVE 0x0008 +#define ZTS_SO_DONTROUTE 0x0010 // NOT YET SUPPORTED +#define ZTS_SO_BROADCAST 0x0020 +#define ZTS_SO_USELOOPBACK 0x0040 // NOT YET SUPPORTED +#define ZTS_SO_LINGER 0x0080 + +/* + * Structure used for manipulating linger option. + */ +struct zts_linger { + int l_onoff; // option on/off + int l_linger; // linger time in seconds +}; + +#define ZTS_SO_DONTLINGER ((int)(~ZTS_SO_LINGER)) +#define ZTS_SO_OOBINLINE 0x0100 // NOT YET SUPPORTED +#define ZTS_SO_REUSEPORT 0x0200 // NOT YET SUPPORTED +#define ZTS_SO_SNDBUF 0x1001 // NOT YET SUPPORTED +#define ZTS_SO_RCVBUF 0x1002 +#define ZTS_SO_SNDLOWAT 0x1003 // NOT YET SUPPORTED +#define ZTS_SO_RCVLOWAT 0x1004 // NOT YET SUPPORTED +#define ZTS_SO_SNDTIMEO 0x1005 +#define ZTS_SO_RCVTIMEO 0x1006 +#define ZTS_SO_ERROR 0x1007 +#define ZTS_SO_TYPE 0x1008 +#define ZTS_SO_CONTIMEO 0x1009 +#define ZTS_SO_NO_CHECK 0x100a +#define ZTS_SO_BINDTODEVICE 0x100b +// IPPROTO_IP options +#define ZTS_IP_TOS 0x0001 +#define ZTS_IP_TTL 0x0002 +#define ZTS_IP_PKTINFO 0x0008 +// IPPROTO_TCP options +#define ZTS_TCP_NODELAY 0x0001 +#define ZTS_TCP_KEEPALIVE 0x0002 +#define ZTS_TCP_KEEPIDLE 0x0003 +#define ZTS_TCP_KEEPINTVL 0x0004 +#define ZTS_TCP_KEEPCNT 0x0005 +// IPPROTO_IPV6 options +#define ZTS_IPV6_CHECKSUM 0x0007 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ +#define ZTS_IPV6_V6ONLY 0x001b /* RFC3493: boolean control to restrict ZTS_AF_INET6 sockets to IPv6 communications only. */ +// UDPLITE options +#define ZTS_UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */ +#define ZTS_UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */ +// UDPLITE options +#define ZTS_IP_MULTICAST_TTL 5 +#define ZTS_IP_MULTICAST_IF 6 +#define ZTS_IP_MULTICAST_LOOP 7 + +// Multicast options +#define ZTS_IP_ADD_MEMBERSHIP 3 +#define ZTS_IP_DROP_MEMBERSHIP 4 + +typedef struct zts_ip_mreq { + struct zts_in_addr imr_multiaddr; /* IP multicast address of group */ + struct zts_in_addr imr_interface; /* local IP address of interface */ +} zts_ip_mreq; + +struct zts_in_pktinfo { + unsigned int ipi_ifindex; /* Interface index */ + struct zts_in_addr ipi_addr; /* Destination (from header) address */ +}; + +#define ZTS_IPV6_JOIN_GROUP 12 +#define ZTS_IPV6_ADD_MEMBERSHIP ZTS_IPV6_JOIN_GROUP +#define ZTS_IPV6_LEAVE_GROUP 13 +#define ZTS_IPV6_DROP_MEMBERSHIP ZTS_IPV6_LEAVE_GROUP + +typedef struct zts_ipv6_mreq { + struct zts_in6_addr ipv6mr_multiaddr; /* IPv6 multicast addr */ + unsigned int ipv6mr_interface; /* interface index, or 0 */ +} zts_ipv6_mreq; + +/* + * The Type of Service provides an indication of the abstract + * parameters of the quality of service desired. These parameters are + * to be used to guide the selection of the actual service parameters + * when transmitting a datagram through a particular network. Several + * networks offer service precedence, which somehow treats high + * precedence traffic as more important than other traffic (generally + * by accepting only traffic above a certain precedence at time of high + * load). The major choice is a three way tradeoff between low-delay, + * high-reliability, and high-throughput. + * The use of the Delay, Throughput, and Reliability indications may + * increase the cost (in some sense) of the service. In many networks + * better performance for one of these parameters is coupled with worse + * performance on another. Except for very unusual cases at most two + * of these three indications should be set. + */ +#define ZTS_IPTOS_TOS_MASK 0x1E +#define ZTS_IPTOS_TOS(tos) ((tos) & ZTS_IPTOS_TOS_MASK) +#define ZTS_IPTOS_LOWDELAY 0x10 +#define ZTS_IPTOS_THROUGHPUT 0x08 +#define ZTS_IPTOS_RELIABILITY 0x04 +#define ZTS_IPTOS_LOWCOST 0x02 +#define ZTS_IPTOS_MINCOST ZTS_IPTOS_LOWCOST + +/* + * The Network Control precedence designation is intended to be used + * within a network only. The actual use and control of that + * designation is up to each network. The Internetwork Control + * designation is intended for use by gateway control originators only. + * If the actual use of these precedence designations is of concern to + * a particular network, it is the responsibility of that network to + * control the access to, and use of, those precedence designations. + */ +#define ZTS_IPTOS_PREC_MASK 0xe0 +#define ZTS_IPTOS_PREC(tos) ((tos) & ZTS_IPTOS_PREC_MASK) +#define ZTS_IPTOS_PREC_NETCONTROL 0xe0 +#define ZTS_IPTOS_PREC_INTERNETCONTROL 0xc0 +#define ZTS_IPTOS_PREC_CRITIC_ECP 0xa0 +#define ZTS_IPTOS_PREC_FLASHOVERRIDE 0x80 +#define ZTS_IPTOS_PREC_FLASH 0x60 +#define ZTS_IPTOS_PREC_IMMEDIATE 0x40 +#define ZTS_IPTOS_PREC_PRIORITY 0x20 +#define ZTS_IPTOS_PREC_ROUTINE 0x00 + +/** + * @brief Set socket options (sets zts_errno) + * + * @param fd Socket file descriptor + * @param level Protocol level to which option name should apply + * @param optname Option name to set + * @param optval Source of option value to set + * @param optlen Length of option value + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_setsockopt( + int fd, int level, int optname, const void *optval, zts_socklen_t optlen); + +/** + * @brief Get socket options (sets zts_errno) + * + * @param fd Socket file descriptor + * @param level Protocol level to which option name should apply + * @param optname Option name to get + * @param optval Where option value will be stored + * @param optlen Length of value + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_getsockopt( + int fd, int level, int optname, void *optval, zts_socklen_t *optlen); + +/** + * @brief Get socket name (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Name associated with this socket + * @param addrlen Length of name + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_getsockname(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); + +/** + * @brief Get the peer name for the remote end of a connected socket + * + * @param fd Socket file descriptor + * @param addr Name associated with remote end of this socket + * @param addrlen Length of name + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_getpeername(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); + +/** + * @brief Close a socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_close(int fd); + +/* FD_SET used for lwip_select */ + +#define MEMP_NUM_NETCONN 1024 + +#ifndef ZTS_FD_SET +#undef ZTS_FD_SETSIZE +// Make FD_SETSIZE match NUM_SOCKETS in socket.c +#define ZTS_FD_SETSIZE MEMP_NUM_NETCONN +#define ZTS_FDSETSAFESET(n, code) do { \ + if (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0)) { \ + code; }} while(0) +#define ZTS_FDSETSAFEGET(n, code) \ + (((n) - LWIP_SOCKET_OFFSET < MEMP_NUM_NETCONN) && (((int)(n) - LWIP_SOCKET_OFFSET) >= 0) ? \ + (code) : 0) +#define ZTS_FD_SET(n, p) \ + ZTS_FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] |= (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define ZTS_FD_CLR(n, p) \ + ZTS_FDSETSAFESET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] &= ~(1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define ZTS_FD_ISSET(n,p) \ + ZTS_FDSETSAFEGET(n, (p)->fd_bits[((n)-LWIP_SOCKET_OFFSET)/8] & (1 << (((n)-LWIP_SOCKET_OFFSET) & 7))) +#define ZTS_FD_ZERO(p) memset((void*)(p), 0, sizeof(*(p))) + +#elif LWIP_SOCKET_OFFSET +#error LWIP_SOCKET_OFFSET does not work with external FD_SET! +#elif ZTS_FD_SETSIZE < MEMP_NUM_NETCONN +#error "external ZTS_FD_SETSIZE too small for number of sockets" +#endif // FD_SET + +typedef struct zts_fd_set +{ + unsigned char fd_bits [(ZTS_FD_SETSIZE+7)/8]; +} zts_fd_set; + +struct zts_timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +/** + * @brief Monitor multiple file descriptors for "readiness" (sets zts_errno) + * + * @param nfds Set to the highest numbered file descriptor in any of the given sets + * @param readfds Set of file descriptors to monitor for READ readiness + * @param writefds Set of file descriptors to monitor for WRITE readiness + * @param exceptfds Set of file descriptors to monitor for exceptional conditions + * @param timeout How long this call should block + * @return Number of ready file descriptors on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_select( + int nfds, zts_fd_set *readfds, zts_fd_set *writefds, zts_fd_set *exceptfds, struct zts_timeval *timeout); + +// fnctl() commands +#define ZTS_F_GETFL 0x0003 +#define ZTS_F_SETFL 0x0004 +/* File status flags and file access modes for fnctl, + these are bits in an int. */ +#define ZTS_O_NONBLOCK 1 +#define ZTS_O_NDELAY ZTS_O_NONBLOCK +#define ZTS_O_RDONLY 2 +#define ZTS_O_WRONLY 4 +#define ZTS_O_RDWR (ZTS_O_RDONLY|ZTS_O_WRONLY) + +/** + * @brief Issue file control commands on a socket + * + * @param fd File descriptor + * @param cmd + * @param flags + * @return + */ +ZT_SOCKET_API int ZTCALL zts_fcntl(int fd, int cmd, int flags); + +#define ZTS_POLLIN 0x001 +#define ZTS_POLLOUT 0x002 +#define ZTS_POLLERR 0x004 +#define ZTS_POLLNVAL 0x008 +/* Below values are unimplemented */ +#define ZTS_POLLRDNORM 0x010 +#define ZTS_POLLRDBAND 0x020 +#define ZTS_POLLPRI 0x040 +#define ZTS_POLLWRNORM 0x080 +#define ZTS_POLLWRBAND 0x100 +#define ZTS_POLLHUP 0x200 + +typedef unsigned int zts_nfds_t; + +struct zts_pollfd +{ + int fd; + short events; + short revents; +}; + +/** + * @brief Wait for some event on a file descriptor. (sets zts_errno) + * + * @param fds Set of file descriptors to monitor + * @param nfds Number of elements in the fds array + * @param timeout How long this call should block + * @return Number of ready file descriptors on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_poll(struct zts_pollfd *fds, zts_nfds_t nfds, int timeout); + +/** + * @brief Control a device (sets zts_errno) + * + * @param fd Socket file descriptor + * @param request + * @param argp + * @return ZTS_ERR_OK on success, ZTS_ERR_SERVICE on failure. + */ +ZT_SOCKET_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); + +/** + * @brief Send data to remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of data to write + * @param flags + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int flags); + +/** + * @brief Send data to remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of data to write + * @param flags + * @param addr Destination address + * @param addrlen Length of destination address + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_sendto( + int fd, const void *buf, size_t len, int flags, const struct zts_sockaddr *addr, zts_socklen_t addrlen); + +struct zts_iovec { + void *iov_base; + size_t iov_len; +}; + +/* */ +struct zts_msghdr { + void *msg_name; + zts_socklen_t msg_namelen; + struct zts_iovec *msg_iov; + int msg_iovlen; + void *msg_control; + zts_socklen_t msg_controllen; + int msg_flags; +}; + +/* struct msghdr->msg_flags bit field values */ +#define ZTS_MSG_TRUNC 0x04 +#define ZTS_MSG_CTRUNC 0x08 + +/** + * @brief Send message to remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param msg + * @param flags + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_sendmsg(int fd, const struct msghdr *msg, int flags); + +/** + * @brief Receive data from remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of data buffer + * @param flags + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_recv(int fd, void *buf, size_t len, int flags); + +/** + * @brief Receive data from remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of data buffer + * @param flags + * @param addr + * @param addrlen + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_recvfrom( + int fd, void *buf, size_t len, int flags, struct zts_sockaddr *addr, zts_socklen_t *addrlen); + +/** + * @brief Receive a message from remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param msg + * @param flags + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_recvmsg(int fd, struct msghdr *msg,int flags); + +/** + * @brief Read bytes from socket onto buffer (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of data buffer to receive data + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_read(int fd, void *buf, size_t len); + +/** + * @brief Read bytes from socket into multiple buffers (sets zts_errno) + * + * @param fd Socket file descriptor + * @param iov Array of destination buffers + * @param iovcnt Number of buffers to read into + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_readv(int fd, const struct zts_iovec *iov, int iovcnt); + +/** + * @brief Write bytes from buffer to socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param len Length of buffer to write + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_write(int fd, const void *buf, size_t len); + +/** + * @brief Write data from multiple buffers to socket. (sets zts_errno) + * + * @param fd Socket file descriptor + * @param iov Array of source buffers + * @param iovcnt Number of buffers to read from + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec *iov, int iovcnt); + +#define ZTS_SHUT_RD 0x0 +#define ZTS_SHUT_WR 0x1 +#define ZTS_SHUT_RDWR 0x2 + +/** + * @brief Shut down some aspect of a socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @param how Which aspects of the socket should be shut down + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ +ZT_SOCKET_API int ZTCALL zts_shutdown(int fd, int how); + +/** + * @brief Add a DNS nameserver for the network stack to use + * + * @param addr Address for DNS nameserver + * @return ZTS_ERR_SERVICE + */ +ZT_SOCKET_API int ZTCALL zts_add_dns_nameserver(struct zts_sockaddr *addr); + +/** + * @brief Remove a DNS nameserver + * + * @param addr Address for DNS nameserver + * @return ZTS_ERR_SERVICE + */ +ZT_SOCKET_API int ZTCALL zts_del_dns_nameserver(struct zts_sockaddr *addr); + +/** + * Convert an uint16_t from host to network byte order. + * + * @param n uint16_t in host byte order + * @return n in network byte order + */ +ZT_SOCKET_API uint16_t ZTCALL zts_htons(uint16_t n); + +/** + * Convert an uint32_t from host to network byte order. + * + * @param n uint32_t in host byte order + * @return n in network byte order + */ +ZT_SOCKET_API uint32_t ZTCALL zts_htonl(uint32_t n); + +/** + * Length of human-readable MAC address string + */ +#define ZTS_MAC_ADDRSTRLEN 18 + +#ifndef htonll +#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) +#endif + +/** + * Convert an uint16_t from network to host byte order. + * + * @param n uint16_t in network byte order + * @return n in host byte order + */ +ZT_SOCKET_API uint16_t ZTCALL zts_ntohs(uint16_t n); + +/** + * Convert an uint32_t from network to host byte order. + * + * @param n uint32_t in network byte order + * @return n in host byte order + */ +ZT_SOCKET_API uint32_t ZTCALL zts_ntohl(uint32_t n); + +/** + * Convert IPv4 and IPv6 address structures to human-readable text form. + * + * @param af Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param src Pointer to source address structure + * @param dst Pointer to destination character array + * @param size Size of the destination buffer + * @return On success, returns a non-null pointer to the destination character array + */ +ZT_SOCKET_API const char * ZTCALL zts_inet_ntop(int af, const void *src, char *dst, zts_socklen_t size); + +/** + * Convert C-string IPv4 and IPv6 addresses to binary form. + * + * @param af Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param src Pointer to source character array + * @param dst Pointer to destination address structure + * @return return 1 on success. 0 or -1 on failure. (Does not follow zts_* conventions) + */ +ZT_SOCKET_API int ZTCALL zts_inet_pton(int af, const char *src, void *dst); + +/** + * Convert a C-string IPv4 address to integer representation. + * + * @param cp Human-readable IPv4 string + * @return 32-bit integer representation of address + */ +ZT_SOCKET_API uint32_t ZTCALL zts_inet_addr(const char *cp); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _H diff --git a/src/Controls.cpp b/src/Controls.cpp index d2c00f5..b8ea3cc 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -14,569 +14,129 @@ /** * @file * - * ZeroTier service controls + * Network control interface */ -#if defined(__linux__) -#include -#endif +#include +#include #include "Node.hpp" -#include "ZeroTierOne.h" +#include "Mutex.hpp" #include "OSUtils.hpp" -#include "Service.hpp" -#include "VirtualTap.hpp" #include "Debug.hpp" -#include "concurrentqueue.h" -#include "ZeroTier.h" -#include "lwipDriver.hpp" +#include "NodeService.hpp" +#include "VirtualTap.hpp" +#include "Events.hpp" +#include "ZeroTierSockets.h" -#if defined(_WIN32) -WSADATA wsaData; -#include -#endif +using namespace ZeroTier; #ifdef SDK_JNI -#include + #include #endif -namespace ZeroTier { - -#ifdef __cplusplus -extern "C" { -#endif -// Custom errno to prevent conflicts with platform's own errno -int zts_errno; -#ifdef __cplusplus -} -#endif - -struct serviceParameters +namespace ZeroTier { - int port; - std::string path; -}; - -int _port; -std::string _path; - -/* - * A lock used to protect any call which relies on the presence of a valid - * pointer to the ZeroTier service. - */ -Mutex _service_lock; - -/* - * A lock used to protect callback method pointers. With a coarser-grained - * lock it would be possible for one thread to alter the callback method - * pointer causing undefined behaviour. - */ -Mutex _callback_lock; - -bool _freeHasBeenCalled = false; -bool _run_service = false; -bool _run_callbacks = false; -bool _run_lwip_tcpip = false; - -bool _network_caching_enabled = true; -bool _peer_caching_enabled = true; - -//bool _startupError = false; - -// Global reference to ZeroTier service -OneService *service; - -// User-provided callback for ZeroTier events -#ifdef SDK_JNI - // Global references to JNI objects and VM kept for future callbacks - static JavaVM *jvm = NULL; - static jobject objRef = NULL; - static jmethodID _userCallbackMethodRef = NULL; -#endif - -void (*_userEventCallbackFunc)(struct zts_callback_msg *); - -moodycamel::ConcurrentQueue _callbackMsgQueue; - -////////////////////////////////////////////////////////////////////////////// -// Internal ZeroTier Service Controls (user application shall not use these)// -////////////////////////////////////////////////////////////////////////////// - -void postEvent(int eventCode, void *arg) -{ - struct zts_callback_msg *msg = new zts_callback_msg(); - - msg->node = NULL; - msg->network = NULL; - msg->netif = NULL; - msg->route = NULL; - msg->path = NULL; - msg->peer = NULL; - msg->addr = NULL; - - msg->eventCode = eventCode; - - if (NODE_EVENT_TYPE(eventCode)) { - msg->node = (struct zts_node_details*)arg; - } if (NETWORK_EVENT_TYPE(eventCode)) { - msg->network = (struct zts_network_details*)arg; - } if (NETIF_EVENT_TYPE(eventCode)) { - msg->netif = (struct zts_netif_details*)arg; - } if (ROUTE_EVENT_TYPE(eventCode)) { - msg->route = (struct zts_virtual_network_route*)arg; - } if (PATH_EVENT_TYPE(eventCode)) { - msg->path = (struct zts_physical_path*)arg; - } if (PEER_EVENT_TYPE(eventCode)) { - msg->peer = (struct zts_peer_details*)arg; - } if (ADDR_EVENT_TYPE(eventCode)) { - msg->addr = (struct zts_addr_details*)arg; - } - - _callbackMsgQueue.enqueue(msg); -} - -void postEvent(int eventCode) { - postEvent(eventCode, (void*)0); -} - -void freeEvent(struct zts_callback_msg *msg) -{ - if (!msg) { - return; - } - if (msg->node) { delete msg->node; } - if (msg->network) { delete msg->network; } - if (msg->netif) { delete msg->netif; } - if (msg->route) { delete msg->route; } - if (msg->path) { delete msg->path; } - if (msg->peer) { delete msg->peer; } - if (msg->addr) { delete msg->addr; } -} - -void _process_callback_event_helper(struct zts_callback_msg *msg) -{ -#ifdef SDK_JNI -/* Old style callback messages are simply a uint64_t with a network/peer/node -if of some sort and an associated message code id. This is deprecated and here -only for legacy reasons. */ -#if 1 - if(_userCallbackMethodRef) { - JNIEnv *env; -#if defined(__ANDROID__) - jint rs = jvm->AttachCurrentThread(&env, NULL); -#else - jint rs = jvm->AttachCurrentThread((void **)&env, NULL); -#endif - assert (rs == JNI_OK); - uint64_t arg = 0; - uint64_t id = 0; - if (NODE_EVENT_TYPE(msg->eventCode)) { - id = msg->node ? msg->node->address : 0; - } - if (NETWORK_EVENT_TYPE(msg->eventCode)) { - id = msg->network ? msg->network->nwid : 0; - } - if (PEER_EVENT_TYPE(msg->eventCode)) { - id = msg->peer ? msg->peer->address : 0; - } - env->CallVoidMethod(objRef, _userCallbackMethodRef, id, msg->eventCode); - freeEvent(msg); - } -#else - if(_userCallbackMethodRef) { - JNIEnv *env; - jint rs = jvm->AttachCurrentThread(&env, NULL); - assert (rs == JNI_OK); - uint64_t arg = 0; - if (NODE_EVENT_TYPE(msg->eventCode)) { - DEBUG_INFO("NODE_EVENT_TYPE(%d)", msg->eventCode); - arg = msg->node->address; - } - if (NETWORK_EVENT_TYPE(msg->eventCode)) { - DEBUG_INFO("NETWORK_EVENT_TYPE(%d)", msg->eventCode); - arg = msg->network->nwid; - } - if (PEER_EVENT_TYPE(msg->eventCode)) { - DEBUG_INFO("PEER_EVENT_TYPE(%d)", msg->eventCode); - arg = msg->peer->address; - } - env->CallVoidMethod(objRef, _userCallbackMethodRef, arg, msg->eventCode); - freeEvent(msg); -#endif -#else - if (_userEventCallbackFunc) { - _userEventCallbackFunc(msg); - freeEvent(msg); - } -#endif -} - -void _process_callback_event(struct zts_callback_msg *msg) -{ - _callback_lock.lock(); - _process_callback_event_helper(msg); - _callback_lock.unlock(); -} - -bool _is_callback_registered() -{ - _callback_lock.lock(); - bool retval = false; -#ifdef SDK_JNI - retval = (jvm && objRef && _userCallbackMethodRef); -#else - retval = _userEventCallbackFunc; -#endif - _callback_lock.unlock(); - return retval; -} - -void _clear_registered_callback() -{ - _callback_lock.lock(); -#ifdef SDK_JNI - objRef = NULL; - _userCallbackMethodRef = NULL; -#else - _userEventCallbackFunc = NULL; -#endif - _callback_lock.unlock(); -} - -int _zts_node_online() -{ - return service && service->getNode() && service->getNode()->online(); -} - -int _zts_can_perform_service_operation() -{ - return service - && service->isRunning() - && service->getNode() - && service->getNode()->online() - && !_freeHasBeenCalled; -} - -void _api_sleep(int interval_ms) -{ -#if defined (_WIN32) - Sleep(interval_ms); -#else - struct timespec sleepValue = {0}; - sleepValue.tv_nsec = interval_ms * 500000; - nanosleep(&sleepValue, NULL); -#endif -} - -int _change_nice(int increment) -{ -#ifndef _WIN32 - if (increment == 0) { - return 0; - } - int priority = getpriority(PRIO_PROCESS, 0); - return setpriority(PRIO_PROCESS, 0, priority+increment); -#endif - return 0; -} - -////////////////////////////////////////////////////////////////////////////// -// Callback thread // -////////////////////////////////////////////////////////////////////////////// - -#if defined(_WIN32) -DWORD WINAPI _zts_run_callbacks(LPVOID thread_id) -#else -void *_zts_run_callbacks(void *thread_id) -#endif -{ - _change_nice(CALLBACK_THREAD_NICENESS); -#if defined(__APPLE__) - pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME); -#endif - while (_run_callbacks || _callbackMsgQueue.size_approx() > 0) - { - struct zts_callback_msg *msg; - size_t sz = _callbackMsgQueue.size_approx(); - for (size_t j = 0; j < sz; j++) { - if (_callbackMsgQueue.try_dequeue(msg)) { - _process_callback_event(msg); - delete msg; - } - } - _api_sleep(ZTS_CALLBACK_PROCESSING_INTERVAL); - } -#if SDK_JNI - JNIEnv *env; - jint rs = jvm->DetachCurrentThread(); - pthread_exit(0); -#endif - return NULL; -} - -////////////////////////////////////////////////////////////////////////////// -// Service thread // -////////////////////////////////////////////////////////////////////////////// - -// Starts a ZeroTier service in the background -#if defined(_WIN32) -DWORD WINAPI _zts_run_service(LPVOID arg) -#else -void *_zts_run_service(void *arg) -#endif -{ -#if defined(__APPLE__) - pthread_setname_np(ZTS_SERVICE_THREAD_NAME); -#endif - //struct serviceParameters *params = arg; - //DEBUG_INFO("path=%s", params->path.c_str()); - int err; - - _change_nice(SERVICE_THREAD_NICENESS); - - try { - std::vector hpsp(OSUtils::split(_path.c_str(), ZT_PATH_SEPARATOR_S,"","")); - std::string ptmp; - if (_path[0] == ZT_PATH_SEPARATOR) { - ptmp.push_back(ZT_PATH_SEPARATOR); - } - for (std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { - if (ptmp.length() > 0) { - ptmp.push_back(ZT_PATH_SEPARATOR); - } - ptmp.append(*pi); - if ((*pi != ".")&&(*pi != "..")) { - if (OSUtils::mkdir(ptmp) == false) { - DEBUG_ERROR("home path does not exist, and could not create"); - err = true; - perror("error\n"); - } - } - } - for(;;) { - _service_lock.lock(); - service = OneService::newInstance(_path.c_str(),_port); - _service_lock.unlock(); - switch(service->run()) { - case OneService::ONE_STILL_RUNNING: - case OneService::ONE_NORMAL_TERMINATION: - postEvent(ZTS_EVENT_NODE_NORMAL_TERMINATION); - break; - case OneService::ONE_UNRECOVERABLE_ERROR: - DEBUG_ERROR("fatal error: %s", service->fatalErrorMessage().c_str()); - err = true; - postEvent(ZTS_EVENT_NODE_UNRECOVERABLE_ERROR); - break; - case OneService::ONE_IDENTITY_COLLISION: { - err = true; - delete service; - service = (OneService *)0; - std::string oldid; - OSUtils::readFile((_path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); - if (oldid.length()) { - OSUtils::writeFile((_path + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); - OSUtils::rm((_path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); - OSUtils::rm((_path + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); - } - postEvent(ZTS_EVENT_NODE_IDENTITY_COLLISION); - } continue; // restart! - } - break; // terminate loop -- normally we don't keep restarting - } - _service_lock.lock(); - _run_service = false; - delete service; - service = (OneService *)0; - _service_lock.unlock(); - postEvent(ZTS_EVENT_NODE_DOWN); - } catch ( ... ) { - DEBUG_ERROR("unexpected exception starting ZeroTier instance"); - } - //delete params; - // TODO: Find a more elegant solution - _api_sleep(ZTS_CALLBACK_PROCESSING_INTERVAL*2); - _run_callbacks = false; -#ifndef _WIN32 - pthread_exit(0); -#endif - return NULL; -} - -#ifdef __cplusplus -extern "C" { -#endif - -////////////////////////////////////////////////////////////////////////////// -// ZeroTier Service Controls // -////////////////////////////////////////////////////////////////////////////// + extern NodeService *service; + extern Mutex serviceLock; + extern void (*_userEventCallbackFunc)(void *); + extern uint8_t allowNetworkCaching; + extern uint8_t allowPeerCaching; + extern uint8_t allowLocalConf; #ifdef SDK_JNI -/* - * Called from Java, saves a static reference to the VM so it can be used - * later to call a user-specified callback method from C. - */ -JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_init( - JNIEnv *env, jobject thisObj) -{ - jint rs = env->GetJavaVM(&jvm); - return rs != JNI_OK ? ZTS_ERR_GENERAL : ZTS_ERR_OK; -} + // References to JNI objects and VM kept for future callbacks + JavaVM *jvm = NULL; + jobject objRef = NULL; + jmethodID _userCallbackMethodRef = NULL; #endif - -int zts_join(const uint64_t nwid) -{ - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - else { - service->join(nwid); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( - JNIEnv *env, jobject thisObj, jlong nwid) -{ - return zts_join((uint64_t)nwid); -} -#endif - -int zts_leave(const uint64_t nwid) -{ - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - else { - service->leave(nwid); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( - JNIEnv *env, jobject thisObj, jlong nwid) -{ - return zts_leave((uint64_t)nwid); -} -#endif - -int zts_leave_all() -{ - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - else { - service->leaveAll(); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -#endif - -int zts_orbit(uint64_t moonWorldId, uint64_t moonSeed) -{ - Mutex::Lock _l(_service_lock); - void *tptr = NULL; - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } else { - service->getNode()->orbit(tptr, moonWorldId, moonSeed); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -#endif - -int zts_deorbit(uint64_t moonWorldId) -{ - Mutex::Lock _l(_service_lock); - void *tptr = NULL; - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } else { - service->getNode()->deorbit(tptr, moonWorldId); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -#endif - -void zts_set_network_caching(bool enabled) -{ - _network_caching_enabled = enabled; } -void zts_set_peer_caching(bool enabled) +int zts_allow_network_caching(uint8_t allowed = 1) { - _peer_caching_enabled = enabled; + Mutex::Lock _l(serviceLock); + if(!service) { + allowNetworkCaching = allowed; + return ZTS_ERR_OK; + } + return ZTS_ERR_SERVICE; } -int zts_start( - const char *path, void (*callback)(struct zts_callback_msg*), int port) +int zts_allow_peer_caching(uint8_t allowed = 1) { - Mutex::Lock _l(_service_lock); - lwip_driver_init(); - if (service || _run_service) { + Mutex::Lock _l(serviceLock); + if(!service) { + allowPeerCaching = allowed; + return ZTS_ERR_OK; + } + return ZTS_ERR_SERVICE; +} + +int zts_allow_local_conf(uint8_t allowed = 1) +{ + Mutex::Lock _l(serviceLock); + if(!service) { + allowLocalConf = allowed; + return ZTS_ERR_OK; + } + return ZTS_ERR_SERVICE; +} + +int zts_start(const char *path, void (*callback)(void *), uint16_t port) +{ + Mutex::Lock _l(serviceLock); + _lwip_driver_init(); + if (service || _getState(ZTS_STATE_NODE_RUNNING)) { // Service is already initialized - return ZTS_ERR_INVALID_OP; + return ZTS_ERR_SERVICE; } - if (_freeHasBeenCalled) { + if (_getState(ZTS_STATE_FREE_CALLED)) { // Stack (presumably lwIP) has been dismantled, // an application restart is required now - return ZTS_ERR_INVALID_OP; + return ZTS_ERR_SERVICE; } #ifdef SDK_JNI _userEventCallbackFunc = callback; -#endif +#else _userEventCallbackFunc = callback; - if (!_is_callback_registered()) { +#endif + if (!_isCallbackRegistered()) { // Must have a callback - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } if (!path) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } if (port < 0 || port > 0xFFFF) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - - _path = std::string(path); - _port = port; - serviceParameters *params = new serviceParameters(); - /* params->port = port; - DEBUG_INFO("path=%s", path); params->path = std::string(path); - DEBUG_INFO("path=%s", params->path.c_str()); if (params->path.length() == 0) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - */ int err; int retval = ZTS_ERR_OK; - _run_callbacks = true; - _run_service = true; + + _setState(ZTS_STATE_CALLBACKS_RUNNING); + _setState(ZTS_STATE_NODE_RUNNING); // Start the ZT service thread -#if defined(_WIN32) - // Initialize WinSock. Used in Phy for loopback pipe - WSAStartup(MAKEWORD(2, 2), &wsaData); - HANDLE serviceThread = CreateThread(NULL, 0, _zts_run_service, (void*)params, 0, NULL); - HANDLE callbackThread = CreateThread(NULL, 0, _zts_run_callbacks, NULL, 0, NULL); +#if defined(__WINDOWS__) + HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)params, 0, NULL); + HANDLE callbackThread = CreateThread(NULL, 0, _runCallbacks, NULL, 0, NULL); #else pthread_t service_thread; pthread_t callback_thread; - if ((err = pthread_create(&service_thread, NULL, _zts_run_service, NULL)) != 0) { + if ((err = pthread_create(&service_thread, NULL, _runNodeService, (void*)params)) != 0) { retval = err; } - if ((err = pthread_create(&callback_thread, NULL, _zts_run_callbacks, NULL)) != 0) { + if ((err = pthread_create(&callback_thread, NULL, _runCallbacks, NULL)) != 0) { retval = err; } #endif @@ -584,32 +144,31 @@ int zts_start( pthread_setname_np(service_thread, ZTS_SERVICE_THREAD_NAME); pthread_setname_np(callback_thread, ZTS_EVENT_CALLBACK_THREAD_NAME); #endif - if (retval != ZTS_ERR_OK) { - _run_callbacks = false; - _run_service = false; - _clear_registered_callback(); - delete params; + _clrState(ZTS_STATE_CALLBACKS_RUNNING); + _clrState(ZTS_STATE_NODE_RUNNING); + _clearRegisteredCallback(); + //delete params; } return retval; } #ifdef SDK_JNI -JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_start( +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_start( JNIEnv *env, jobject thisObj, jstring path, jobject callback, jint port) { if (!path) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } jclass eventListenerClass = env->GetObjectClass(callback); if(eventListenerClass == NULL) { DEBUG_ERROR("Couldn't find class for ZeroTierEventListener instance"); - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } jmethodID eventListenerCallbackMethod = env->GetMethodID(eventListenerClass, "onZeroTierEvent", "(JI)V"); if(eventListenerCallbackMethod == NULL) { DEBUG_ERROR("Couldn't find onZeroTierEvent method"); - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } // Reference used for later calls objRef = env->NewGlobalRef(callback); @@ -627,11 +186,11 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_start( int zts_stop() { - Mutex::Lock _l(_service_lock); - if (_zts_can_perform_service_operation()) { - _run_service = false; + Mutex::Lock _l(serviceLock); + if (_canPerformServiceOperation()) { + _clrState(ZTS_STATE_NODE_RUNNING); service->terminate(); -#if defined(_WIN32) +#if defined(__WINDOWS__) WSACleanup(); #endif return ZTS_ERR_OK; @@ -648,40 +207,44 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop( int zts_restart() { - _service_lock.lock(); + serviceLock.lock(); // Store callback references #ifdef SDK_JNI static jmethodID _tmpUserCallbackMethodRef = _userCallbackMethodRef; #else - void (*_tmpUserEventCallbackFunc)(struct zts_callback_msg *); + void (*_tmpUserEventCallbackFunc)(void *); _tmpUserEventCallbackFunc = _userEventCallbackFunc; #endif - int tmpPort = _port; - std::string tmpPath = _path; + int userProvidedPort = 0; + std::string userProvidedPath; + if (service) { + userProvidedPort = service->_userProvidedPort; + userProvidedPath = service->_userProvidedPath; + } // Stop the service - if (_zts_can_perform_service_operation()) { - _run_service = false; + if (_canPerformServiceOperation()) { + _clrState(ZTS_STATE_NODE_RUNNING); service->terminate(); -#if defined(_WIN32) +#if defined(__WINDOWS__) WSACleanup(); #endif } else { - _service_lock.unlock(); + serviceLock.unlock(); return ZTS_ERR_SERVICE; } // Start again with same parameters as initial call - _service_lock.unlock(); + serviceLock.unlock(); while (service) { - _api_sleep(ZTS_CALLBACK_PROCESSING_INTERVAL); + zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL); } /* Some of the logic in Java_com_zerotier_libzt_ZeroTier_start is replicated here */ #ifdef SDK_JNI _userCallbackMethodRef = _tmpUserCallbackMethodRef; - return zts_start(tmpPath.c_str(), NULL, tmpPort); + return zts_start(userProvidedPath.c_str(), NULL, userProvidedPort); #else - return ::zts_start(tmpPath.c_str(), _tmpUserEventCallbackFunc, tmpPort); + //return zts_start(userProvidedPath.c_str(), _tmpUserEventCallbackFunc, userProvidedPort); #endif } #ifdef SDK_JNI @@ -694,11 +257,11 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( int zts_free() { - Mutex::Lock _l(_service_lock); - if (_freeHasBeenCalled) { - return ZTS_ERR_INVALID_OP; + Mutex::Lock _l(serviceLock); + if (_getState(ZTS_STATE_FREE_CALLED)) { + return ZTS_ERR_SERVICE; } - _freeHasBeenCalled = true; + _setState(ZTS_STATE_FREE_CALLED); return zts_stop(); // TODO: add stack shutdown logic } @@ -712,9 +275,9 @@ JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( uint64_t zts_get_node_id() { - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; + Mutex::Lock _l(serviceLock); + if (!_canPerformServiceOperation()) { + return ZTS_ERR_OK; // Not really } return service->getNode()->address(); } @@ -726,10 +289,161 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( } #endif +int zts_get_node_status() +{ + Mutex::Lock _l(serviceLock); + // Don't check _canPerformServiceOperation() here. + return service + && service->getNode() + && service->getNode()->online() ? ZTS_EVENT_NODE_ONLINE : ZTS_EVENT_NODE_OFFLINE; +} +#ifdef SDK_JNI +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1status( + JNIEnv *env, jobject thisObj) +{ + return zts_get_node_status(); +} +#endif + +int zts_get_network_status(uint64_t networkId) +{ + Mutex::Lock _l(serviceLock); + if (!networkId) { + return ZTS_ERR_ARG; + } + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } + /* + TODO: + ZTS_EVENT_NETWORK_READY_IP4 + ZTS_EVENT_NETWORK_READY_IP6 + ZTS_EVENT_NETWORK_READY_IP4_IP6 + */ + return ZTS_ERR_NO_RESULT; +} +#ifdef SDK_JNI +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1network_1status( + JNIEnv *env, jobject thisObj, jlong networkId) +{ + return zts_get_network_status(networkId); +} +#endif + +int zts_get_peer_status(uint64_t peerId) +{ + Mutex::Lock _l(serviceLock); + int retval = ZTS_ERR_OK; + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } + return service->getPeerStatus(peerId); +} +#ifdef SDK_JNI +JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( + JNIEnv *env, jobject thisObj, jlong peerId) +{ + return zts_get_peer_status(peerId); +} +#endif + +#ifdef SDK_JNI +/* + * Called from Java, saves a static reference to the VM so it can be used + * later to call a user-specified callback method from C. + */ +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init( + JNIEnv *env, jobject thisObj) +{ + jint rs = env->GetJavaVM(&jvm); + return rs != JNI_OK ? ZTS_ERR_GENERAL : ZTS_ERR_OK; +} +#endif + +int zts_join(const uint64_t nwid) +{ + Mutex::Lock _l(serviceLock); + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } + else { + service->join(nwid); + } + return ZTS_ERR_OK; +} +#ifdef SDK_JNI +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( + JNIEnv *env, jobject thisObj, jlong nwid) +{ + return zts_join((uint64_t)nwid); +} +#endif + +int zts_leave(const uint64_t nwid) +{ + Mutex::Lock _l(serviceLock); + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } + else { + service->leave(nwid); + } + return ZTS_ERR_OK; +} +#ifdef SDK_JNI +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( + JNIEnv *env, jobject thisObj, jlong nwid) +{ + return zts_leave((uint64_t)nwid); +} +#endif + +int zts_leave_all() +{ + Mutex::Lock _l(serviceLock); + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } + else { + service->leaveAll(); + } + return ZTS_ERR_OK; +} +#ifdef SDK_JNI +#endif + +int zts_orbit(uint64_t moonWorldId, uint64_t moonSeed) +{ + Mutex::Lock _l(serviceLock); + void *tptr = NULL; + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } else { + service->getNode()->orbit(tptr, moonWorldId, moonSeed); + } + return ZTS_ERR_OK; +} +#ifdef SDK_JNI +#endif + +int zts_deorbit(uint64_t moonWorldId) +{ + Mutex::Lock _l(serviceLock); + void *tptr = NULL; + if (!_canPerformServiceOperation()) { + return ZTS_ERR_SERVICE; + } else { + service->getNode()->deorbit(tptr, moonWorldId); + } + return ZTS_ERR_OK; +} +#ifdef SDK_JNI +#endif + int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { if (!addr || !nwid || !nodeId) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } InetAddress _6planeAddr = InetAddress::makeIpv66plane(nwid,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; @@ -740,7 +454,7 @@ int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, cons int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { if (!addr || !nwid || !nodeId) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(nwid,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; @@ -748,6 +462,7 @@ int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, con return ZTS_ERR_OK; } + uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange) { char nwidStr[INET6_ADDRSTRLEN]; @@ -755,40 +470,20 @@ uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t return strtoull(nwidStr, NULL, 16); } -////////////////////////////////////////////////////////////////////////////// -// Peers // -////////////////////////////////////////////////////////////////////////////// - -int zts_get_peer_count() +int zts_get_peers(struct zts_peer_details *pds, uint32_t *num) { - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - return service->getNode()->peers()->peerCount; -} -#ifdef SDK_JNI -JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1count( - JNIEnv *env, jobject thisObj) -{ - return zts_get_peer_count(); -} -#endif - -int zts_get_peers(struct zts_peer_details *pds, unsigned int *num) -{ - Mutex::Lock _l(_service_lock); + Mutex::Lock _l(serviceLock); if (!pds || !num) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (!_zts_can_perform_service_operation()) { + if (!_canPerformServiceOperation()) { return ZTS_ERR_SERVICE; } ZT_PeerList *pl = service->getNode()->peers(); if (pl) { if (*num < pl->peerCount) { service->getNode()->freeQueryResult((void *)pl); - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } *num = pl->peerCount; for(unsigned long i=0;ipeerCount;++i) { @@ -807,11 +502,11 @@ int zts_get_peers(struct zts_peer_details *pds, unsigned int *num) int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) { - Mutex::Lock _l(_service_lock); + Mutex::Lock _l(serviceLock); if (!pd || !peerId) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (!_zts_can_perform_service_operation()) { + if (!_canPerformServiceOperation()) { return ZTS_ERR_SERVICE; } ZT_PeerList *pl = service->getNode()->peers(); @@ -835,30 +530,6 @@ int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) #ifdef SDK_JNI #endif -////////////////////////////////////////////////////////////////////////////// -// Networks // -////////////////////////////////////////////////////////////////////////////// - -size_t zts_get_num_joined_networks() -{ - Mutex::Lock _l(_service_lock); - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - return service->networkCount(); -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1num_1joined_1networks( - JNIEnv *env, jobject thisObj) -{ - return zts_get_num_joined_networks(); -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// Network Details // -////////////////////////////////////////////////////////////////////////////// - void _get_network_details_helper(uint64_t nwid, struct zts_network_details *nd) { /* @@ -905,10 +576,10 @@ void _get_all_network_details(struct zts_network_details *nds, int *num) int zts_get_network_details(uint64_t nwid, struct zts_network_details *nd) { /* - _service_lock.lock(); + serviceLock.lock(); int retval = ZTS_ERR_OK; if (!nd || nwid == 0) { - retval = ZTS_ERR_INVALID_ARG; + retval = ZTS_ERR_ARG; } if (!service || _freeHasBeenCalled || _serviceIsShuttingDown) { retval = ZTS_ERR_SERVICE; @@ -916,7 +587,7 @@ int zts_get_network_details(uint64_t nwid, struct zts_network_details *nd) if (retval == ZTS_ERR_OK) { _get_network_details(nwid, nd); } - _service_lock.unlock(); + serviceLock.unlock(); return retval; */ return 0; @@ -927,10 +598,10 @@ int zts_get_network_details(uint64_t nwid, struct zts_network_details *nd) int zts_get_all_network_details(struct zts_network_details *nds, int *num) { /* - _service_lock.lock(); + serviceLock.lock(); int retval = ZTS_ERR_OK; if (!nds || !num) { - retval = ZTS_ERR_INVALID_ARG; + retval = ZTS_ERR_ARG; } if (!service || _freeHasBeenCalled || _serviceIsShuttingDown) { retval = ZTS_ERR_SERVICE; @@ -938,7 +609,7 @@ int zts_get_all_network_details(struct zts_network_details *nds, int *num) if (retval == ZTS_ERR_OK) { _get_all_network_details(nds, num); } - _service_lock.unlock(); + serviceLock.unlock(); return retval; */ return 0; @@ -946,206 +617,13 @@ int zts_get_all_network_details(struct zts_network_details *nds, int *num) #ifdef SDK_JNI #endif -////////////////////////////////////////////////////////////////////////////// -// Statistics // -////////////////////////////////////////////////////////////////////////////// - -#include "lwip/stats.h" - -extern struct stats_ lwip_stats; - -int zts_get_all_stats(struct zts_stats *statsDest) +void zts_delay_ms(long interval_ms) { -#if LWIP_STATS - if (!statsDest) { - return ZTS_ERR_INVALID_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; +#if defined(__WINDOWS__) + Sleep(interval_ms); #else - return ZTS_ERR_NO_RESULT; + struct timespec sleepValue = {0}; + sleepValue.tv_nsec = interval_ms * 500000; + nanosleep(&sleepValue, NULL); #endif } -#ifdef SDK_JNI - // No implementation for JNI -#endif - -int zts_get_protocol_stats(int protocolType, void *protoStatsDest) -{ -#if LWIP_STATS - if (!protoStatsDest) { - return ZTS_ERR_INVALID_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_INVALID_ARG; - } - return ZTS_ERR_OK; -#else - return ZTS_ERR_NO_RESULT; -#endif -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1protocol_1stats( - JNIEnv *env, jobject thisObj, jint protocolType, jobject protoStatsObj) -{ - struct stats_proto stats; - int retval = zts_get_protocol_stats(protocolType, &stats); - // Copy stats into Java object - jclass c = env->GetObjectClass(protoStatsObj); - if (!c) { - return ZTS_ERR_INVALID_ARG; - } - jfieldID fid; - fid = env->GetFieldID(c, "xmit", "I"); - env->SetIntField(protoStatsObj, fid, stats.xmit); - fid = env->GetFieldID(c, "recv", "I"); - env->SetIntField(protoStatsObj, fid, stats.recv); - fid = env->GetFieldID(c, "fw", "I"); - env->SetIntField(protoStatsObj, fid, stats.fw); - fid = env->GetFieldID(c, "drop", "I"); - env->SetIntField(protoStatsObj, fid, stats.drop); - fid = env->GetFieldID(c, "chkerr", "I"); - env->SetIntField(protoStatsObj, fid, stats.chkerr); - fid = env->GetFieldID(c, "lenerr", "I"); - env->SetIntField(protoStatsObj, fid, stats.lenerr); - fid = env->GetFieldID(c, "memerr", "I"); - env->SetIntField(protoStatsObj, fid, stats.memerr); - fid = env->GetFieldID(c, "rterr", "I"); - env->SetIntField(protoStatsObj, fid, stats.rterr); - fid = env->GetFieldID(c, "proterr", "I"); - env->SetIntField(protoStatsObj, fid, stats.proterr); - fid = env->GetFieldID(c, "opterr", "I"); - env->SetIntField(protoStatsObj, fid, stats.opterr); - fid = env->GetFieldID(c, "err", "I"); - env->SetIntField(protoStatsObj, fid, stats.err); - fid = env->GetFieldID(c, "cachehit", "I"); - env->SetIntField(protoStatsObj, fid, stats.cachehit); - return retval; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// Multipath/QoS // -////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////// -// Status getters // -////////////////////////////////////////////////////////////////////////////// - -int zts_get_node_status() -{ - Mutex::Lock _l(_service_lock); - // Don't check _zts_can_perform_service_operation() here. - return service - && service->getNode() - && service->getNode()->online() ? ZTS_EVENT_NODE_ONLINE : ZTS_EVENT_NODE_OFFLINE; -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1status( - JNIEnv *env, jobject thisObj) -{ - return zts_get_node_status(); -} -#endif - -int zts_get_network_status(uint64_t networkId) -{ - Mutex::Lock _l(_service_lock); - if (!networkId) { - return ZTS_ERR_INVALID_ARG; - } - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - /* - TODO: - ZTS_EVENT_NETWORK_READY_IP4 - ZTS_EVENT_NETWORK_READY_IP6 - ZTS_EVENT_NETWORK_READY_IP4_IP6 - */ - return ZTS_ERR_NO_RESULT; -} -#ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1network_1status( - JNIEnv *env, jobject thisObj, jlong networkId) -{ - return zts_get_network_status(networkId); -} -#endif - -int zts_get_peer_status(uint64_t peerId) -{ - Mutex::Lock _l(_service_lock); - int retval = ZTS_ERR_OK; - if (!_zts_can_perform_service_operation()) { - return ZTS_ERR_SERVICE; - } - return service->getPeerStatus(peerId); -} -#ifdef SDK_JNI -JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( - JNIEnv *env, jobject thisObj, jlong peerId) -{ - return zts_get_peer_status(peerId); -} -#endif - -#ifdef __cplusplus -} -#endif - -} // namespace ZeroTier \ No newline at end of file diff --git a/src/Controls.hpp b/src/Controls.hpp deleted file mode 100644 index 06b464d..0000000 --- a/src/Controls.hpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * Header for ZeroTier service controls - */ - -#ifndef LIBZT_CONTROLS_HPP -#define LIBZT_CONTROLS_HPP - -#ifdef _WIN32 -#include -#endif - -namespace ZeroTier { - -////////////////////////////////////////////////////////////////////////////// -// ZeroTier Internal Service Controls // -////////////////////////////////////////////////////////////////////////////// - -/** - * Add a callback event message to the queue. This can be safely called - * from other threads since a lock-free queue is used. - * - * @param eventCode The event ID for this event - * @param msg Pointer to a structure of pointers to other message-relevant - * data structures. - */ -void postEvent(int eventCode, void *arg); - -/** - * Add a callback event message to the queue. This can be safely called - * from other threads since a lock-free queue is used. Note: For use in - * situations when no additional information needs to be conveyed to the - * user application. - * - * @param eventCode The event ID for this event - */ -void postEvent(int eventCode); - -/** - * Free whatever was allocated to contain the callback message - * - * @param msg Message to be freed - */ -void freeEvent(struct zts_callback_msg *msg); - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Starts a ZeroTier service in the background - * - * @usage For internal use only. - * @param - * @return - */ -#if defined(_WIN32) -DWORD WINAPI _zts_run_service(LPVOID thread_id); -#else -void *_zts_run_service(void *thread_id); -#endif - -/** - * @brief [Should not be called from user application] This function must be surrounded by - * ZT service locks. It will determine if it is currently safe and allowed to operate on - * the service. - * @usage Can be called at any time - * @return 1 or 0 - */ -int _zts_can_perform_service_operation(); - -/** - * @brief [Should not be called from user application] Returns whether or not the node is - * online. - * @usage Can be called at any time - * @return 1 or 0 - */ -int _zts_node_online(); - -/** - * @brief [Should not be called from user application] Adjusts the delay multiplier for the - * network stack driver thread. - * @usage Can be called at any time - */ -void _hibernate_if_needed(); - -#ifdef __cplusplus -} -#endif - -} // namespace ZeroTier - -#endif // _H \ No newline at end of file diff --git a/src/Events.cpp b/src/Events.cpp new file mode 100644 index 0000000..3cbd332 --- /dev/null +++ b/src/Events.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-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. + */ +/****/ + +/** + * @file + * + * Callback event processing logic + */ + +#include "concurrentqueue.h" + +#ifdef SDK_JNI + #include +#endif + +#include "Node.hpp" +#include "OSUtils.hpp" + +#include "Debug.hpp" +#include "Events.hpp" +#include "ZeroTierSockets.h" +#include "NodeService.hpp" + +#define NODE_EVENT_TYPE(code) code >= ZTS_EVENT_NODE_UP && code <= ZTS_EVENT_NODE_NORMAL_TERMINATION +#define NETWORK_EVENT_TYPE(code) code >= ZTS_EVENT_NETWORK_NOT_FOUND && code <= ZTS_EVENT_NETWORK_DOWN +#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_UNREACHABLE +#define PATH_EVENT_TYPE(code) code >= ZTS_EVENT_PATH_DISCOVERED && code <= ZTS_EVENT_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 + +namespace ZeroTier { + +extern NodeService *service; + +// Global state variable shared between Socket, Control, Event and NodeService logic. +uint8_t _serviceStateFlags; + +// Lock to guard access to callback function pointers. +Mutex _callbackLock; + +void (*_userEventCallbackFunc)(void *); + +moodycamel::ConcurrentQueue _callbackMsgQueue; + +void _enqueueEvent(int16_t eventCode, void *arg) +{ + struct ::zts_callback_msg *msg = new ::zts_callback_msg(); + + msg->node = NULL; + msg->network = NULL; + msg->netif = NULL; + msg->route = NULL; + msg->path = NULL; + msg->peer = NULL; + msg->addr = NULL; + + msg->eventCode = eventCode; + + if (NODE_EVENT_TYPE(eventCode)) { + msg->node = (struct zts_node_details*)arg; + } if (NETWORK_EVENT_TYPE(eventCode)) { + msg->network = (struct zts_network_details*)arg; + } if (NETIF_EVENT_TYPE(eventCode)) { + msg->netif = (struct zts_netif_details*)arg; + } if (ROUTE_EVENT_TYPE(eventCode)) { + msg->route = (struct zts_virtual_network_route*)arg; + } if (PATH_EVENT_TYPE(eventCode)) { + msg->path = (struct zts_physical_path*)arg; + } if (PEER_EVENT_TYPE(eventCode)) { + msg->peer = (struct zts_peer_details*)arg; + } if (ADDR_EVENT_TYPE(eventCode)) { + msg->addr = (struct zts_addr_details*)arg; + } + _callbackMsgQueue.enqueue(msg); +} + +void _freeEvent(struct ::zts_callback_msg *msg) +{ + if (!msg) { + return; + } + if (msg->node) { delete msg->node; } + if (msg->network) { delete msg->network; } + if (msg->netif) { delete msg->netif; } + if (msg->route) { delete msg->route; } + if (msg->path) { delete msg->path; } + if (msg->peer) { delete msg->peer; } + if (msg->addr) { delete msg->addr; } +} + +void _passDequeuedEventToUser(struct ::zts_callback_msg *msg) +{ +#ifdef SDK_JNI + if(_userCallbackMethodRef) { + JNIEnv *env; +#if defined(__ANDROID__) + jint rs = jvm->AttachCurrentThread(&env, NULL); +#else + jint rs = jvm->AttachCurrentThread((void **)&env, NULL); +#endif + assert (rs == JNI_OK); + uint64_t arg = 0; + uint64_t id = 0; + if (NODE_EVENT_TYPE(msg->eventCode)) { + id = msg->node ? msg->node->address : 0; + } + if (NETWORK_EVENT_TYPE(msg->eventCode)) { + id = msg->network ? msg->network->nwid : 0; + } + if (PEER_EVENT_TYPE(msg->eventCode)) { + id = msg->peer ? msg->peer->address : 0; + } + env->CallVoidMethod(objRef, _userCallbackMethodRef, id, msg->eventCode); + _freeEvent(msg); + } +#else + if (_userEventCallbackFunc) { + _userEventCallbackFunc(msg); + _freeEvent(msg); + } +#endif +} + +bool _isCallbackRegistered() +{ + _callbackLock.lock(); + bool retval = false; +#ifdef SDK_JNI + retval = (jvm && objRef && _userCallbackMethodRef); +#else + retval = _userEventCallbackFunc; +#endif + _callbackLock.unlock(); + return retval; +} + +void _clearRegisteredCallback() +{ + _callbackLock.lock(); +#ifdef SDK_JNI + objRef = NULL; + _userCallbackMethodRef = NULL; +#else + _userEventCallbackFunc = NULL; +#endif + _callbackLock.unlock(); +} + +int _canPerformServiceOperation() +{ + return service + && service->isRunning() + && service->getNode() + && service->getNode()->online() + && !_getState(ZTS_STATE_FREE_CALLED); +} + +#define RESET_FLAGS( ) _serviceStateFlags = 0; +#define SET_FLAGS(f) _serviceStateFlags |= f; +#define CLR_FLAGS(f) _serviceStateFlags &= ~f; +#define GET_FLAGS(f) ((_serviceStateFlags & f) > 0) + +void _setState(uint8_t newFlags) +{ + if ((newFlags ^ _serviceStateFlags) & ZTS_STATE_NET_SERVICE_RUNNING) { + return; // No effect. Not allowed to set this flag manually + } + SET_FLAGS(newFlags); + if ( GET_FLAGS(ZTS_STATE_NODE_RUNNING) + && GET_FLAGS(ZTS_STATE_STACK_RUNNING) + && !(GET_FLAGS(ZTS_STATE_FREE_CALLED))) + { + SET_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING); + } + else { + CLR_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING); + } +} + +void _clrState(uint8_t newFlags) +{ + if (newFlags & ZTS_STATE_NET_SERVICE_RUNNING) { + return; // No effect. Not allowed to set this flag manually + } + CLR_FLAGS(newFlags); + if ( GET_FLAGS(ZTS_STATE_NODE_RUNNING) + && GET_FLAGS(ZTS_STATE_STACK_RUNNING) + && !(GET_FLAGS(ZTS_STATE_FREE_CALLED))) + { + SET_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING); + } + else { + CLR_FLAGS(ZTS_STATE_NET_SERVICE_RUNNING); + } +} + +bool _getState(uint8_t testFlags) +{ + return testFlags & _serviceStateFlags; +} + +#if defined(__WINDOWS__) +DWORD WINAPI _runCallbacks(LPVOID thread_id) +#else +void *_runCallbacks(void *thread_id) +#endif +{ +#if defined(__APPLE__) + pthread_setname_np(ZTS_EVENT_CALLBACK_THREAD_NAME); +#endif + while (_getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) + { + struct ::zts_callback_msg *msg; + size_t sz = _callbackMsgQueue.size_approx(); + for (size_t j = 0; j < sz; j++) { + if (_callbackMsgQueue.try_dequeue(msg)) { + _callbackLock.lock(); + _passDequeuedEventToUser(msg); + _callbackLock.unlock(); + delete msg; + } + } + zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL); + } +#if SDK_JNI + JNIEnv *env; + jint rs = jvm->DetachCurrentThread(); + pthread_exit(0); +#endif + return NULL; +} + +} // namespace ZeroTier diff --git a/src/Events.hpp b/src/Events.hpp new file mode 100644 index 0000000..58a3406 --- /dev/null +++ b/src/Events.hpp @@ -0,0 +1,106 @@ +/* + * Copyright (c)2013-2020 ZeroTier, Inc. + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file in the project's root directory. + * + * Change Date: 2024-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. + */ +/****/ + +/** + * @file + * + * Header for callback event processing logic + */ + +#ifndef ZT_EVENTS_HPP +#define ZT_EVENTS_HPP + +#include + +#include "ZeroTierSockets.h" + +#ifdef SDK_JNI + #include +#endif +namespace ZeroTier { + +#define ZTS_STATE_NODE_RUNNING 0x01 +#define ZTS_STATE_STACK_RUNNING 0x02 +#define ZTS_STATE_NET_SERVICE_RUNNING 0x04 +#define ZTS_STATE_CALLBACKS_RUNNING 0x08 +#define ZTS_STATE_FREE_CALLED 0x10 + +#ifdef SDK_JNI + // References to JNI objects and VM kept for future callbacks + extern JavaVM *jvm; + extern jobject objRef; + extern jmethodID _userCallbackMethodRef; +#endif + +/** + * How often callback messages are assembled and/or sent + */ +#define ZTS_CALLBACK_PROCESSING_INTERVAL 25 + +/** + * Enqueue an event to be sent to the user application + */ +void _enqueueEvent(int16_t eventCode, void *arg); + +/** + * Send callback message to user application + */ +void _passDequeuedEventToUser(struct ::zts_callback_msg *msg); + +/** + * Free memory occupied by callback structures + */ +void _freeEvent(struct ::zts_callback_msg *msg); + +/** + * Return whether a callback method has been set + */ +bool _isCallbackRegistered(); + +/** + * Clear pointer reference to user-provided callback function + */ +void _clearRegisteredCallback(); + +/** + * Return whether service operation can be performed at this time + */ +int _canPerformServiceOperation(); + +/** + * Set internal state flags + */ +void _setState(uint8_t newFlags); + +/** + * Clear internal state flags + */ +void _clrState(uint8_t newFlags); + +/** + * Get internal state flags + */ +bool _getState(uint8_t testFlags); + +#ifdef __WINDOWS__ +DWORD WINAPI _runCallbacks(LPVOID thread_id); +#else +/** + * Event callback thread + */ +void *_runCallbacks(void *thread_id); +#endif + +} // namespace ZeroTier + +#endif // _H \ No newline at end of file diff --git a/src/Service.cpp b/src/NodeService.cpp similarity index 85% rename from src/Service.cpp rename to src/NodeService.cpp index e9e587a..3a04d61 100644 --- a/src/Service.cpp +++ b/src/NodeService.cpp @@ -11,89 +11,57 @@ */ /****/ -#include -#include -#include +/** + * @file + * + * ZeroTier Node Service (a distant relative of OneService) + */ -#include -#include -#include -#include -#include #include -#include -#include -#include "version.h" -#include "ZeroTierOne.h" +#include "Debug.hpp" +#include "Events.hpp" +#include "NodeService.hpp" +#include "ZeroTierSockets.h" +#include "VirtualTap.hpp" #include "Constants.hpp" -#include "Mutex.hpp" #include "Node.hpp" #include "Utils.hpp" -#include "InetAddress.hpp" #include "MAC.hpp" -#include "Identity.hpp" -#include "World.hpp" -#include "Salsa20.hpp" -#include "Poly1305.hpp" -#include "SHA512.hpp" - #include "Phy.hpp" #include "Thread.hpp" #include "OSUtils.hpp" #include "PortMapper.hpp" #include "Binder.hpp" #include "ManagedRoute.hpp" +#include "InetAddress.hpp" #include "BlockingQueue.hpp" -#include "Service.hpp" -#include "Debug.hpp" -#include "concurrentqueue.h" - -#include "ZeroTier.h" -#include "lwipDriver.hpp" - -#ifdef __WINDOWS__ +#if defined(__WINDOWS__) +WSADATA wsaData; #include #include #include #include #include -//#include #define stat _stat -#else -#include -#include -#include -#include -#include -#include #endif -#include "Controls.hpp" +#ifdef SDK_JNI +#include +#endif -// Use the virtual netcon endpoint instead of a tun/tap port driver -#include "VirtualTap.hpp" -namespace ZeroTier { typedef VirtualTap EthernetTap; } - -// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also -// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi. -#define ZT_IF_METRIC 5000 - -// How often to check for new multicast subscriptions on a tap device -#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 - -// How often to check for local interface addresses -#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000 +// Custom errno-like reporting variable +int zts_errno; namespace ZeroTier { -extern void postEvent(uint64_t id, int eventCode); -extern bool _network_caching_enabled; -extern bool _peer_caching_enabled; +uint8_t allowNetworkCaching; +uint8_t allowPeerCaching; +uint8_t allowLocalConf; -namespace { +typedef VirtualTap EthernetTap; static std::string _trimString(const std::string &s) { @@ -114,7 +82,7 @@ static std::string _trimString(const std::string &s) return s.substr(start,end - start); } -class OneServiceImpl; +class NodeServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf); static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData); @@ -126,7 +94,7 @@ static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t z static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result); static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); -struct OneServiceIncomingPacket +struct NodeServiceIncomingPacket { uint64_t now; int64_t sock; @@ -135,7 +103,7 @@ struct OneServiceIncomingPacket uint8_t data[ZT_MAX_MTU]; }; -class OneServiceImpl : public OneService +class NodeServiceImpl : public NodeService { public: // begin member variables -------------------------------------------------- @@ -145,7 +113,7 @@ public: const std::string _networksPath; const std::string _moonsPath; - Phy _phy; + Phy _phy; Node *_node; bool _updateAutoApply; unsigned int _multipathMode = 0; @@ -159,8 +127,8 @@ public: // unsigned long _incomingPacketConcurrency; - std::vector _incomingPacketMemoryPool; - BlockingQueue _incomingPacketQueue; + std::vector _incomingPacketMemoryPool; + BlockingQueue _incomingPacketQueue; std::vector _incomingPacketThreads; Mutex _incomingPacketMemoryPoolLock,_incomingPacketThreadsLock; @@ -238,7 +206,7 @@ public: // end member variables ---------------------------------------------------- - OneServiceImpl(const char *hp,unsigned int port) : + NodeServiceImpl(const char *hp,unsigned int port) : _homePath((hp) ? hp : ".") ,_phy(this,false,true) ,_node((Node *)0) @@ -259,51 +227,16 @@ public: _ports[1] = 0; _ports[2] = 0; - /* Packet input concurrency is disabled intentially since it - would force the user-space network stack to constantly re-order - frames, resulting in lower RX performance */ - - /* - _incomingPacketConcurrency = 1; - // std::max((unsigned long)1,std::min((unsigned long)16,(unsigned long)std::thread::hardware_concurrency())); - char *envPool = std::getenv("INCOMING_PACKET_CONCURRENCY"); - if (envPool != NULL) { - int tmp = atoi(envPool); - if (tmp > 0) { - _incomingPacketConcurrency = tmp; - } - } - for(long t=0;t<_incomingPacketConcurrency;++t) { - _incomingPacketThreads.push_back(std::thread([this]() { - OneServiceIncomingPacket *pkt = nullptr; - for(;;) { - if (!_incomingPacketQueue.get(pkt)) - break; - if (!pkt) - break; - if (!_run) - break; - - const ZT_ResultCode rc = _node->processWirePacket(nullptr,pkt->now,pkt->sock,&(pkt->from),pkt->data,pkt->size,&_nextBackgroundTaskDeadline); - { - Mutex::Lock l(_incomingPacketMemoryPoolLock); - _incomingPacketMemoryPool.push_back(pkt); - } - if (ZT_ResultCode_isFatal(rc)) { - char tmp[256]; - OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc); - Mutex::Lock _l(_termReason_m); - _termReason = ONE_UNRECOVERABLE_ERROR; - _fatalErrorMessage = tmp; - this->terminate(); - break; - } - } - })); - }*/ + allowNetworkCaching = true; + allowPeerCaching = true; + allowLocalConf = false; +#ifdef __WINDOWS__ + // Initialize WinSock. Used in Phy for loopback pipe + WSAStartup(MAKEWORD(2, 2), &wsaData); +#endif } - virtual ~OneServiceImpl() + virtual ~NodeServiceImpl() { _incomingPacketQueue.stop(); _incomingPacketThreadsLock.lock(); @@ -425,7 +358,7 @@ public: } #endif // Join existing networks in networks.d - if (_network_caching_enabled) { + if (allowNetworkCaching) { std::vector networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str())); for(std::vector::iterator f(networksDotD.begin());f!=networksDotD.end();++f) { std::size_t dot = f->find_last_of('.'); @@ -769,7 +702,7 @@ public: *nuptr = (void *)0; delete n.tap; _nets.erase(nwid); - if (_network_caching_enabled) { + if (allowNetworkCaching) { if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY) { char nlcpath[256]; OSUtils::ztsnprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid); @@ -787,17 +720,25 @@ public: inline void nodeEventCallback(enum ZT_Event event,const void *metaData) { // Feed node events into lock-free queue for later dequeuing by the callback thread - if (event <= ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION) { - if (event == ZTS_EVENT_NODE_ONLINE) { + switch(event) { + case ZT_EVENT_UP: { + _enqueueEvent(ZTS_EVENT_NODE_UP, NULL); + } break; + case ZT_EVENT_ONLINE: { struct zts_node_details *nd = new zts_node_details; nd->address = _node->address(); - postEvent(event, (void*)nd); - } - else { - postEvent(event, (void*)0); - } - } - switch(event) { + _enqueueEvent(ZTS_EVENT_NODE_ONLINE, (void*)nd); + } break; + case ZT_EVENT_OFFLINE: { + struct zts_node_details *nd = new zts_node_details; + nd->address = _node->address(); + _enqueueEvent(ZTS_EVENT_NODE_OFFLINE, (void*)nd); + } break; + case ZT_EVENT_DOWN: { + struct zts_node_details *nd = new zts_node_details; + nd->address = _node->address(); + _enqueueEvent(ZTS_EVENT_NODE_DOWN, (void*)nd); + } break; case ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION: { Mutex::Lock _l(_termReason_m); _termReason = ONE_IDENTITY_COLLISION; @@ -828,7 +769,7 @@ public: { // Force the ordering of callback messages, these messages are // only useful if the node and stack are both up and running - if (!_node->online() || !lwip_is_up()) { + if (!_node->online() || !_lwip_is_up()) { return; } // Generate messages to be dequeued by the callback message thread @@ -842,26 +783,26 @@ public: } switch (mostRecentStatus) { case ZT_NETWORK_STATUS_NOT_FOUND: - postEvent(ZTS_EVENT_NETWORK_NOT_FOUND, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_NOT_FOUND, (void*)prepare_network_details_msg(nwid)); break; case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: - postEvent(ZTS_EVENT_NETWORK_CLIENT_TOO_OLD, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_CLIENT_TOO_OLD, (void*)prepare_network_details_msg(nwid)); break; case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: - postEvent(ZTS_EVENT_NETWORK_REQUESTING_CONFIG, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_REQ_CONFIG, (void*)prepare_network_details_msg(nwid)); break; case ZT_NETWORK_STATUS_OK: - if (tap->hasIpv4Addr() && lwip_is_netif_up(tap->netif4)) { - postEvent(ZTS_EVENT_NETWORK_READY_IP4, (void*)prepare_network_details_msg(nwid)); + if (tap->hasIpv4Addr() && _lwip_is_netif_up(tap->netif4)) { + _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP4, (void*)prepare_network_details_msg(nwid)); } - if (tap->hasIpv6Addr() && lwip_is_netif_up(tap->netif6)) { - postEvent(ZTS_EVENT_NETWORK_READY_IP6, (void*)prepare_network_details_msg(nwid)); + if (tap->hasIpv6Addr() && _lwip_is_netif_up(tap->netif6)) { + _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP6, (void*)prepare_network_details_msg(nwid)); } // In addition to the READY messages, send one OK message - postEvent(ZTS_EVENT_NETWORK_OK, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_OK, (void*)prepare_network_details_msg(nwid)); break; case ZT_NETWORK_STATUS_ACCESS_DENIED: - postEvent(ZTS_EVENT_NETWORK_ACCESS_DENIED, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_ACCESS_DENIED, (void*)prepare_network_details_msg(nwid)); break; default: break; @@ -879,12 +820,12 @@ public: if (pl->peers[i].pathCount > 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - postEvent(ZTS_EVENT_PEER_P2P, (void*)pd); + _enqueueEvent(ZTS_EVENT_PEER_DIRECT, (void*)pd); } if (pl->peers[i].pathCount == 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - postEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); + _enqueueEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); } } // Previously known peer, update status @@ -892,12 +833,12 @@ public: if ((peerCache[pl->peers[i].address] == false) && pl->peers[i].pathCount > 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - postEvent(ZTS_EVENT_PEER_P2P, (void*)pd); + _enqueueEvent(ZTS_EVENT_PEER_DIRECT, (void*)pd); } if ((peerCache[pl->peers[i].address] == true) && pl->peers[i].pathCount == 0) { pd = new zts_peer_details; memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - postEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); + _enqueueEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); } } // Update our cache with most recently observed path count @@ -938,7 +879,7 @@ public: if (pl) { for(unsigned long i=0;ipeerCount;++i) { if (pl->peers[i].address == id) { - status = pl->peers[i].pathCount > 0 ? ZTS_EVENT_PEER_P2P : ZTS_EVENT_PEER_RELAY; + status = pl->peers[i].pathCount > 0 ? ZTS_EVENT_PEER_DIRECT : ZTS_EVENT_PEER_RELAY; break; } } @@ -967,7 +908,7 @@ public: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; case ZT_STATE_OBJECT_NETWORK_CONFIG: - if (_network_caching_enabled) { + if (allowNetworkCaching) { OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "networks.d",_homePath.c_str()); OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.16llx.conf",dirname,(unsigned long long)id[0]); secure = true; @@ -977,7 +918,7 @@ public: } break; case ZT_STATE_OBJECT_PEER: - if (_peer_caching_enabled) { + if (allowPeerCaching) { OSUtils::ztsnprintf(dirname,sizeof(dirname),"%s" ZT_PATH_SEPARATOR_S "peers.d",_homePath.c_str()); OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "%.10llx.peer",dirname,(unsigned long long)id[0]); } @@ -1032,7 +973,7 @@ public: OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str()); break; case ZT_STATE_OBJECT_NETWORK_CONFIG: - if (_network_caching_enabled) { + if (allowNetworkCaching) { OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]); } else { @@ -1040,7 +981,7 @@ public: } break; case ZT_STATE_OBJECT_PEER: - if (_peer_caching_enabled) { + if (allowPeerCaching) { OSUtils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "peers.d" ZT_PATH_SEPARATOR_S "%.10llx.peer",_homePath.c_str(),(unsigned long long)id[0]); } break; @@ -1249,32 +1190,121 @@ public: }; static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf) -{ return reinterpret_cast(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); } +{ return reinterpret_cast(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); } static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData) -{ reinterpret_cast(uptr)->nodeEventCallback(event,metaData); } +{ reinterpret_cast(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(uptr)->nodeStatePutFunction(type,id,data,len); } +{ reinterpret_cast(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(uptr)->nodeStateGetFunction(type,id,data,maxlen); } +{ return reinterpret_cast(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(uptr)->nodeWirePacketSendFunction(localSocket,addr,data,len,ttl); } +{ return reinterpret_cast(uptr)->nodeWirePacketSendFunction(localSocket,addr,data,len,ttl); } static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) -{ reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); } +{ reinterpret_cast(uptr)->nodeVirtualNetworkFrameFunction(nwid,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(uptr)->nodePathCheckFunction(ztaddr,localSocket,remoteAddr); } +{ return reinterpret_cast(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(uptr)->nodePathLookupFunction(ztaddr,family,result); } +{ return reinterpret_cast(uptr)->nodePathLookupFunction(ztaddr,family,result); } static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) -{ reinterpret_cast(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } +{ reinterpret_cast(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); } -} // anonymous namespace -std::string OneService::platformDefaultHomePath() +std::string NodeService::platformDefaultHomePath() { return OSUtils::platformDefaultHomePath(); } -OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } -OneService::~OneService() {} +NodeService *NodeService::newInstance(const char *hp,unsigned int port) { return new NodeServiceImpl(hp,port); } +NodeService::~NodeService() {} + +////////////////////////////////////////////////////////////////////////////// +// Service // +////////////////////////////////////////////////////////////////////////////// + +NodeService *service; + +// Lock to guard access to ZeroTier core service +Mutex serviceLock; + +// Starts a ZeroTier NodeService background thread +#if defined(__WINDOWS__) +DWORD WINAPI _runNodeService(LPVOID arg) +#else +void *_runNodeService(void *arg) +#endif +{ +#if defined(__APPLE__) + pthread_setname_np(ZTS_SERVICE_THREAD_NAME); +#endif + struct serviceParameters *params = (struct serviceParameters *)arg; + int err; + try { + std::vector hpsp(OSUtils::split(params->path.c_str(), ZT_PATH_SEPARATOR_S,"","")); + std::string ptmp; + if (params->path[0] == ZT_PATH_SEPARATOR) { + ptmp.push_back(ZT_PATH_SEPARATOR); + } + for (std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { + if (ptmp.length() > 0) { + ptmp.push_back(ZT_PATH_SEPARATOR); + } + ptmp.append(*pi); + if ((*pi != ".")&&(*pi != "..")) { + if (OSUtils::mkdir(ptmp) == false) { + DEBUG_ERROR("home path does not exist, and could not create"); + err = true; + perror("error\n"); + } + } + } + for(;;) { + serviceLock.lock(); + service = NodeService::newInstance(params->path.c_str(),params->port); + service->_userProvidedPort = params->port; + service->_userProvidedPath = params->path; + serviceLock.unlock(); + switch(service->run()) { + case NodeService::ONE_STILL_RUNNING: + case NodeService::ONE_NORMAL_TERMINATION: + _enqueueEvent(ZTS_EVENT_NODE_NORMAL_TERMINATION,NULL); + break; + case NodeService::ONE_UNRECOVERABLE_ERROR: + DEBUG_ERROR("fatal error: %s", service->fatalErrorMessage().c_str()); + err = true; + _enqueueEvent(ZTS_EVENT_NODE_UNRECOVERABLE_ERROR,NULL); + break; + case NodeService::ONE_IDENTITY_COLLISION: { + err = true; + delete service; + service = (NodeService *)0; + std::string oldid; + OSUtils::readFile((params->path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); + if (oldid.length()) { + OSUtils::writeFile((params->path + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); + OSUtils::rm((params->path + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); + OSUtils::rm((params->path + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); + } + _enqueueEvent(ZTS_EVENT_NODE_IDENTITY_COLLISION,NULL); + } continue; // restart! + } + break; // terminate loop -- normally we don't keep restarting + } + serviceLock.lock(); + _clrState(ZTS_STATE_NODE_RUNNING); + delete service; + service = (NodeService *)0; + serviceLock.unlock(); + _enqueueEvent(ZTS_EVENT_NODE_DOWN,NULL); + } catch ( ... ) { + DEBUG_ERROR("unexpected exception starting ZeroTier instance"); + } + delete params; + zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL*2); + _clrState(ZTS_STATE_CALLBACKS_RUNNING); +#ifndef __WINDOWS__ + pthread_exit(0); +#endif + return NULL; +} } // namespace ZeroTier diff --git a/src/Service.hpp b/src/NodeService.hpp similarity index 75% rename from src/Service.hpp rename to src/NodeService.hpp index b111d5f..43e4ce8 100644 --- a/src/Service.hpp +++ b/src/NodeService.hpp @@ -11,33 +11,49 @@ */ /****/ -#ifndef ZT_ONESERVICE_HPP -#define ZT_ONESERVICE_HPP +/** + * @file + * + * Header for ZeroTier Node Service (a distant relative of OneService) + */ + +#ifndef ZT_NODE_SERVICE_HPP +#define ZT_NODE_SERVICE_HPP #include #include -#ifdef SDK_JNI -#include +#include "Node.hpp" +#include "InetAddress.hpp" +#include "Mutex.hpp" +#include "ZeroTierSockets.h" + +#define ZTS_SERVICE_THREAD_NAME "ZTServiceThread" +#define ZTS_EVENT_CALLBACK_THREAD_NAME "ZTEventCallbackThread" +// Interface metric for ZeroTier taps -- this ensures that if we are on WiFi and also +// bridged via ZeroTier to the same LAN traffic will (if the OS is sane) prefer WiFi. +#define ZT_IF_METRIC 5000 +// How often to check for new multicast subscriptions on a tap device +#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 +// How often to check for local interface addresses +#define ZT_LOCAL_INTERFACE_CHECK_INTERVAL 60000 + +#ifdef __WINDOWS__ +#include #endif namespace ZeroTier { -class VirtualTap; -// Use the virtual libzt endpoint instead of a tun/tap port driver -namespace ZeroTier { typedef VirtualTap EthernetTap; } - -// Forward declaration so we can avoid dragging everything in -struct InetAddress; -class Node; - /** * Local service for ZeroTier One as system VPN/NFV provider */ -class OneService +class NodeService { public: + uint16_t _userProvidedPort; + std::string _userProvidedPath; + /** * Returned by node main if/when it terminates */ @@ -109,9 +125,9 @@ public: * @param hp Home path * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port) */ - static OneService *newInstance(const char *hp,unsigned int port); + static NodeService *newInstance(const char *hp,unsigned int port); - virtual ~OneService(); + virtual ~NodeService(); /** * Execute the service main I/O loop until terminated @@ -178,13 +194,28 @@ public: inline bool isRunning() const { return (this->reasonForTermination() == ONE_STILL_RUNNING); } protected: - OneService() {} + NodeService() {} private: - OneService(const OneService &one) {} - inline OneService &operator=(const OneService &one) { return *this; } + NodeService(const NodeService &one) {} + inline NodeService &operator=(const NodeService &one) { return *this; } }; +struct serviceParameters +{ + int port; + std::string path; +}; + +#ifdef __WINDOWS__ +DWORD WINAPI _runNodeService(LPVOID arg); +#else +/** + * NodeService thread + */ +void *_runNodeService(void *arg); +#endif + } // namespace ZeroTier #endif diff --git a/src/Options.h b/src/Options.h deleted file mode 100644 index 74708dd..0000000 --- a/src/Options.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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 LIBZT_OPTIONS_H -#define LIBZT_OPTIONS_H - -////////////////////////////////////////////////////////////////////////////// -// Callbacks // -////////////////////////////////////////////////////////////////////////////// - -/** - * The maximum number of un-processed callback messages - */ -#define ZTS_CALLBACK_MSG_QUEUE_LEN 256 - -////////////////////////////////////////////////////////////////////////////// -// Timing // -////////////////////////////////////////////////////////////////////////////// - -/** - * How often callback messages are assembled and/or sent - */ -#define ZTS_CALLBACK_PROCESSING_INTERVAL 25 - -/** - * Polling interval (in ms) for fds wrapped in the Phy I/O loop - */ -#define ZTS_TAP_THREAD_POLLING_INTERVAL 50 - -#define ZTS_HOUSEKEEPING_INTERVAL 50 - -/** - * By how much thread I/O and callback loop delays are multiplied (unitless) - */ -#define ZTS_HIBERNATION_MULTIPLIER 50 - -////////////////////////////////////////////////////////////////////////////// -// Threading // -////////////////////////////////////////////////////////////////////////////// - -#define SERVICE_THREAD_NICENESS 0 // -10 -#define CALLBACK_THREAD_NICENESS 0 // 10 -#define LWIP_DRIVER_THREAD_NICENESS 0 // 10 -#define TCPIP_THREAD_NICENESS 0 // -10 -#define TAP_THREAD_NICENESS 0 // 10 - -#define ZTS_SERVICE_THREAD_NAME "ZeroTierServiceThread" -#define ZTS_EVENT_CALLBACK_THREAD_NAME "ZeroTierEventCallbackThread" -#define ZTS_LWIP_DRIVER_THREAD_NAME "lwipDriver" - -////////////////////////////////////////////////////////////////////////////// -// lwIP behaviour (tcpip driver) // -////////////////////////////////////////////////////////////////////////////// - -/** - * How many frames are handled per call from core - */ -#define LWIP_FRAMES_HANDLED_PER_CORE_CALL 16 - -/** - * How often the lwIP tcpip thread callback checks for incoming frames - */ -#define LWIP_DRIVER_LOOP_INTERVAL 250 - -/** - * Number of packets that can be queued for ingress into the lwIP core - */ -#define ZTS_LWIP_MAX_RX_QUEUE_LEN 1024 - -#endif \ No newline at end of file diff --git a/src/Sockets.cpp b/src/Sockets.cpp index 9adc0c2..7844c51 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -17,42 +17,41 @@ * ZeroTier Socket API */ -#include - #include "lwip/sockets.h" #include "lwip/def.h" +#include "lwip/inet.h" +#include "lwip/stats.h" -#include "ZeroTierConstants.h" -#include "Options.h" -#include "Debug.hpp" +#include "ZeroTierSockets.h" +#include "Events.hpp" #ifdef SDK_JNI #include #endif +extern int zts_errno; + namespace ZeroTier { -////////////////////////////////////////////////////////////////////////////// -// ZeroTier Socket API // -////////////////////////////////////////////////////////////////////////////// - -extern bool _run_service; -extern bool _run_lwip_tcpip; +extern uint8_t _serviceStateFlags; #ifdef __cplusplus extern "C" { #endif #ifdef SDK_JNI -void ss2zta(JNIEnv *env, struct sockaddr_storage *ss, jobject addr); -void zta2ss(JNIEnv *env, struct sockaddr_storage *ss, jobject addr); -void ztfdset2fdset(JNIEnv *env, int nfds, jobject src_ztfd_set, fd_set *dest_fd_set); -void fdset2ztfdset(JNIEnv *env, int nfds, fd_set *src_fd_set, jobject dest_ztfd_set); +void ss2zta(JNIEnv *env, struct zts_sockaddr_storage *ss, jobject addr); +void zta2ss(JNIEnv *env, struct zts_sockaddr_storage *ss, jobject addr); +void ztfdset2fdset(JNIEnv *env, int nfds, jobject src_ztfd_set, zts_fd_set *dest_fd_set); +void fdset2ztfdset(JNIEnv *env, int nfds, zts_fd_set *src_fd_set, jobject dest_ztfd_set); #endif -int zts_socket(int socket_family, int socket_type, int protocol) +int zts_socket(const int socket_family, const int socket_type, const int protocol) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_socket(socket_family, socket_type, protocol); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_socket(socket_family, socket_type, protocol); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_socket( @@ -63,53 +62,62 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_socket( } #endif -int zts_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) +int zts_connect(int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen) { if (!addr) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (addrlen > (int)sizeof(struct sockaddr_storage) || addrlen < (int)sizeof(struct sockaddr_in)) { - return ZTS_ERR_INVALID_ARG; + if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) { + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_connect(fd, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_connect(fd, (sockaddr*)addr, addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_connect( JNIEnv *env, jobject thisObj, jint fd, jobject addr) { - struct sockaddr_storage ss; + struct zts_sockaddr_storage ss; zta2ss(env, &ss, addr); - socklen_t addrlen = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - int retval = zts_connect(fd, (struct sockaddr *)&ss, addrlen); + socklen_t addrlen = ss.ss_family == ZTS_AF_INET ? sizeof(struct zts_sockaddr_in) : sizeof(struct zts_sockaddr_in6); + int retval = zts_connect(fd, (struct zts_sockaddr *)&ss, addrlen); return retval > -1 ? retval : -(zts_errno); } #endif -int zts_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +int zts_bind(int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen) { if (!addr) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (addrlen > (int)sizeof(struct sockaddr_storage) || addrlen < (int)sizeof(struct sockaddr_in)) { - return ZTS_ERR_INVALID_ARG; + if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) { + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_bind(fd, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_bind(fd, (sockaddr*)addr, addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_bind( JNIEnv *env, jobject thisObj, jint fd, jobject addr) { - struct sockaddr_storage ss; + struct zts_sockaddr_storage ss; zta2ss(env, &ss, addr); - socklen_t addrlen = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - int retval = zts_bind(fd, (struct sockaddr*)&ss, addrlen); + zts_socklen_t addrlen = ss.ss_family == ZTS_AF_INET ? sizeof(struct zts_sockaddr_in) : sizeof(struct zts_sockaddr_in6); + int retval = zts_bind(fd, (struct zts_sockaddr*)&ss, addrlen); return retval > -1 ? retval : -(zts_errno); } #endif int zts_listen(int fd, int backlog) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_listen(fd, backlog); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_listen(fd, backlog); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_listen( @@ -120,26 +128,32 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_listen( } #endif -int zts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +int zts_accept(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_accept(fd, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_accept(fd, (sockaddr*)addr, (socklen_t*)addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_accept( JNIEnv *env, jobject thisObj, jint fd, jobject addr, jint port) { - struct sockaddr_storage ss; - socklen_t addrlen = sizeof(struct sockaddr_storage); - int retval = zts_accept(fd, (struct sockaddr *)&ss, &addrlen); + struct zts_sockaddr_storage ss; + zts_socklen_t addrlen = sizeof(struct zts_sockaddr_storage); + int retval = zts_accept(fd, (zts_sockaddr*)&ss, &addrlen); ss2zta(env, &ss, addr); return retval > -1 ? retval : -(zts_errno); } #endif #if defined(__linux__) -int zts_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags) +int zts_accept4(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen, int flags) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_INVALID_OP : ZTS_ERR_INVALID_OP; + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return ZTS_ERR_SERVICE; // TODO } #endif #ifdef SDK_JNI @@ -147,18 +161,21 @@ int zts_accept4(int fd, struct sockaddr *addr, socklen_t *addrlen, int flags) JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_accept4( JNIEnv *env, jobject thisObj, jint fd, jobject addr, jint port, jint flags) { - struct sockaddr_storage ss; - socklen_t addrlen = sizeof(struct sockaddr_storage); - int retval = zts_accept4(fd, (struct sockaddr *)&ss, &addrlen, flags); + struct zts_sockaddr_storage ss; + zts_socklen_t addrlen = sizeof(struct zts_sockaddr_storage); + int retval = zts_accept4(fd, (struct zts_sockaddr *)&ss, &addrlen, flags); ss2zta(env, &ss, addr); return retval > -1 ? retval : -(zts_errno); } #endif #endif -int zts_setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +int zts_setsockopt(int fd, int level, int optname, const void *optval,zts_socklen_t optlen) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_setsockopt(fd, level, optname, optval, optlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_setsockopt(fd, level, optname, optval, optlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_setsockopt( @@ -166,7 +183,7 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_setsockopt( { jclass c = env->GetObjectClass(optval); if (!c) { - return ZTS_ERR_INVALID_OP; + return ZTS_ERR_SERVICE; } int optval_int = -1; @@ -206,9 +223,12 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_setsockopt( } #endif -int zts_getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +int zts_getsockopt(int fd, int level, int optname, void *optval, zts_socklen_t *optlen) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_getsockopt(fd, level, optname, optval, optlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_getsockopt(fd, level, optname, optval, (socklen_t*)optlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getsockopt( @@ -216,15 +236,15 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getsockopt( { jclass c = env->GetObjectClass(optval); if (!c) { - return ZTS_ERR_INVALID_OP; + return ZTS_ERR_SERVICE; } int optval_int = 0; - socklen_t optlen; // Intentionally not used + zts_socklen_t optlen; // Intentionally not used int retval; if (optname == SO_RCVTIMEO) { - struct timeval tv; + struct zts_timeval tv; optlen = sizeof(tv); retval = zts_getsockopt(fd, level, optname, &tv, &optlen); // Convert seconds and microseconds back to milliseconds @@ -261,91 +281,61 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getsockopt( } #endif -int zts_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) +int zts_getsockname(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen) { if (!addr) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (*addrlen > (int)sizeof(struct sockaddr_storage) || *addrlen < (int)sizeof(struct sockaddr_in)) { - return ZTS_ERR_INVALID_ARG; + if (*addrlen > (int)sizeof(struct zts_sockaddr_storage) || *addrlen < (int)sizeof(struct zts_sockaddr_in)) { + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_getsockname(fd, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_getsockname(fd, (sockaddr*)addr, (socklen_t*)addrlen); } #ifdef SDK_JNI JNIEXPORT jboolean JNICALL Java_com_zerotier_libzt_ZeroTier_getsockname(JNIEnv *env, jobject thisObj, jint fd, jobject addr) { - struct sockaddr_storage ss; - socklen_t addrlen = sizeof(struct sockaddr_storage); - int retval =zts_getsockname(fd, (struct sockaddr *)&ss, &addrlen); + struct zts_sockaddr_storage ss; + zts_socklen_t addrlen = sizeof(struct zts_sockaddr_storage); + int retval = zts_getsockname(fd, (struct zts_sockaddr *)&ss, &addrlen); ss2zta(env, &ss, addr); return retval > -1 ? retval : -(zts_errno); } #endif -int zts_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) +int zts_getpeername(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen) { if (!addr) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (*addrlen > (int)sizeof(struct sockaddr_storage) || *addrlen < (int)sizeof(struct sockaddr_in)) { - return ZTS_ERR_INVALID_ARG; + if (*addrlen > (int)sizeof(struct zts_sockaddr_storage) || *addrlen < (int)sizeof(struct zts_sockaddr_in)) { + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_getpeername(fd, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_getpeername(fd, (sockaddr*)addr, (socklen_t*)addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_getpeername(JNIEnv *env, jobject thisObj, jint fd, jobject addr) { - struct sockaddr_storage ss; - int retval = zts_getpeername(fd, (struct sockaddr *)&ss, (socklen_t *)sizeof(struct sockaddr_storage)); + struct zts_sockaddr_storage ss; + int retval = zts_getpeername(fd, (struct zts_sockaddr *)&ss, (zts_socklen_t*)sizeof(struct zts_sockaddr_storage)); ss2zta(env, &ss, addr); return retval > -1 ? retval : -(zts_errno); } #endif -int zts_gethostname(char *name, size_t len) -{ - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_INVALID_OP : ZTS_ERR_INVALID_OP; // TODO -} -#ifdef SDK_JNI -#endif - -int zts_sethostname(const char *name, size_t len) -{ - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_INVALID_OP : ZTS_ERR_INVALID_OP; // TODO -} -#ifdef SDK_JNI -#endif - -/* -struct hostent *zts_gethostbyname(const char *name) -{ - return (struct hostent *)((!_run_service || !_run_lwip_tcpip) ? NULL : NULL); - // TODO: Test thread safety - char buf[256]; - int buflen = 256; - int h_err = 0; - struct hostent hret; - struct hostent **result = NULL; - int err = 0; - if ((err = lwip_gethostbyname_r(name, &hret, buf, buflen, result, &h_err)) != 0) { - DEBUG_ERROR("err = %d", err); - DEBUG_ERROR("h_err = %d", h_err); - errno = h_err; - return NULL; // failure - } - return *result; - - return lwip_gethostbyname(name); -} -#ifdef SDK_JNI -#endif -*/ - int zts_close(int fd) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_close(fd); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_close(fd); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_close( @@ -355,22 +345,25 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_close( } #endif -int zts_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, - struct timeval *timeout) +int zts_select(int nfds, zts_fd_set *readfds, zts_fd_set *writefds, zts_fd_set *exceptfds, + struct zts_timeval *timeout) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_select(nfds, readfds, writefds, exceptfds, timeout); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_select(nfds, (fd_set*)readfds, (fd_set*)writefds, (fd_set*)exceptfds, (timeval*)timeout); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_select(JNIEnv *env, jobject thisObj, jint nfds, jobject readfds, jobject writefds, jobject exceptfds, jint timeout_sec, jint timeout_usec) { - struct timeval _timeout; + struct zts_timeval _timeout; _timeout.tv_sec = timeout_sec; _timeout.tv_usec = timeout_usec; - fd_set _readfds, _writefds, _exceptfds; - fd_set *r = NULL; - fd_set *w = NULL; - fd_set *e = NULL; + zts_fd_set _readfds, _writefds, _exceptfds; + zts_fd_set *r = NULL; + zts_fd_set *w = NULL; + zts_fd_set *e = NULL; if (readfds) { r = &_readfds; ztfdset2fdset(env, nfds, readfds, &_readfds); @@ -411,7 +404,10 @@ int zts_fcntl(int fd, int cmd, int flags) translated_flags = 1; } #endif - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_fcntl(fd, cmd, translated_flags); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_fcntl(fd, cmd, translated_flags); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_fcntl( @@ -422,12 +418,24 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_fcntl( } #endif +// TODO: JNI version +int zts_poll(struct zts_pollfd *fds, nfds_t nfds, int timeout) +{ + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_poll((pollfd*)fds, nfds, timeout); +} + int zts_ioctl(int fd, unsigned long request, void *argp) { if (!argp) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_ioctl(fd, request, argp); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_ioctl(fd, request, argp); } #ifdef SDK_JNI JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_ioctl( @@ -435,13 +443,12 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_ioctl( { int retval = ZTS_ERR_OK; if (request == FIONREAD) { - // DEBUG_ERROR("FIONREAD"); int bytesRemaining = 0; retval = zts_ioctl(fd, request, &bytesRemaining); // set value in general object jclass c = env->GetObjectClass(argp); if (!c) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } jfieldID fid = env->GetFieldID(c, "integer", "I"); env->SetIntField(argp, fid, bytesRemaining); @@ -449,7 +456,6 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_ioctl( if (request == FIONBIO) { // TODO: double check int meaninglessVariable = 0; - // DEBUG_ERROR("FIONBIO"); retval = zts_ioctl(fd, request, &meaninglessVariable); } return retval > -1 ? retval : -(zts_errno); @@ -459,9 +465,12 @@ JNIEXPORT int JNICALL Java_com_zerotier_libzt_ZeroTier_ioctl( ssize_t zts_send(int fd, const void *buf, size_t len, int flags) { if (!buf || len <= 0) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_send(fd, buf, len, flags); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_send(fd, buf, len, flags); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_send( @@ -475,25 +484,28 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_send( #endif ssize_t zts_sendto(int fd, const void *buf, size_t len, int flags, - const struct sockaddr *addr, socklen_t addrlen) + const struct zts_sockaddr *addr,zts_socklen_t addrlen) { if (!addr || !buf || len <= 0) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - if (addrlen > (int)sizeof(struct sockaddr_storage) || addrlen < (int)sizeof(struct sockaddr_in)) { - return ZTS_ERR_INVALID_ARG; + if (addrlen > (int)sizeof(struct zts_sockaddr_storage) || addrlen < (int)sizeof(struct zts_sockaddr_in)) { + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_sendto(fd, buf, len, flags, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_sendto(fd, buf, len, flags, (sockaddr*)addr, addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_sendto( JNIEnv *env, jobject thisObj, jint fd, jbyteArray buf, jint flags, jobject addr) { void *data = env->GetPrimitiveArrayCritical(buf, NULL); - struct sockaddr_storage ss; + struct zts_sockaddr_storage ss; zta2ss(env, &ss, addr); - socklen_t addrlen = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - int retval = zts_sendto(fd, data, env->GetArrayLength(buf), flags, (struct sockaddr *)&ss, addrlen); + zts_socklen_t addrlen = ss.ss_family == ZTS_AF_INET ? sizeof(struct zts_sockaddr_in) : sizeof(struct zts_sockaddr_in6); + int retval = zts_sendto(fd, data, env->GetArrayLength(buf), flags, (struct zts_sockaddr *)&ss, addrlen); env->ReleasePrimitiveArrayCritical(buf, data, 0); return retval > -1 ? retval : -(zts_errno); } @@ -501,7 +513,10 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_sendto( ssize_t zts_sendmsg(int fd, const struct msghdr *msg, int flags) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_sendmsg(fd, msg, flags); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_sendmsg(fd, msg, flags); } #ifdef SDK_JNI #endif @@ -509,9 +524,12 @@ ssize_t zts_sendmsg(int fd, const struct msghdr *msg, int flags) ssize_t zts_recv(int fd, void *buf, size_t len, int flags) { if (!buf) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_recv(fd, buf, len, flags); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_recv(fd, buf, len, flags); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recv(JNIEnv *env, jobject thisObj, @@ -525,49 +543,64 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recv(JNIEnv *env, jobjec #endif ssize_t zts_recvfrom(int fd, void *buf, size_t len, int flags, - struct sockaddr *addr, socklen_t *addrlen) + struct zts_sockaddr *addr, zts_socklen_t *addrlen) { if (!buf) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_recvfrom(fd, buf, len, flags, addr, addrlen); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_recvfrom(fd, buf, len, flags, (sockaddr*)addr, (socklen_t*)addrlen); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_recvfrom( JNIEnv *env, jobject thisObj, jint fd, jbyteArray buf, jint flags, jobject addr) { - socklen_t addrlen = sizeof(struct sockaddr_storage); - struct sockaddr_storage ss; + zts_socklen_t addrlen = sizeof(struct zts_sockaddr_storage); + struct zts_sockaddr_storage ss; void *data = env->GetPrimitiveArrayCritical(buf, NULL); - int retval = zts_recvfrom(fd, data, env->GetArrayLength(buf), flags, (struct sockaddr *)&ss, &addrlen); + int retval = zts_recvfrom(fd, data, env->GetArrayLength(buf), flags, (struct zts_sockaddr *)&ss, &addrlen); env->ReleasePrimitiveArrayCritical(buf, data, 0); ss2zta(env, &ss, addr); return retval > -1 ? retval : -(zts_errno); } #endif +// TODO: JNI version ssize_t zts_recvmsg(int fd, struct msghdr *msg, int flags) { - // Not currently implemented by stack - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : ZTS_ERR_GENERAL; + if (!msg) { + return ZTS_ERR_ARG; + } + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_recvmsg(fd, msg, flags); } #ifdef SDK_JNI #endif -int zts_read(int fd, void *buf, size_t len) +ssize_t zts_read(int fd, void *buf, size_t len) { if (!buf) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_read(fd, buf, len); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_read(fd, buf, len); } -int zts_read_offset(int fd, void *buf, size_t offset, size_t len) +ssize_t zts_read_offset(int fd, void *buf, size_t offset, size_t len) { if (!buf) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; + } + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; } char *cbuf = (char*)buf; - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_read(fd, &(cbuf[offset]), len); + return lwip_read(fd, &(cbuf[offset]), len); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_read(JNIEnv *env, jobject thisObj, @@ -596,12 +629,24 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_read_1length(JNIEnv *env } #endif -int zts_write(int fd, const void *buf, size_t len) +// TODO: JNI version +ssize_t zts_readv(int s, const struct zts_iovec *iov, int iovcnt) +{ + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_readv(s, (iovec*)iov, iovcnt); +} + +ssize_t zts_write(int fd, const void *buf, size_t len) { if (!buf || len <= 0) { - return ZTS_ERR_INVALID_ARG; + return ZTS_ERR_ARG; } - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_write(fd, buf, len); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_write(fd, buf, len); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_write__IB(JNIEnv *env, jobject thisObj, @@ -628,9 +673,21 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_write_1byte(JNIEnv *env, } #endif +// TODO: JNI version +ssize_t zts_writev(int fd, const struct zts_iovec *iov, int iovcnt) +{ + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_writev(fd, (iovec*)iov, iovcnt); +} + int zts_shutdown(int fd, int how) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : lwip_shutdown(fd, how); + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return lwip_shutdown(fd, how); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_shutdown( @@ -640,42 +697,213 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_shutdown( } #endif -int zts_add_dns_nameserver(struct sockaddr *addr) +int zts_add_dns_nameserver(struct zts_sockaddr *addr) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : -1; // TODO + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return ZTS_ERR_SERVICE; // TODO } #ifdef SDK_JNI #endif -int zts_del_dns_nameserver(struct sockaddr *addr) +int zts_del_dns_nameserver(struct zts_sockaddr *addr) { - return (!_run_service || !_run_lwip_tcpip) ? ZTS_ERR_SERVICE : -1; // TODO + if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { + return ZTS_ERR_SERVICE; + } + return ZTS_ERR_SERVICE; // TODO } #ifdef SDK_JNI #endif +uint16_t zts_htons(uint16_t n) +{ + return lwip_htons(n); +} + +uint32_t zts_htonl(uint32_t n) +{ + return lwip_htonl(n); +} + +uint16_t zts_ntohs(uint16_t n) +{ + return lwip_htons(n); +} + +uint32_t zts_ntohl(uint32_t n) +{ + return lwip_htonl(n); +} + +const char *zts_inet_ntop(int af, const void *src, char *dst,zts_socklen_t size) +{ + return lwip_inet_ntop(af,src,dst,size); +} + +int zts_inet_pton(int af, const char *src, void *dst) +{ + return lwip_inet_pton(af,src,dst); +} + +uint32_t zts_inet_addr(const char *cp) +{ + return ipaddr_addr(cp); +} + +////////////////////////////////////////////////////////////////////////////// +// Statistics // +////////////////////////////////////////////////////////////////////////////// + +extern struct stats_ lwip_stats; + +int zts_get_all_stats(struct zts_stats *statsDest) +{ +#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 +} #ifdef SDK_JNI -void ztfdset2fdset(JNIEnv *env, int nfds, jobject src_ztfd_set, fd_set *dest_fd_set) + // No implementation for JNI +#endif + +int zts_get_protocol_stats(int protocolType, void *protoStatsDest) +{ +#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 +} +#ifdef SDK_JNI +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1protocol_1stats( + JNIEnv *env, jobject thisObj, jint protocolType, jobject protoStatsObj) +{ + struct stats_proto stats; + int retval = zts_get_protocol_stats(protocolType, &stats); + // Copy stats into Java object + jclass c = env->GetObjectClass(protoStatsObj); + if (!c) { + return ZTS_ERR_ARG; + } + jfieldID fid; + fid = env->GetFieldID(c, "xmit", "I"); + env->SetIntField(protoStatsObj, fid, stats.xmit); + fid = env->GetFieldID(c, "recv", "I"); + env->SetIntField(protoStatsObj, fid, stats.recv); + fid = env->GetFieldID(c, "fw", "I"); + env->SetIntField(protoStatsObj, fid, stats.fw); + fid = env->GetFieldID(c, "drop", "I"); + env->SetIntField(protoStatsObj, fid, stats.drop); + fid = env->GetFieldID(c, "chkerr", "I"); + env->SetIntField(protoStatsObj, fid, stats.chkerr); + fid = env->GetFieldID(c, "lenerr", "I"); + env->SetIntField(protoStatsObj, fid, stats.lenerr); + fid = env->GetFieldID(c, "memerr", "I"); + env->SetIntField(protoStatsObj, fid, stats.memerr); + fid = env->GetFieldID(c, "rterr", "I"); + env->SetIntField(protoStatsObj, fid, stats.rterr); + fid = env->GetFieldID(c, "proterr", "I"); + env->SetIntField(protoStatsObj, fid, stats.proterr); + fid = env->GetFieldID(c, "opterr", "I"); + env->SetIntField(protoStatsObj, fid, stats.opterr); + fid = env->GetFieldID(c, "err", "I"); + env->SetIntField(protoStatsObj, fid, stats.err); + fid = env->GetFieldID(c, "cachehit", "I"); + env->SetIntField(protoStatsObj, fid, stats.cachehit); + return retval; +} +#endif + +#ifdef SDK_JNI +void ztfdset2fdset(JNIEnv *env, int nfds, jobject src_ztfd_set, zts_fd_set *dest_fd_set) { jclass c = env->GetObjectClass(src_ztfd_set); if (!c) { return; } - FD_ZERO(dest_fd_set); + ZTS_FD_ZERO(dest_fd_set); jfieldID fid = env->GetFieldID(c, "fds_bits", "[B"); jobject fdData = env->GetObjectField (src_ztfd_set, fid); jbyteArray * arr = reinterpret_cast(&fdData); char *data = (char*)env->GetByteArrayElements(*arr, NULL); for (int i=0; iReleaseByteArrayElements(*arr, (jbyte*)data, 0); return; } -void fdset2ztfdset(JNIEnv *env, int nfds, fd_set *src_fd_set, jobject dest_ztfd_set) +void fdset2ztfdset(JNIEnv *env, int nfds, zts_fd_set *src_fd_set, jobject dest_ztfd_set) { jclass c = env->GetObjectClass(dest_ztfd_set); if (!c) { @@ -686,7 +914,7 @@ void fdset2ztfdset(JNIEnv *env, int nfds, fd_set *src_fd_set, jobject dest_ztfd_ jbyteArray * arr = reinterpret_cast(&fdData); char *data = (char*)env->GetByteArrayElements(*arr, NULL); for (int i=0; iGetObjectClass(addr); if (!c) { return; } - if(ss->ss_family == AF_INET) + if(ss->ss_family == ZTS_AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in*)ss; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)ss; jfieldID fid = env->GetFieldID(c, "_port", "I"); env->SetIntField(addr, fid, lwip_ntohs(in4->sin_port)); fid = env->GetFieldID(c,"_family", "I"); @@ -720,9 +948,9 @@ void ss2zta(JNIEnv *env, struct sockaddr_storage *ss, jobject addr) return; } - if(ss->ss_family == AF_INET6) + if(ss->ss_family == ZTS_AF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)ss; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)ss; jfieldID fid = env->GetFieldID(c, "_port", "I"); env->SetIntField(addr, fid, lwip_ntohs(in6->sin6_port)); fid = env->GetFieldID(c,"_family", "I"); @@ -737,7 +965,7 @@ void ss2zta(JNIEnv *env, struct sockaddr_storage *ss, jobject addr) } } -void zta2ss(JNIEnv *env, struct sockaddr_storage *ss, jobject addr) +void zta2ss(JNIEnv *env, struct zts_sockaddr_storage *ss, jobject addr) { jclass c = env->GetObjectClass(addr); if (!c) { @@ -745,12 +973,12 @@ void zta2ss(JNIEnv *env, struct sockaddr_storage *ss, jobject addr) } jfieldID fid = env->GetFieldID(c, "_family", "I"); int family = env->GetIntField(addr, fid); - if (family == AF_INET) + if (family == ZTS_AF_INET) { - struct sockaddr_in *in4 = (struct sockaddr_in*)ss; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)ss; fid = env->GetFieldID(c, "_port", "I"); in4->sin_port = lwip_htons(env->GetIntField(addr, fid)); - in4->sin_family = AF_INET; + in4->sin_family = ZTS_AF_INET; fid = env->GetFieldID(c, "_ip4", "[B"); jobject ipData = env->GetObjectField (addr, fid); jbyteArray * arr = reinterpret_cast(&ipData); @@ -759,13 +987,13 @@ void zta2ss(JNIEnv *env, struct sockaddr_storage *ss, jobject addr) env->ReleaseByteArrayElements(*arr, (jbyte*)data, 0); return; } - if (family == AF_INET6) + if (family == ZTS_AF_INET6) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)ss; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)ss; jfieldID fid = env->GetFieldID(c, "_port", "I"); in6->sin6_port = lwip_htons(env->GetIntField(addr, fid)); fid = env->GetFieldID(c,"_family", "I"); - in6->sin6_family = AF_INET6; + in6->sin6_family = ZTS_AF_INET6; fid = env->GetFieldID(c, "_ip6", "[B"); jobject ipData = env->GetObjectField (addr, fid); jbyteArray * arr = reinterpret_cast(&ipData); diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index 88b6861..fd1b32a 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -14,28 +14,41 @@ /** * @file * - * Virtual Ethernet tap device + * Virtual ethernet tap device and combined network stack driver */ -#include "VirtualTap.hpp" -#include "Phy.hpp" -#include "Node.hpp" -//#include "OSUtils.hpp" - -#include "Service.hpp" +#include "MAC.hpp" #include "Mutex.hpp" -#include "lwipDriver.hpp" -#include "ZeroTier.h" +#include "InetAddress.hpp" +#include "MulticastGroup.hpp" -#ifdef _MSC_VER +#include "lwip/netif.h" +#include "lwip/etharp.h" +#include "lwip/sys.h" +#include "lwip/ethip6.h" +#include "lwip/tcpip.h" +#include "netif/ethernet.h" + +#ifdef LWIP_STATS +#include "lwip/stats.h" +#endif + +#include "VirtualTap.hpp" +#include "ZeroTierSockets.h" +#include "Events.hpp" +#include "Debug.hpp" + +#if defined(__WINDOWS__) +#include #include "Synchapi.h" #endif +#define ZTS_TAP_THREAD_POLLING_INTERVAL 50 +#define LWIP_DRIVER_LOOP_INTERVAL 250 + namespace ZeroTier { -class VirtualTap; -extern OneService *service; -extern void postEvent(int eventCode, void *arg); +extern void _enqueueEvent(int16_t eventCode, void *arg = NULL); /** * A virtual tap device. The ZeroTier core service creates one of these for each @@ -66,7 +79,7 @@ VirtualTap::VirtualTap( memset(vtap_full_name, 0, sizeof(vtap_full_name)); snprintf(vtap_full_name, sizeof(vtap_full_name), "libzt%llx", (unsigned long long)_nwid); _dev = vtap_full_name; -#ifndef _WIN32 +#ifndef __WINDOWS__ ::pipe(_shutdownSignalPipe); #endif // Start virtual tap thread and stack I/O loops @@ -77,18 +90,18 @@ VirtualTap::~VirtualTap() { struct zts_network_details *nd = new zts_network_details; nd->nwid = _nwid; - postEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); + _enqueueEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); _run = false; -#ifndef _WIN32 +#ifndef __WINDOWS__ ::write(_shutdownSignalPipe[1],"\0",1); #endif _phy.whack(); - lwip_remove_netif(netif4); + _lwip_remove_netif(netif4); netif4 = NULL; - lwip_remove_netif(netif6); + _lwip_remove_netif(netif6); netif6 = NULL; Thread::join(_thread); -#ifndef _WIN32 +#ifndef __WINDOWS__ ::close(_shutdownSignalPipe[0]); ::close(_shutdownSignalPipe[1]); #endif @@ -156,7 +169,7 @@ bool VirtualTap::addIp(const InetAddress &ip) return false; } if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { - lwip_init_interface((void*)this, ip); + _lwip_init_interface((void*)this, ip); // TODO: Add ZTS_EVENT_ADDR_NEW ? _ips.push_back(ip); // Send callback message @@ -165,12 +178,12 @@ bool VirtualTap::addIp(const InetAddress &ip) if (ip.isV4()) { struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); memcpy(&(in4->sin_addr.s_addr), ip.rawIpData(), 4); - postEvent(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad); + _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad); } if (ip.isV6()) { struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); memcpy(&(in6->sin6_addr.s6_addr), ip.rawIpData(), 16); - postEvent(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad); + _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad); } std::sort(_ips.begin(),_ips.end()); } @@ -187,14 +200,14 @@ bool VirtualTap::removeIp(const InetAddress &ip) if (ip.isV4()) { struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); memcpy(&(in4->sin_addr.s_addr), ip.rawIpData(), 4); - postEvent(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad); + _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad); // FIXME: De-register from network stack } if (ip.isV6()) { // FIXME: De-register from network stack struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); memcpy(&(in6->sin6_addr.s6_addr), ip.rawIpData(), 16); - postEvent(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad); + _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad); } _ips.erase(i); } @@ -211,7 +224,7 @@ void VirtualTap::put(const MAC &from,const MAC &to,unsigned int etherType, const void *data,unsigned int len) { if (len <= _mtu && _enabled) { - lwip_eth_rx(this, from, to, etherType, data, len); + _lwip_eth_rx(this, from, to, etherType, data, len); } } @@ -220,19 +233,6 @@ std::string VirtualTap::deviceName() const return _dev; } -std::string VirtualTap::nodeId() const -{ - if (service) { - char id[16]; - memset(id, 0, sizeof(id)); - sprintf(id, "%llx", (unsigned long long)((OneService *)service)->getNode()->address()); - return std::string(id); - } - else { - return std::string("----------"); - } -} - void VirtualTap::setFriendlyName(const char *friendlyName) { DEBUG_INFO("%s", friendlyName); @@ -290,7 +290,7 @@ void VirtualTap::threadMain() if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) { break; } -#if defined(_WIN32) +#if defined(__WINDOWS__) Sleep(ZTS_TAP_THREAD_POLLING_INTERVAL); #else struct timespec sleepValue = {0}; @@ -300,11 +300,6 @@ void VirtualTap::threadMain() } } -void VirtualTap::Housekeeping() -{ - // -} - void VirtualTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, const struct sockaddr *from,void *data,unsigned long len) {} void VirtualTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) {} @@ -313,5 +308,461 @@ void VirtualTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,v void VirtualTap::phyOnTcpClose(PhySocket *sock,void **uptr) {} void VirtualTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} void VirtualTap::phyOnTcpWritable(PhySocket *sock,void **uptr) {} +void VirtualTap::phyOnUnixClose(PhySocket *sock,void **uptr) {} -} // namespace ZeroTier \ No newline at end of file +////////////////////////////////////////////////////////////////////////////// +// Netif driver code for lwIP network stack // +////////////////////////////////////////////////////////////////////////////// + +bool _has_exited = false; + +// Used to generate enumerated lwIP interface names +int netifCount = 0; + +// Lock to guard access to network stack state changes +Mutex stackLock; + +// Callback for when the TCPIP thread has been successfully started +static void _tcpip_init_done(void *arg) +{ + sys_sem_t *sem; + sem = (sys_sem_t *)arg; + _setState(ZTS_STATE_STACK_RUNNING); + _enqueueEvent(ZTS_EVENT_STACK_UP); + sys_sem_signal(sem); +} + +static void _main_lwip_driver_loop(void *arg) +{ +#if defined(__linux__) + pthread_setname_np(pthread_self(), ZTS_LWIP_DRIVER_THREAD_NAME); +#endif +#if defined(__APPLE__) + pthread_setname_np(ZTS_LWIP_DRIVER_THREAD_NAME); +#endif + sys_sem_t sem; + LWIP_UNUSED_ARG(arg); + if (sys_sem_new(&sem, 0) != ERR_OK) { + DEBUG_ERROR("failed to create semaphore"); + } + tcpip_init(_tcpip_init_done, &sem); + sys_sem_wait(&sem); + // Main loop + while(_getState(ZTS_STATE_STACK_RUNNING)) { + zts_delay_ms(LWIP_DRIVER_LOOP_INTERVAL); + } + _has_exited = true; + _enqueueEvent(ZTS_EVENT_STACK_DOWN); +} + +bool _lwip_is_up() +{ + Mutex::Lock _l(stackLock); + return _getState(ZTS_STATE_STACK_RUNNING); +} + +bool _lwip_has_previously_shutdown() +{ + Mutex::Lock _l(stackLock); + return _has_exited; +} + +void _lwip_driver_init() +{ + if (_lwip_is_up()) { + return; + } + if (_lwip_has_previously_shutdown()) { + return; + } + Mutex::Lock _l(stackLock); +#if defined(__WINDOWS__) + sys_init(); // Required for win32 init of critical sections +#endif + sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, _main_lwip_driver_loop, + NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); +} + +void _lwip_driver_shutdown() +{ + if (_lwip_has_previously_shutdown()) { + return; + } + Mutex::Lock _l(stackLock); + // Set flag to stop sending frames into the core + _clrState(ZTS_STATE_STACK_RUNNING); + // Wait until the main lwIP thread has exited + while (!_has_exited) { zts_delay_ms(LWIP_DRIVER_LOOP_INTERVAL); } + /* + if (tcpip_shutdown() == ERR_OK) { + sys_timeouts_free(); + } + */ +} + +void _lwip_remove_netif(void *netif) +{ + if (!netif) { + return; + } + struct netif *n = (struct netif*)netif; + LOCK_TCPIP_CORE(); + netif_remove(n); + netif_set_down(n); + netif_set_link_down(n); + UNLOCK_TCPIP_CORE(); +} + +err_t _lwip_eth_tx(struct netif *n, struct pbuf *p) +{ + if (!n) { + return ERR_IF; + } + struct pbuf *q; + char buf[ZT_MAX_MTU+32]; + char *bufptr; + int totalLength = 0; + + VirtualTap *tap = (VirtualTap*)n->state; + bufptr = buf; + for (q = p; q != NULL; q = q->next) { + memcpy(bufptr, q->payload, q->len); + bufptr += q->len; + totalLength += q->len; + } + struct eth_hdr *ethhdr; + ethhdr = (struct eth_hdr *)buf; + + MAC src_mac; + MAC dest_mac; + src_mac.setTo(ethhdr->src.addr, 6); + dest_mac.setTo(ethhdr->dest.addr, 6); + + char *data = buf + sizeof(struct eth_hdr); + int len = totalLength - sizeof(struct eth_hdr); + int proto = Utils::ntoh((uint16_t)ethhdr->type); + tap->_handler(tap->_arg, NULL, tap->_nwid, 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; +} + +void _lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int etherType, + const void *data, unsigned int len) +{ +#ifdef LWIP_STATS + stats_display(); +#endif + if (!_getState(ZTS_STATE_STACK_RUNNING)) { + return; + } + struct pbuf *p,*q; + struct eth_hdr ethhdr; + from.copyTo(ethhdr.src.addr, 6); + to.copyTo(ethhdr.dest.addr, 6); + 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); + if (!p) { + DEBUG_ERROR("dropped packet: unable to allocate memory for pbuf"); + return; + } + // First pbuf gets ethernet header at start + q = p; + if (q->len < sizeof(ethhdr)) { + pbuf_free(p); + p = NULL; + DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header"); + return; + } + // Copy frame data into pbuf + const char *dataptr = reinterpret_cast(data); + memcpy(q->payload,ðhdr,sizeof(ethhdr)); + int remainingPayloadSpace = q->len - sizeof(ethhdr); + memcpy((char*)q->payload + sizeof(ethhdr),dataptr,remainingPayloadSpace); + dataptr += remainingPayloadSpace; + // Remaining pbufs (if any) get rest of data + while ((q = q->next)) { + memcpy(q->payload,dataptr,q->len); + dataptr += q->len; + } + // Feed packet into stack + int err; + + if (Utils::ntoh(ethhdr.type) == 0x800 || Utils::ntoh(ethhdr.type) == 0x806) { + if ((err = ((struct netif *)tap->netif4)->input(p, (struct netif *)tap->netif4)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } + } + if (Utils::ntoh(ethhdr.type) == 0x86DD) { + if ((err = ((struct netif *)tap->netif6)->input(p, (struct netif *)tap->netif6)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } + } +} + +/* +static void print_netif_info(struct netif *n) { + DEBUG_INFO("n=%p, %c%c, %d, o=%p, o6=%p, mc=%x:%x:%x:%x:%x:%x, hwln=%d, st=%p, flgs=%d\n", + n, + n->name[0], + n->name[1], + n->mtu, + n->output, + n->output_ip6, + n->hwaddr[0], + n->hwaddr[1], + n->hwaddr[2], + n->hwaddr[3], + n->hwaddr[4], + n->hwaddr[5], + n->hwaddr_len, + n->state, + n->flags + ); +} +*/ + +bool _lwip_is_netif_up(void *n) +{ + if (!n) { + return false; + } + LOCK_TCPIP_CORE(); + bool result = netif_is_up((struct netif*)n); + UNLOCK_TCPIP_CORE(); + return result; +} + +/** + * Called when a netif is removed (ZTS_EVENT_NETIF_INTERFACE_REMOVED) + */ +#if LWIP_NETIF_REMOVE_CALLBACK +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; + uint64_t mac = 0; + memcpy(&mac, n->hwaddr, n->hwaddr_len); + struct zts_netif_details *ifd = new zts_netif_details; + ifd->nwid = tap->_nwid; + memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); + ifd->mac = lwip_htonl(ifd->mac) >> 16; + _enqueueEvent(ZTS_EVENT_NETIF_REMOVED, (void*)ifd); +} +#endif + +/** + * Called when a link is brought up or down (ZTS_EVENT_NETIF_LINK_UP, ZTS_EVENT_NETIF_LINK_DOWN) + */ +#if LWIP_NETIF_LINK_CALLBACK +static void _netif_link_callback(struct netif *n) +{ + // Called from core, no need to lock + if (!n || !n->state) { + return; + } + VirtualTap *tap = (VirtualTap *)n->state; + uint64_t mac = 0; + memcpy(&mac, n->hwaddr, n->hwaddr_len); + if (n->flags & NETIF_FLAG_LINK_UP) { + struct zts_netif_details *ifd = new zts_netif_details; + ifd->nwid = tap->_nwid; + memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); + ifd->mac = lwip_htonl(ifd->mac) >> 16; + _enqueueEvent(ZTS_EVENT_NETIF_LINK_UP, (void*)ifd); + } + if (n->flags & NETIF_FLAG_LINK_UP) { + struct zts_netif_details *ifd = new zts_netif_details; + ifd->nwid = tap->_nwid; + memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); + ifd->mac = lwip_htonl(ifd->mac) >> 16; + _enqueueEvent(ZTS_EVENT_NETIF_LINK_DOWN, (void*)ifd); + } +} +#endif + +void _lwip_set_callbacks(struct netif *n) +{ + if (!n) { + return; + } +#if LWIP_NETIF_STATUS_CALLBACK + // Not currently used + netif_set_status_callback(n, netif_status_callback); +#endif +#if LWIP_NETIF_REMOVE_CALLBACK + netif_set_remove_callback(n, netif_remove_callback); +#endif +#if LWIP_NETIF_LINK_CALLBACK + netif_set_link_callback(n, netif_link_callback); +#endif +} + +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 *ifd = new zts_netif_details; + ifd->nwid = tap->_nwid; + ifd->mtu = n->mtu; + memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); + ifd->mac = htonll(ifd->mac) >> 16; + return ifd; +} + +static err_t _netif_init4(struct netif *n) +{ + if (!n || !n->state) { + return ERR_IF; + } + // Called from netif code, no need to lock + n->hwaddr_len = 6; + n->name[0] = '4'; + n->name[1] = 'a'+netifCount; + n->linkoutput = _lwip_eth_tx; + n->output = etharp_output; + n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; + n->flags = NETIF_FLAG_BROADCAST + | NETIF_FLAG_ETHARP + | NETIF_FLAG_ETHERNET + | NETIF_FLAG_IGMP + | NETIF_FLAG_MLD6 + | NETIF_FLAG_LINK_UP + | NETIF_FLAG_UP; + n->hwaddr_len = sizeof(n->hwaddr); + VirtualTap *tap = (VirtualTap*)(n->state); + tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); + return ERR_OK; +} + +static err_t _netif_init6(struct netif *n) +{ + if (!n || !n->state) { + return ERR_IF; + } + n->hwaddr_len = sizeof(n->hwaddr); + VirtualTap *tap = (VirtualTap*)(n->state); + tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); + // Called from netif code, no need to lock + n->hwaddr_len = 6; + n->name[0] = '6'; + n->name[1] = 'a'+netifCount; + n->linkoutput = _lwip_eth_tx; + n->output_ip6 = ethip6_output; + n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; + n->flags = NETIF_FLAG_BROADCAST + | NETIF_FLAG_ETHARP + | NETIF_FLAG_ETHERNET + | NETIF_FLAG_IGMP + | NETIF_FLAG_MLD6 + | NETIF_FLAG_LINK_UP + | NETIF_FLAG_UP; + return ERR_OK; +} + +void _lwip_init_interface(void *tapref, const InetAddress &ip) +{ + char ipbuf[INET6_ADDRSTRLEN]; + char macbuf[ZTS_MAC_ADDRSTRLEN]; + + VirtualTap *vtap = (VirtualTap*)tapref; + struct netif *n = NULL; + bool isNewNetif = false; + + if (ip.isV4()) { + if (vtap->netif4) { + n = (struct netif*)vtap->netif4; + } + else { + n = new struct netif; + isNewNetif = true; + netifCount++; + } + char nmbuf[INET6_ADDRSTRLEN]; + static ip4_addr_t ip4, netmask, gw; + IP4_ADDR(&gw,127,0,0,1); + ip4.addr = *((u32_t *)ip.rawIpData()); + netmask.addr = *((u32_t *)ip.netmask().rawIpData()); + LOCK_TCPIP_CORE(); + netif_add(n, &ip4, &netmask, &gw, (void*)vtap, _netif_init4, tcpip_input); + vtap->netif4 = (void*)n; + _enqueueEvent(ZTS_EVENT_NETIF_UP, (void*)_lwip_prepare_netif_status_msg(n)); + UNLOCK_TCPIP_CORE(); + snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", + n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], + n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); + 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 (vtap->netif6) { + n = (struct netif*)vtap->netif6; + } + else { + n = new struct netif; + isNewNetif = true; + netifCount++; + } + static ip6_addr_t ip6; + memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr)); + LOCK_TCPIP_CORE(); + if (isNewNetif) { + vtap->netif6 = (void*)n; + netif_add(n, NULL, NULL, NULL, (void*)vtap, _netif_init6, ethernet_input); + n->ip6_autoconfig_enabled = 1; + vtap->_mac.copyTo(n->hwaddr, n->hwaddr_len); + netif_create_ip6_linklocal_address(n, 1); + netif_set_link_up(n); + netif_set_up(n); + netif_set_default(n); + } + netif_add_ip6_address(n,&ip6,NULL); + n->output_ip6 = ethip6_output; + UNLOCK_TCPIP_CORE(); + _enqueueEvent(ZTS_EVENT_NETIF_UP, (void*)_lwip_prepare_netif_status_msg(n)); + snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", + n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], + n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); + DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n, + macbuf, ip.toString(ipbuf), vtap); + } +} + +} // namespace ZeroTier diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp index 3703e96..486225c 100644 --- a/src/VirtualTap.hpp +++ b/src/VirtualTap.hpp @@ -14,38 +14,30 @@ /** * @file * - * Virtual Ethernet tap device + * Header for virtual ethernet tap device and combined network stack driver */ -#ifndef LIBZT_VIRTUALTAP_HPP -#define LIBZT_VIRTUALTAP_HPP +#ifndef ZT_VIRTUAL_TAP_HPP +#define ZT_VIRTUAL_TAP_HPP -#ifndef _MSC_VER -extern int errno; -#endif +#include "lwip/err.h" +#define ZTS_LWIP_DRIVER_THREAD_NAME "NetworkStackThread" + +#include "MAC.hpp" #include "Phy.hpp" #include "Thread.hpp" -#include "InetAddress.hpp" -#include "MulticastGroup.hpp" -#include "Mutex.hpp" - -#include "Options.h" - -#if defined(_WIN32) -#include -#include -#include -#include -#endif namespace ZeroTier { class Mutex; +class MAC; +class MulticastGroup; +struct InetAddress; /** - * A virtual tap device. The ZeroTier core service creates one of these for each - * virtual network joined. It will be destroyed upon leave(). + * A virtual tap device. The ZeroTier Node Service will create one per + * joined network. It will be destroyed upon leave(). */ class VirtualTap { @@ -84,18 +76,18 @@ public: bool hasIpv6Addr(); /** - * Adds an address to the userspace 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 */ bool addIp(const InetAddress &ip); /** - * Removes an address from the userspace stack interface associated with this VirtualTap + * Removes an address from the user-space stack interface associated with this VirtualTap */ bool removeIp(const InetAddress &ip); /** - * Presents data to the userspace stack + * Presents data to the user-space stack */ void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data, unsigned int len); @@ -105,11 +97,6 @@ public: */ std::string deviceName() const; - /** - * Get Node ID (ZT address) - */ - std::string nodeId() const; - /** * Set friendly name */ @@ -152,10 +139,6 @@ public: void (*_handler)(void *, void *, uint64_t, const MAC &, const MAC &, unsigned int, unsigned int, const void *, unsigned int); - ////////////////////////////////////////////////////////////////////////////// - // Lower-level lwIP netif handling and traffic handling readiness // - ////////////////////////////////////////////////////////////////////////////// - void *netif4 = NULL; void *netif6 = NULL; @@ -164,20 +147,10 @@ public: */ uint64_t _lastConfigUpdateTime = 0; - /** - * The last time that a callback notification was sent to the user application signalling - * that this interface is ready to process traffic. - */ - uint64_t _lastReadyReportTime = 0; - void lastConfigUpdate(uint64_t lastConfigUpdateTime); int _networkStatus = 0; - ////////////////////////////////////////////////////////////////////////////// - // Vars // - ////////////////////////////////////////////////////////////////////////////// - std::vector > routes; char vtap_full_name[64]; @@ -205,17 +178,6 @@ public: std::vector _multicastGroups; Mutex _multicastGroups_m; - /* - * Timestamp of last run of housekeeping - * SEE: ZT_HOUSEKEEPING_INTERVAL in ZeroTier.h - */ - uint64_t last_housekeeping_ts = 0; - - /** - * Performs miscellaneous background tasks - */ - void Housekeeping(); - ////////////////////////////////////////////////////////////////////////////// // Not used in this implementation // ////////////////////////////////////////////////////////////////////////////// @@ -228,8 +190,135 @@ public: void phyOnTcpClose(PhySocket *sock,void **uptr); void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len); void phyOnTcpWritable(PhySocket *sock,void **uptr); + void phyOnUnixClose(PhySocket *sock,void **uptr); }; +/** + * @brief Return whether a given netif's NETIF_FLAG_UP flag is set + * + * @usage This is a convenience function to encapsulate a macro + */ +bool _lwip_is_netif_up(void *netif); + +/** + * @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 + */ +void _lwip_hibernate_driver(); + +/** + * @brief Decrease the delay multiplier for the main driver loop + * + * @usage This should be called when at least one virtual tap is active + */ +void _lwip_wake_driver(); + +/** + * Returns whether the lwIP network stack is up and ready to process traffic + */ +bool _lwip_is_up(); + +/** + * @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 once + * @return + */ +void _lwip_driver_init(); + +/** + * @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 place. + * The tcpip thread will be stopped, all interfaces will be brought down and all resources will + * be deallocated. A full application restart will be required to bring the stack back online. + * @return + */ +void _lwip_driver_shutdown(); + +/** + * @brief Requests that a netif be brought down and removed. + * + * @return + */ +void _lwip_remove_netif(void *netif); + +/** + * @brief Initialize and start the DNS client + * + * @usage Called after lwip_driver_init() + * @return + */ +void _lwip_dns_init(); + +/** + * @brief Starts DHCP timers + * + * @usage lwip_driver_init() + * @return + */ +void _lwip_start_dhcp(void *netif); + +/** + * @brief Called when the status of a netif changes: + * - Interface is up/down (ZTS_EVENT_NETIF_UP, ZTS_EVENT_NETIF_DOWN) + * - Address changes while up (ZTS_EVENT_NETIF_NEW_ADDRESS) + */ +#if LWIP_NETIF_STATUS_CALLBACK +static void _netif_status_callback(struct netif *netif); +#endif + +/** + * @brief Called when a netif is removed (ZTS_EVENT_NETIF_INTERFACE_REMOVED) + */ +#if LWIP_NETIF_REMOVE_CALLBACK +static void _netif_remove_callback(struct netif *netif); +#endif + +/** + * @brief Called when a link is brought up or down (ZTS_EVENT_NETIF_LINK_UP, ZTS_EVENT_NETIF_LINK_DOWN) + */ +#if LWIP_NETIF_LINK_CALLBACK +static void _netif_link_callback(struct netif *netif); +#endif + +/** + * @brief Set up an interface in the network stack for the VirtualTap. + * + * @param + * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data + * @param ip Virtual IP address for this ZeroTier VirtualTap interface + * @return + */ +void _lwip_init_interface(void *tapref, const InetAddress &ip); + +/** + * @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier virtual wire here. + * + * @usage This shall only be called from the stack or the stack driver. Not the application thread. + * @param netif Transmits an outgoing Ethernet fram from the network stack onto the ZeroTier virtual wire + * @param p A pointer to the beginning of a chain pf struct pbufs + * @return + */ +err_t _lwip_eth_tx(struct netif *netif, struct pbuf *p); + +/** + * @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()) + * @param tap Pointer to VirtualTap from which this data comes + * @param from Origin address (virtual ZeroTier hardware address) + * @param to Intended destination address (virtual ZeroTier hardware address) + * @param etherType Protocol type + * @param data Pointer to Ethernet frame + * @param len Length of Ethernet frame + * @return + */ +void _lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int etherType, + const void *data, unsigned int len); + } // namespace ZeroTier #endif // _H + diff --git a/src/lwipDriver.cpp b/src/lwipDriver.cpp deleted file mode 100644 index b8fa9e2..0000000 --- a/src/lwipDriver.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * lwIP network stack driver - */ - -#include - -#include "MAC.hpp" -#include "Mutex.hpp" - -#include "netif/ethernet.h" -#include "lwip/netif.h" -#include "lwip/etharp.h" -#include "lwip/tcpip.h" -#include "lwip/mem.h" -#include "lwip/memp.h" -#include "lwip/sys.h" -#include "lwip/tcp.h" -#include "lwip/timeouts.h" -#include "lwip/stats.h" -#include "lwip/ethip6.h" -#include "lwip/ip_addr.h" -#include "lwip/nd6.h" -#include "lwip/netifapi.h" - -#ifdef LWIP_STATS -#include "lwip/stats.h" -#endif - -#include "VirtualTap.hpp" -#include "lwipDriver.hpp" -#include "ZeroTier.h" -#include "Controls.hpp" - -extern void postEvent(uint64_t eventCode, void *arg); -extern void postEvent(uint64_t eventCode); - -#if defined(_WIN32) -#include -#endif - -/** - * Length of human-readable MAC address string - */ -#define ZTS_MAC_ADDRSTRLEN 18 - -#ifndef htonll -#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) -#endif - -namespace ZeroTier { - -bool _has_exited = false; -int hibernationDelayMultiplier = 1; -int netifCount = 0; -extern bool _run_lwip_tcpip; -Mutex lwip_driver_m; - -void lwip_sleep(long ms) -{ -#if defined(_WIN32) - Sleep(ms*hibernationDelayMultiplier); -#else - usleep(ms*1000*hibernationDelayMultiplier); -#endif -} - -void lwip_hibernate_driver() -{ - hibernationDelayMultiplier = ZTS_HIBERNATION_MULTIPLIER; -} - -void lwip_wake_driver() -{ - hibernationDelayMultiplier = 1; -} - -// Callback for when the TCPIP thread has been successfully started -static void tcpip_init_done(void *arg) -{ - sys_sem_t *sem; - sem = (sys_sem_t *)arg; - _run_lwip_tcpip = true; - postEvent(ZTS_EVENT_STACK_UP); - sys_sem_signal(sem); -} - -static void main_lwip_driver_loop(void *arg) -{ -#if defined(__linux__) - pthread_setname_np(pthread_self(), ZTS_LWIP_DRIVER_THREAD_NAME); -#endif -#if defined(__APPLE__) - pthread_setname_np(ZTS_LWIP_DRIVER_THREAD_NAME); -#endif - sys_sem_t sem; - LWIP_UNUSED_ARG(arg); - if (sys_sem_new(&sem, 0) != ERR_OK) { - DEBUG_ERROR("failed to create semaphore"); - } - tcpip_init(tcpip_init_done, &sem); - sys_sem_wait(&sem); - // Main loop - while(_run_lwip_tcpip) { - lwip_sleep(LWIP_DRIVER_LOOP_INTERVAL); - } - _has_exited = true; - postEvent(ZTS_EVENT_STACK_DOWN); -} - -bool lwip_is_up() -{ - Mutex::Lock _l(lwip_driver_m); - return _run_lwip_tcpip; -} - -bool lwip_has_previously_shutdown() -{ - Mutex::Lock _l(lwip_driver_m); - return _has_exited; -} - -void lwip_driver_init() -{ - if (lwip_is_up()) { - return; - } - if (lwip_has_previously_shutdown()) { - return; - } - Mutex::Lock _l(lwip_driver_m); -#if defined(_WIN32) - sys_init(); // Required for win32 init of critical sections -#endif - sys_thread_new(ZTS_LWIP_DRIVER_THREAD_NAME, main_lwip_driver_loop, - NULL, DEFAULT_THREAD_STACKSIZE, DEFAULT_THREAD_PRIO); -} - -void lwip_driver_shutdown() -{ - if (lwip_has_previously_shutdown()) { - return; - } - Mutex::Lock _l(lwip_driver_m); - // Set flag to stop sending frames into the core - _run_lwip_tcpip = false; - // Wait until the main lwIP thread has exited - while (!_has_exited) { lwip_sleep(LWIP_DRIVER_LOOP_INTERVAL); } - /* - if (tcpip_shutdown() == ERR_OK) { - sys_timeouts_free(); - } - */ -} - -void lwip_remove_netif(void *netif) -{ - if (!netif) { - return; - } - struct netif *n = (struct netif*)netif; - LOCK_TCPIP_CORE(); - netif_remove(n); - netif_set_down(n); - netif_set_link_down(n); - UNLOCK_TCPIP_CORE(); -} - -err_t lwip_eth_tx(struct netif *n, struct pbuf *p) -{ - if (!n) { - return ERR_IF; - } - struct pbuf *q; - char buf[ZT_MAX_MTU+32]; - char *bufptr; - int totalLength = 0; - - VirtualTap *tap = (VirtualTap*)n->state; - bufptr = buf; - for (q = p; q != NULL; q = q->next) { - memcpy(bufptr, q->payload, q->len); - bufptr += q->len; - totalLength += q->len; - } - struct eth_hdr *ethhdr; - ethhdr = (struct eth_hdr *)buf; - - MAC src_mac; - MAC dest_mac; - src_mac.setTo(ethhdr->src.addr, 6); - dest_mac.setTo(ethhdr->dest.addr, 6); - - char *data = buf + sizeof(struct eth_hdr); - int len = totalLength - sizeof(struct eth_hdr); - int proto = Utils::ntoh((uint16_t)ethhdr->type); - tap->_handler(tap->_arg, NULL, tap->_nwid, 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; -} - -void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int etherType, - const void *data, unsigned int len) -{ -#ifdef LWIP_STATS - stats_display(); -#endif - if (!_run_lwip_tcpip) { - return; - } - struct pbuf *p,*q; - struct eth_hdr ethhdr; - from.copyTo(ethhdr.src.addr, 6); - to.copyTo(ethhdr.dest.addr, 6); - 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); - if (!p) { - DEBUG_ERROR("dropped packet: unable to allocate memory for pbuf"); - return; - } - // First pbuf gets ethernet header at start - q = p; - if (q->len < sizeof(ethhdr)) { - pbuf_free(p); - p = NULL; - DEBUG_ERROR("dropped packet: first pbuf smaller than ethernet header"); - return; - } - // Copy frame data into pbuf - const char *dataptr = reinterpret_cast(data); - memcpy(q->payload,ðhdr,sizeof(ethhdr)); - int remainingPayloadSpace = q->len - sizeof(ethhdr); - memcpy((char*)q->payload + sizeof(ethhdr),dataptr,remainingPayloadSpace); - dataptr += remainingPayloadSpace; - // Remaining pbufs (if any) get rest of data - while ((q = q->next)) { - memcpy(q->payload,dataptr,q->len); - dataptr += q->len; - } - // Feed packet into stack - int err; - - if (Utils::ntoh(ethhdr.type) == 0x800 || Utils::ntoh(ethhdr.type) == 0x806) { - if ((err = ((struct netif *)tap->netif4)->input(p, (struct netif *)tap->netif4)) != ERR_OK) { - DEBUG_ERROR("packet input error (%d)", err); - pbuf_free(p); - } - } - if (Utils::ntoh(ethhdr.type) == 0x86DD) { - if ((err = ((struct netif *)tap->netif6)->input(p, (struct netif *)tap->netif6)) != ERR_OK) { - DEBUG_ERROR("packet input error (%d)", err); - pbuf_free(p); - } - } -} - -/* -static void print_netif_info(struct netif *n) { - DEBUG_INFO("n=%p, %c%c, %d, o=%p, o6=%p, mc=%x:%x:%x:%x:%x:%x, hwln=%d, st=%p, flgs=%d\n", - n, - n->name[0], - n->name[1], - n->mtu, - n->output, - n->output_ip6, - n->hwaddr[0], - n->hwaddr[1], - n->hwaddr[2], - n->hwaddr[3], - n->hwaddr[4], - n->hwaddr[5], - n->hwaddr_len, - n->state, - n->flags - ); -} -*/ - -bool lwip_is_netif_up(void *n) -{ - if (!n) { - return false; - } - LOCK_TCPIP_CORE(); - bool result = netif_is_up((struct netif*)n); - UNLOCK_TCPIP_CORE(); - return result; -} - -/** - * Called when a netif is removed (ZTS_EVENT_NETIF_INTERFACE_REMOVED) - */ -#if LWIP_NETIF_REMOVE_CALLBACK -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; - uint64_t mac = 0; - memcpy(&mac, n->hwaddr, n->hwaddr_len); - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - postEvent(ZTS_EVENT_NETIF_REMOVED, (void*)ifd); -} -#endif - -/** - * Called when a link is brought up or down (ZTS_EVENT_NETIF_LINK_UP, ZTS_EVENT_NETIF_LINK_DOWN) - */ -#if LWIP_NETIF_LINK_CALLBACK -static void netif_link_callback(struct netif *n) -{ - // Called from core, no need to lock - if (!n || !n->state) { - return; - } - VirtualTap *tap = (VirtualTap *)n->state; - uint64_t mac = 0; - memcpy(&mac, n->hwaddr, n->hwaddr_len); - if (n->flags & NETIF_FLAG_LINK_UP) { - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - postEvent(ZTS_EVENT_NETIF_LINK_UP, (void*)ifd); - } - if (n->flags & NETIF_FLAG_LINK_UP) { - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - postEvent(ZTS_EVENT_NETIF_LINK_DOWN, (void*)ifd); - } -} -#endif - -void lwip_set_callbacks(struct netif *n) -{ - if (!n) { - return; - } -#if LWIP_NETIF_STATUS_CALLBACK - // Not currently used - netif_set_status_callback(n, netif_status_callback); -#endif -#if LWIP_NETIF_REMOVE_CALLBACK - netif_set_remove_callback(n, netif_remove_callback); -#endif -#if LWIP_NETIF_LINK_CALLBACK - netif_set_link_callback(n, netif_link_callback); -#endif -} - -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 *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - ifd->mtu = n->mtu; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = htonll(ifd->mac) >> 16; - return ifd; -} - -static err_t netif_init(struct netif *n) -{ - if (!n || !n->state) { - return ERR_IF; - } - // Called from netif code, no need to lock - n->hwaddr_len = 6; - n->name[0] = '4'; - n->name[1] = 'a'+netifCount; - n->linkoutput = lwip_eth_tx; - n->output = etharp_output; - n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; - n->flags = NETIF_FLAG_BROADCAST - | NETIF_FLAG_ETHARP - | NETIF_FLAG_ETHERNET - | NETIF_FLAG_IGMP - | NETIF_FLAG_MLD6 - | NETIF_FLAG_LINK_UP - | NETIF_FLAG_UP; - n->hwaddr_len = sizeof(n->hwaddr); - VirtualTap *tap = (VirtualTap*)(n->state); - tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); - return ERR_OK; -} - -static err_t netif_init6(struct netif *n) -{ - if (!n || !n->state) { - return ERR_IF; - } - n->hwaddr_len = sizeof(n->hwaddr); - VirtualTap *tap = (VirtualTap*)(n->state); - tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); - // Called from netif code, no need to lock - n->hwaddr_len = 6; - n->name[0] = '6'; - n->name[1] = 'a'+netifCount; - n->linkoutput = lwip_eth_tx; - n->output_ip6 = ethip6_output; - n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; - n->flags = NETIF_FLAG_BROADCAST - | NETIF_FLAG_ETHARP - | NETIF_FLAG_ETHERNET - | NETIF_FLAG_IGMP - | NETIF_FLAG_MLD6 - | NETIF_FLAG_LINK_UP - | NETIF_FLAG_UP; - return ERR_OK; -} - -void lwip_init_interface(void *tapref, const InetAddress &ip) -{ - char ipbuf[INET6_ADDRSTRLEN]; - char macbuf[ZTS_MAC_ADDRSTRLEN]; - - VirtualTap *vtap = (VirtualTap*)tapref; - struct netif *n = NULL; - bool isNewNetif = false; - - if (ip.isV4()) { - if (vtap->netif4) { - n = (struct netif*)vtap->netif4; - } - else { - n = new struct netif; - isNewNetif = true; - netifCount++; - } - char nmbuf[INET6_ADDRSTRLEN]; - static ip4_addr_t ip4, netmask, gw; - IP4_ADDR(&gw,127,0,0,1); - ip4.addr = *((u32_t *)ip.rawIpData()); - netmask.addr = *((u32_t *)ip.netmask().rawIpData()); - LOCK_TCPIP_CORE(); - netif_add(n, &ip4, &netmask, &gw, (void*)vtap, netif_init, tcpip_input); - vtap->netif4 = (void*)n; - postEvent(ZTS_EVENT_NETIF_UP, (void*)lwip_prepare_netif_status_msg(n)); - UNLOCK_TCPIP_CORE(); - snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", - n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], - n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - 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 (vtap->netif6) { - n = (struct netif*)vtap->netif6; - } - else { - n = new struct netif; - isNewNetif = true; - netifCount++; - } - static ip6_addr_t ip6; - memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr)); - LOCK_TCPIP_CORE(); - if (isNewNetif) { - vtap->netif6 = (void*)n; - netif_add(n, NULL, NULL, NULL, (void*)vtap, netif_init6, ethernet_input); - n->ip6_autoconfig_enabled = 1; - vtap->_mac.copyTo(n->hwaddr, n->hwaddr_len); - netif_create_ip6_linklocal_address(n, 1); - netif_set_link_up(n); - netif_set_up(n); - netif_set_default(n); - } - netif_add_ip6_address(n,&ip6,NULL); - n->output_ip6 = ethip6_output; - UNLOCK_TCPIP_CORE(); - postEvent(ZTS_EVENT_NETIF_UP, (void*)lwip_prepare_netif_status_msg(n)); - snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", - n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], - n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n, - macbuf, ip.toString(ipbuf), vtap); - } -} - -} // namespace ZeroTier \ No newline at end of file diff --git a/src/lwipDriver.hpp b/src/lwipDriver.hpp deleted file mode 100644 index c2bcd9a..0000000 --- a/src/lwipDriver.hpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c)2013-2020 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2024-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. - */ -/****/ - -/** - * @file - * - * lwIP network stack driver - */ - -#ifndef LIBZT_LWIP_DRIVER_HPP -#define LIBZT_LWIP_DRIVER_HPP - -#include "Debug.hpp" -#include "lwip/err.h" - -namespace ZeroTier { - -class MAC; -class Mutex; -class VirtualTap; -struct InetAddress; - -/** - * @brief Structure used to associate packets with interfaces. - */ -struct zts_sorted_packet -{ - // lwIP pbuf containing packet (originally encapsulated by ZT packet) - struct pbuf *p; - // ZT VirtualTap from which this packet originates - VirtualTap *vtap; - // lwIP netif we should accept this packet on - struct netif *n; -}; - -/** - * @brief Return whether a given netif's NETIF_FLAG_UP flag is set - * - * @usage This is a convenience function to encapsulate a macro - */ -bool lwip_is_netif_up(void *netif); - -/** - * @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 - */ -void lwip_hibernate_driver(); - -/** - * @brief Decrease the delay multiplier for the main driver loop - * - * @usage This should be called when at least one virtual tap is active - */ -void lwip_wake_driver(); - -/** - * Returns whether the lwIP network stack is up and ready to process traffic - */ -bool lwip_is_up(); - -/** - * @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 once - * @return - */ -void lwip_driver_init(); - -/** - * @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 place. - * The tcpip thread will be stopped, all interfaces will be brought down and all resources will - * be deallocated. A full application restart will be required to bring the stack back online. - * @return - */ -void lwip_driver_shutdown(); - -/** - * @brief Requests that a netif be brought down and removed. - * - * @return - */ -void lwip_remove_netif(void *netif); - -/** - * @brief Initialize and start the DNS client - * - * @usage Called after lwip_driver_init() - * @return - */ -void lwip_dns_init(); - -/** - * @brief Starts DHCP timers - * - * @usage lwip_driver_init() - * @return - */ -void lwip_start_dhcp(void *netif); - -/** - * @brief Called when the status of a netif changes: - * - Interface is up/down (ZTS_EVENT_NETIF_UP, ZTS_EVENT_NETIF_DOWN) - * - Address changes while up (ZTS_EVENT_NETIF_NEW_ADDRESS) - */ -#if LWIP_NETIF_STATUS_CALLBACK -static void netif_status_callback(struct netif *netif); -#endif - -/** - * @brief Called when a netif is removed (ZTS_EVENT_NETIF_INTERFACE_REMOVED) - */ -#if LWIP_NETIF_REMOVE_CALLBACK -static void netif_remove_callback(struct netif *netif); -#endif - -/** - * @brief Called when a link is brought up or down (ZTS_EVENT_NETIF_LINK_UP, ZTS_EVENT_NETIF_LINK_DOWN) - */ -#if LWIP_NETIF_LINK_CALLBACK -static void netif_link_callback(struct netif *netif); -#endif - -/** - * @brief Set up an interface in the network stack for the VirtualTap. - * - * @param - * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data - * @param ip Virtual IP address for this ZeroTier VirtualTap interface - * @return - */ -void lwip_init_interface(void *tapref, const InetAddress &ip); - -/** - * @brief Called from the stack, outbound ethernet frames from the network stack enter the ZeroTier virtual wire here. - * - * @usage This shall only be called from the stack or the stack driver. Not the application thread. - * @param netif Transmits an outgoing Ethernet fram from the network stack onto the ZeroTier virtual wire - * @param p A pointer to the beginning of a chain pf struct pbufs - * @return - */ -err_t lwip_eth_tx(struct netif *netif, struct pbuf *p); - -/** - * @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()) - * @param tap Pointer to VirtualTap from which this data comes - * @param from Origin address (virtual ZeroTier hardware address) - * @param to Intended destination address (virtual ZeroTier hardware address) - * @param etherType Protocol type - * @param data Pointer to Ethernet frame - * @param len Length of Ethernet frame - * @return - */ -void lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int etherType, - const void *data, unsigned int len); - -} // namespace ZeroTier - -#endif // _H From aad11cfa4d101c35d02c25909f4c9079552234ad Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 1 May 2020 19:26:34 -0700 Subject: [PATCH 52/78] Fix documentation --- CHANGELOG.md | 118 --------------------------------------------------- README.md | 6 --- 2 files changed, 124 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 2120c5d..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,118 +0,0 @@ -## [1.3.1-hb0] - 2019-05-13 -*** -- Minor tweaks for Homebrew-core compliance -## [1.3.1] - 2019-05-12 -*** -- Minor build-related improvements. No API or core functionality changes -## [1.3.0] - 2019-04-22 -*** -- Added new callback API. Significant improvements to performance and reliability -## [1.2.0] - 2019-03-01 -*** -- ZeroTier core version upgrade to 1.2.12, major bugfixes, improved platform compatibility, revamped build process -## [1.1.6] - 2018-02-08 -*** -- Improved concurrent thread performance -## [1.1.4] - 2017-06-06 -*** -- Important Dual-stack IPv4/IPv6 bug fix -## [1.1.3] - 2017-06-06 -*** -- Updated picoTCP to 1.4.0, improved selftest -## [1.1.2] - 2017-06-06 -*** -- Minor ZTO source update, improved selftest, documentation -## [1.1.1] - 2017-06-06 -*** -- Updated ZTO source to 1.2.4, bug fixes -## [1.1.0] - 2017-06-06 -*** -- Added new socket multiplexing logic, significant restructuring of picoTCP stack driver -## [1.0.0] - 2017-06-06 -*** -- Refactored library structure. Dynamic loading of stack no longer required. lwIP Support dropped -## [0.9.0] - 2017-06-06 -*** -- Updated ZTO source for tptr feature -## [0.8.0] - 2017-06-06 -*** -- Updated ZTO source to 1.2.0, bug fixes -## [0.7.0] - 2017-06-06 -*** -- Updated ZTO source to 1.1.17, updated documentation, bug fixes -## [0.6.2] - 2017-06-06 -*** -- Updated ZTO source for reliability -## [0.6.1] - 2017-06-06 -*** -- Stack driver support for IPv4 XOR IPv6 -## [0.6.0] - 2017-06-06 -*** -- Updated ZTO source to 1.1.14 -## [0.5.0] - 2017-06-06 -*** -- Implemented driver for picoTCP network stack -## [0.4.2] - 2017-06-06 -*** -- IPv6-related fixes -## [0.4.1] - 2017-06-06 -*** -- Added IPv6 support to lwIP stack driver -## [0.4.0] - 2017-06-06 -*** -- Upgraded to lwIP 2.0.0 -## [0.3.5] - 2017-06-06 -*** -- Introduced prettier and less complex Swift API -## [0.3.4] - 2017-06-06 -*** -- Improved debugging traces -## [0.3.3] - 2017-06-06 -*** -- Improved API parameter naming consistency -## [0.3.2] - 2017-06-06 -*** -- Improved proxy server controls -## [0.3.1] - 2017-06-06 -*** -- API consistency improvements -## [0.3.0] - 2017-06-06 -*** -- Updated ZTO source to 1.1.4 -## [0.2.1] - 2017-06-06 -*** -- Normalized zt_ API naming conventions -## [0.2.0] - 2017-06-06 -*** -- Old netcon project moved into its own repo - - -[1.3.1-hb0]: https://github.com/zerotier/libzt/compare/1.3.1..1.3.1-hb0 -[1.3.1]: https://github.com/zerotier/libzt/compare/1.3.0..1.3.1 -[1.3.0]: https://github.com/zerotier/libzt/compare/1.2.0..1.3.0 -[1.2.0]: https://github.com/zerotier/libzt/compare/1.1.6..1.2.0 -[1.1.6]: https://github.com/zerotier/libzt/compare/1.1.4..1.1.6 -[1.1.4]: https://github.com/zerotier/libzt/compare/1.1.3..1.1.4 -[1.1.3]: https://github.com/zerotier/libzt/compare/1.1.2..1.1.3 -[1.1.2]: https://github.com/zerotier/libzt/compare/1.1.1..1.1.2 -[1.1.1]: https://github.com/zerotier/libzt/compare/1.1.0..1.1.1 -[1.1.0]: https://github.com/zerotier/libzt/compare/1.0.0..1.1.0 -[1.0.0]: https://github.com/zerotier/libzt/compare/0.9.0..1.0.0 -[0.9.0]: https://github.com/zerotier/libzt/compare/0.8.0..0.9.0 -[0.8.0]: https://github.com/zerotier/libzt/compare/0.7.0..0.8.0 -[0.7.0]: https://github.com/zerotier/libzt/compare/0.6.2..0.7.0 -[0.6.2]: https://github.com/zerotier/libzt/compare/0.6.1..0.6.2 -[0.6.1]: https://github.com/zerotier/libzt/compare/0.6.0..0.6.1 -[0.6.0]: https://github.com/zerotier/libzt/compare/0.5.0..0.6.0 -[0.5.0]: https://github.com/zerotier/libzt/compare/0.4.2..0.5.0 -[0.4.2]: https://github.com/zerotier/libzt/compare/0.4.1..0.4.2 -[0.4.1]: https://github.com/zerotier/libzt/compare/0.4.0..0.4.1 -[0.4.0]: https://github.com/zerotier/libzt/compare/0.3.5..0.4.0 -[0.3.5]: https://github.com/zerotier/libzt/compare/0.3.4..0.3.5 -[0.3.4]: https://github.com/zerotier/libzt/compare/0.3.3..0.3.4 -[0.3.3]: https://github.com/zerotier/libzt/compare/0.3.2..0.3.3 -[0.3.2]: https://github.com/zerotier/libzt/compare/0.3.1..0.3.2 -[0.3.1]: https://github.com/zerotier/libzt/compare/0.3.0..0.3.1 -[0.3.0]: https://github.com/zerotier/libzt/compare/0.2.1..0.3.0 -[0.2.1]: https://github.com/zerotier/libzt/compare/0.2.0..0.2.1 -[0.2.0]: https://github.com/zerotier/libzt/compare/e073a35b72781a8947e197419677e82b9ed9f9bc..0.2.0 diff --git a/README.md b/README.md index db8b845..da67e90 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ ---- -title: README -created: '2020-04-17T17:19:44.552Z' -modified: '2020-05-02T00:31:45.563Z' ---- - # ZeroTier SDK Connect physical devices, virtual devices, and application instances as if everything is on a single LAN. *** From 3852e1f85f90c39eb17c4f8695c26f625d284633 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 1 May 2020 19:45:56 -0700 Subject: [PATCH 53/78] Update Objective-C and Swift examples --- examples/objective-c/adhoc.m | 54 +++++++++------------- examples/swift/main.swift | 89 ++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/examples/objective-c/adhoc.m b/examples/objective-c/adhoc.m index 26323e3..0b0a194 100644 --- a/examples/objective-c/adhoc.m +++ b/examples/objective-c/adhoc.m @@ -10,7 +10,7 @@ #import -#import +#import #include @@ -22,12 +22,12 @@ * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api * - * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * - An exception to the above rule is if you are using an Ad-hoc network, it has no * controller and therefore requires no authorization. * * @@ -60,28 +60,27 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * [ 0] ZTS_ERR_OK - No error. - * [-1] ZTS_ERR - Error (see zts_errno for more information). - * [-2] ZTS_ERR_INVALID_ARG - An argument provided is invalid. - * [-3] ZTS_ERR_SERVICE - ZT is not yet initialized. Try again. - * [-4] ZTS_ERR_INVALID_OP - Operation is not permitted (Doesn't make sense in this state). - * [-5] ZTS_ERR_NO_RESULT - Call succeeded but no result was available. Not always an error. - * [-6] ZTS_ERR_GENERAL - General internal failure. Consider filing a bug report. + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With * the added possibility of zts_errno being set. Much like standard * errno this will provide a more specific reason for an error's occurrence. - * These error values are defined in: libzt/ext/lwip/src/include/lwip/errno.h - * and closely map to standard Linux error values. + * See ZeroTierSockets.h for values. * * * API COMPATIBILITY WITH HOST OS: * - * - Since libzt re-implements a socket API probably very similar to your host OS's own - * API 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, so here are a few - * guidelines: + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: * @@ -92,19 +91,8 @@ * * struct zts_sockaddr_in in4; <------ Note the zts_* prefix * ... - * zts_bind(fd, (struct sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) * - * If you are calling a host OS function, use your host OS's constants (and structures!): - * - * inet_ntop(AF_INET6, &(in6->sin6_addr), ...); (CORRECT) - * inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ...); (INCORRECT) - * - * 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: - * - * struct zts_sockaddr_in6 in6; - * ... - * inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN); */ void delay_ms(long ms) { usleep(ms*1000); } @@ -129,8 +117,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) NSLog(@"ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", msg->network->nwid); } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQUESTING_CONFIG) { - NSLog(@"ZTS_EVENT_NETWORK_REQUESTING_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + NSLog(@"ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); } if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) { NSLog(@"ZTS_EVENT_NETWORK_ACCESS_DENIED --- Access to virtual network %llx has been denied. Did you authorize the node yet?\n", @@ -169,8 +157,8 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) } // Peer events // If you don't recognize the peer ID, don't panic, this is most likely one of our root servers - if (msg->eventCode == ZTS_EVENT_PEER_P2P) { - NSLog(@"ZTS_EVENT_PEER_P2P --- There is now a direct path to peer %llx\n", + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + NSLog(@"ZTS_EVENT_PEER_DIRECT --- There is now a direct path to peer %llx\n", msg->peer->address); } if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { @@ -218,7 +206,7 @@ int main(int argc, char **argv) uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); int err = ZTS_ERR_OK; - zts_set_network_caching(false); + zts_allow_network_caching(false); if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { NSLog(@"Unable to start service, error = %d. Exiting.\n", err); diff --git a/examples/swift/main.swift b/examples/swift/main.swift index 36dbc92..85c010c 100644 --- a/examples/swift/main.swift +++ b/examples/swift/main.swift @@ -3,11 +3,94 @@ * * swiftc -lc++ -import-objc-header ../../include/ZeroTierSockets.h -L. -lzt main.swift -o main; * ./main + * + * TODO: This example is incomplete */ import Swift import Foundation +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - An exception to the above rule is if you are using an Ad-hoc network, it has no + * controller and therefore requires no authorization. + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * ZTS_ERR_OK 0 // No error + * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno + * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time + * ZTS_ERR_ARG -3 // Invalid argument + * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) + * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * See ZeroTierSockets.h for values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * struct zts_sockaddr_in in4; <------ Note the zts_* prefix + * ... + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * + */ + var nodeReady:Bool = false var networkReady:Bool = false @@ -39,9 +122,9 @@ let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void let networkId:UInt64 = network!.pointee.nwid print(String(format: "ZTS_EVENT_NETWORK_NOT_FOUND (%llx)", networkId)) - case ZTS_EVENT_NETWORK_REQUESTING_CONFIG: + case ZTS_EVENT_NETWORK_REQ_CONFIG: let networkId:UInt64 = network!.pointee.nwid - print(String(format: "ZTS_EVENT_NETWORK_REQUESTING_CONFIG (%llx)", networkId)) + print(String(format: "ZTS_EVENT_NETWORK_REQ_CONFIG (%llx)", networkId)) case ZTS_EVENT_NETWORK_ACCESS_DENIED: let networkId:UInt64 = network!.pointee.nwid @@ -132,7 +215,7 @@ let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void func main() { print("waiting for node to come online...") - zts_start("../../config_path_a", myZeroTierEventCallback, 0) + zts_start("config_path", myZeroTierEventCallback, 0) while(!nodeReady) { sleep(1) } From db0f801f07b316d58f3f0e73d18b95262732fdd8 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 1 May 2020 21:42:26 -0700 Subject: [PATCH 54/78] Update documentation --- README.md | 2 +- examples/cpp/adhoc.cpp | 4 ++-- examples/cpp/client.cpp | 4 ++-- examples/cpp/comprehensive.cpp | 4 ++-- examples/cpp/earthtest.cpp | 4 ++-- examples/cpp/server.cpp | 4 ++-- examples/objective-c/adhoc.m | 4 ++-- examples/swift/main.swift | 8 ++++---- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index da67e90..c915f20 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ ZTS_EVENT_NETWORK_DOWN # Connecting and communicating with peers -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_P2P` has been received for the peer you are attempting to communicate with. All of the above is optional, but it will improve your experience. +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` diff --git a/examples/cpp/adhoc.cpp b/examples/cpp/adhoc.cpp index 3650dfd..ec6ee0e 100644 --- a/examples/cpp/adhoc.cpp +++ b/examples/cpp/adhoc.cpp @@ -35,10 +35,10 @@ * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index 405d539..75b35d3 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -127,10 +127,10 @@ void myZeroTierEventCallback(void *msgPtr) * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index 2f1f57a..eeaf08a 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -35,10 +35,10 @@ * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/cpp/earthtest.cpp b/examples/cpp/earthtest.cpp index 0e29549..a6dff6a 100644 --- a/examples/cpp/earthtest.cpp +++ b/examples/cpp/earthtest.cpp @@ -35,10 +35,10 @@ * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp index ff7c5a7..51e61bb 100644 --- a/examples/cpp/server.cpp +++ b/examples/cpp/server.cpp @@ -127,10 +127,10 @@ void myZeroTierEventCallback(void *msgPtr) * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/objective-c/adhoc.m b/examples/objective-c/adhoc.m index 0b0a194..46b2c67 100644 --- a/examples/objective-c/adhoc.m +++ b/examples/objective-c/adhoc.m @@ -44,10 +44,10 @@ * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * diff --git a/examples/swift/main.swift b/examples/swift/main.swift index 85c010c..bb82621 100644 --- a/examples/swift/main.swift +++ b/examples/swift/main.swift @@ -40,10 +40,10 @@ import Foundation * root server has passed contact information to both peers that a direct connection will be * established. Therefore, it is required that multiple connection attempts be undertaken * when initially communicating with a peer. After a transport-triggered link is - * established libzt will inform you via ZTS_EVENT_PEER_P2P for a specific peer ID. No + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No * action is required on your part for this callback event. * - * Note: In these initial moments before ZTS_EVENT_PEER_P2P has been received for a + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a * specific peer, traffic may be slow, jittery and there may be high packet loss. * This will subside within a couple of seconds. * @@ -200,8 +200,8 @@ let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void ipstr, msg.addr->nwid) */ // Peer events - case ZTS_EVENT_PEER_P2P: - print("ZTS_EVENT_PEER_P2P --- node=%llx\n", msg.peer->address) + case ZTS_EVENT_PEER_DIRECT: + print("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg.peer->address) case ZTS_EVENT_PEER_RELAY: print("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg.peer->address) From 112d66ea20117f5425bbadd3920b2d6e23b94c4c Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Tue, 5 May 2020 14:35:22 -0700 Subject: [PATCH 55/78] Fix non-blocking behavior of sockets (removal of a portion of the old compatibility layer) --- src/Sockets.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/Sockets.cpp b/src/Sockets.cpp index 7844c51..3366209 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -392,22 +392,10 @@ JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_select(JNIEnv *env, jobj int zts_fcntl(int fd, int cmd, int flags) { - // translation from platform flag values to stack flag values - int translated_flags = 0; -#if defined(__linux__) - if (flags == 2048) { - translated_flags = 1; - } -#endif -#if defined(__APPLE__) - if (flags == 4) { - translated_flags = 1; - } -#endif if (!(_serviceStateFlags & ZTS_STATE_NET_SERVICE_RUNNING)) { return ZTS_ERR_SERVICE; } - return lwip_fcntl(fd, cmd, translated_flags); + return lwip_fcntl(fd, cmd, flags); } #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_fcntl( From 676ddd15d4c580ff01e7c36984fb0c0902d259c3 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 6 May 2020 20:24:58 -0700 Subject: [PATCH 56/78] Change default MTU to 2800 --- src/VirtualTap.cpp | 8 ++++---- src/lwipopts.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index fd1b32a..52e2f56 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -223,7 +223,7 @@ std::vector VirtualTap::ips() const void VirtualTap::put(const MAC &from,const MAC &to,unsigned int etherType, const void *data,unsigned int len) { - if (len <= _mtu && _enabled) { + if (len && _enabled) { _lwip_eth_rx(this, from, to, etherType, data, len); } } @@ -653,12 +653,13 @@ static err_t _netif_init4(struct netif *n) return ERR_IF; } // Called from netif code, no need to lock + VirtualTap *tap = (VirtualTap*)(n->state); n->hwaddr_len = 6; n->name[0] = '4'; n->name[1] = 'a'+netifCount; n->linkoutput = _lwip_eth_tx; n->output = etharp_output; - n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; + n->mtu = std::min(LWIP_MTU,(int)tap->_mtu); n->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET @@ -667,7 +668,6 @@ static err_t _netif_init4(struct netif *n) | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP; n->hwaddr_len = sizeof(n->hwaddr); - VirtualTap *tap = (VirtualTap*)(n->state); tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); return ERR_OK; } @@ -686,7 +686,7 @@ static err_t _netif_init6(struct netif *n) n->name[1] = 'a'+netifCount; n->linkoutput = _lwip_eth_tx; n->output_ip6 = ethip6_output; - n->mtu = LWIP_MTU < ZT_MAX_MTU ? LWIP_MTU : ZT_MAX_MTU; + n->mtu = std::min(LWIP_MTU,(int)tap->_mtu); n->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET diff --git a/src/lwipopts.h b/src/lwipopts.h index cce9749..bfbe47c 100644 --- a/src/lwipopts.h +++ b/src/lwipopts.h @@ -83,7 +83,7 @@ ------------------------------------ Presets ----------------------------------- ------------------------------------------------------------------------------*/ -#define LWIP_MTU 1500 +#define LWIP_MTU 2800 #define LWIP_CHKSUM_ALGORITHM 2 // memory #define MEMP_NUM_NETCONN 1024 From a708c72dfa5b8fc7591635e287587adad147ab8d Mon Sep 17 00:00:00 2001 From: Lann Martin Date: Thu, 7 May 2020 22:56:54 -0400 Subject: [PATCH 57/78] Fix struct name in zts_get_rfc_*_addr --- src/Controls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controls.cpp b/src/Controls.cpp index b8ea3cc..549b479 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -440,7 +440,7 @@ int zts_deorbit(uint64_t moonWorldId) #ifdef SDK_JNI #endif -int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +int zts_get_6plane_addr(struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { if (!addr || !nwid || !nodeId) { return ZTS_ERR_ARG; @@ -451,7 +451,7 @@ int zts_get_6plane_addr(struct sockaddr_storage *addr, const uint64_t nwid, cons return ZTS_ERR_OK; } -int zts_get_rfc4193_addr(struct sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +int zts_get_rfc4193_addr(struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) { if (!addr || !nwid || !nodeId) { return ZTS_ERR_ARG; From af186f1cb24dda306ac59088e41de7fe28a6315e Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 28 May 2020 13:55:20 -0700 Subject: [PATCH 58/78] Update copyright year in version.rc.in --- version.rc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.rc.in b/version.rc.in index 891a5e8..aefd247 100644 --- a/version.rc.in +++ b/version.rc.in @@ -29,7 +29,7 @@ BEGIN VALUE "CompanyName", "ZeroTier, Inc.\0" VALUE "FileDescription", "ZeroTier SDK\0" VALUE "InternalName", "zt\0" - VALUE "LegalCopyright", "Copyright (C) 2019 ZeroTier, Inc.\0" + VALUE "LegalCopyright", "Copyright (C) 2020 ZeroTier, Inc.\0" VALUE "OriginalFilename", "libzt.dll\0" VALUE "PrivateBuild", "0\0" VALUE "ProductName", "libzt\0" From 8dc2b3b4f2584596ff2d9419774da1bc905c5fc3 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 28 May 2020 13:58:39 -0700 Subject: [PATCH 59/78] Update ios/macos framework modulemap and reference in CMakeLists --- CMakeLists.txt | 14 ++++++++++---- ports/module.modulemap | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14062b9..4acc3d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,13 +257,13 @@ file (GLOB libnatpmpSrcGlob file (GLOB libminiupnpcSrcGlob ${ZTO_SRC_DIR}/ext/miniupnpc/connecthostport.c - ${ZTO_SRC_DIR}/ext/miniupnpc/igd_desc_parse.c + ${ZTO_SRC_DIR}/ext/miniupnpc/igd_desc_parse.c ${ZTO_SRC_DIR}/ext/miniupnpc/minisoap.c ${ZTO_SRC_DIR}/ext/miniupnpc/minissdpc.c - ${ZTO_SRC_DIR}/ext/miniupnpc/miniupnpc.c + ${ZTO_SRC_DIR}/ext/miniupnpc/miniupnpc.c ${ZTO_SRC_DIR}/ext/miniupnpc/miniwget.c ${ZTO_SRC_DIR}/ext/miniupnpc/minixml.c - ${ZTO_SRC_DIR}/ext/miniupnpc/portlistingparse.c + ${ZTO_SRC_DIR}/ext/miniupnpc/portlistingparse.c ${ZTO_SRC_DIR}/ext/miniupnpc/receivedata.c ${ZTO_SRC_DIR}/ext/miniupnpc/upnpcommands.c ${ZTO_SRC_DIR}/ext/miniupnpc/upnpdev.c @@ -465,6 +465,7 @@ endif () if (IN_XCODE) include_directories (${frameworkHeaderGlob}) + add_library(${XCODE_FRAMEWORK_NAME} STATIC $ $ @@ -474,12 +475,17 @@ if (IN_XCODE) ${libztSrcGlob} ${frameworkHeaderGlob}) + set_target_properties(${XCODE_FRAMEWORK_NAME} PROPERTIES ENABLE_BITCODE "YES") + set_target_properties(${XCODE_FRAMEWORK_NAME} PROPERTIES BITCODE_GENERATION_MODE bitcode) + target_compile_options( ${XCODE_FRAMEWORK_NAME} PUBLIC -fembed-bitcode ) + target_link_libraries( ${XCODE_FRAMEWORK_NAME} PUBLIC -fembed-bitcode ) + set_target_properties(${XCODE_FRAMEWORK_NAME} PROPERTIES FRAMEWORK TRUE FRAMEWORK_VERSION A DEFINES_MODULE TRUE MACOSX_FRAMEWORK_IDENTIFIER com.cmake.${XCODE_FRAMEWORK_NAME} - MODULE_MAP "~/op/zt/libzt/libzt_experimental/ports/module.modulemap" + MODULE_MAP "${PROJ_DIR}/ports/module.modulemap" PUBLIC_HEADER "${frameworkHeaderGlob}" XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer" ) diff --git a/ports/module.modulemap b/ports/module.modulemap index 2fd95d7..5760913 100644 --- a/ports/module.modulemap +++ b/ports/module.modulemap @@ -1,5 +1,5 @@ framework module zt { - umbrella header "Xcode-Bridging-Header.h" + umbrella header "ZeroTierSockets.h" export * module * { export * } From 31771cb805fbbe81859398b0380696739df42a72 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 28 May 2020 14:18:43 -0700 Subject: [PATCH 60/78] Add documentation to examples section --- examples/README.md | 57 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..bcfb75f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,57 @@ +Useful things to know + ===== + +### IDENTITIES and AUTHORIZATION: + +Upon the first execution of this code, a new identity will be generated and placed in the location given in the first argument to zts_start(path, ...). If you accidentally duplicate the identity files and use them simultaneously in a different node instance **you will experience undefined behavior** and it is likely that nothing will work. + +You must authorize the node ID provided by the `ZTS_EVENT_NODE_ONLINE` callback to join your network, otherwise nothing will happen. This can be done manually or via our web API: https://my.zerotier.com/help/api + +An exception to the above rule is if you are using an Ad-hoc network, it has no controller and therefore requires no authorization. + + +### ESTABLISHING A CONNECTION: + +Creating a standard socket connection generally works the same as it would using an ordinary socket interface, however with libzt there is a subtle difference in how connections are established which may cause confusion: + +The underlying virtual ZT layer creates what are called "transport-triggered links" between nodes. That is, links are not established until an attempt to communicate with a peer has taken place. The side effect is that the first few packets sent from a libzt instance are usually relayed via our free infrastructure and it isn't until a root server has passed contact information to both peers that a direct connection will be established. Therefore, it is required that multiple connection attempts be undertaken when initially communicating with a peer. After a transport-triggered link is established libzt will inform you via `ZTS_EVENT_PEER_DIRECT` for a specific peer ID. No action is required on your part for this callback event. + +*Note: In these initial moments before `ZTS_EVENT_PEER_DIRECT` has been received for a specific peer, traffic may be slow, jittery and there may be high packet loss. This will subside within a couple of seconds.* + + +### ERROR HANDLING: + +libzt's API is actually composed of two categories of functions with slightly different error reporting mechanisms. + +- Category 1: Control functions (`zts_start`, `zts_join`, `zts_get_peer_status`, etc). Errors returned by these functions can be any of the following: + +``` +ZTS_ERR_OK // No error +ZTS_ERR_SOCKET // Socket error, see zts_errno +ZTS_ERR_SERVICE // You probably did something at the wrong time +ZTS_ERR_ARG // Invalid argument +ZTS_ERR_NO_RESULT // No result (not necessarily an error) +ZTS_ERR_GENERAL // Consider filing a bug report +``` + +- Category 2: Sockets (`zts_socket`, `zts_bind`, `zts_connect`, `zts_listen`, etc). Errors returned by these functions can be the same as the above. With the added possibility of `zts_errno` being set. Much like standard errno this will provide a more specific reason for an error's occurrence. See `ZeroTierSockets.h` for values. + + +### API COMPATIBILITY WITH HOST OS: + +While the ZeroTier socket interface can coexist with your host OS's own interface in the same file with no type and naming conflicts, try not to mix and match host OS/libzt structures, functions, or constants. It may look similar and may even work some of the time but there enough differences that it will cause headaches: + +If you are calling a `zts_*` function, use the appropriate `ZTS_*` constants: + +``` +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: + +``` +struct zts_sockaddr_in in4; <------ Note the zts_* prefix + ... +zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) +``` \ No newline at end of file From 58d9ac8eeafd714744cb25476a3b909195cdace8 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 28 May 2020 14:20:58 -0700 Subject: [PATCH 61/78] Add non-blocking socket examples --- CMakeLists.txt | 4 + examples/cpp/nonblockingclient.cpp | 304 ++++++++++++++++++++++++ examples/cpp/nonblockingserver.cpp | 364 +++++++++++++++++++++++++++++ 3 files changed, 672 insertions(+) create mode 100644 examples/cpp/nonblockingclient.cpp create mode 100644 examples/cpp/nonblockingserver.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4acc3d0..444f257 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -506,6 +506,10 @@ if (SHOULD_BUILD_TESTS) target_link_libraries(client ${STATIC_LIB_NAME}) add_executable (server ${PROJ_DIR}/examples/cpp/server.cpp) target_link_libraries(server ${STATIC_LIB_NAME}) + add_executable (nonblockingclient ${PROJ_DIR}/examples/cpp/nonblockingclient.cpp) + target_link_libraries(nonblockingclient ${STATIC_LIB_NAME}) + add_executable (nonblockingserver ${PROJ_DIR}/examples/cpp/nonblockingserver.cpp) + target_link_libraries(nonblockingserver ${STATIC_LIB_NAME}) endif () # ----------------------------------------------------------------------------- diff --git a/examples/cpp/nonblockingclient.cpp b/examples/cpp/nonblockingclient.cpp new file mode 100644 index 0000000..96d43ba --- /dev/null +++ b/examples/cpp/nonblockingclient.cpp @@ -0,0 +1,304 @@ +/** + * libzt API example + */ + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * See ZeroTierSockets.h for values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * struct zts_sockaddr_in in4; <------ Note the zts_* prefix + * ... + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * + */ + +#include +#include +#include +#include + +#include "ZeroTierSockets.h" + +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; + +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ +void myZeroTierEventCallback(void *msgPtr) +{ + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + myNode.id = msg->node->address; + myNode.online = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + myNode.online = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + myNode.online = false; + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", + msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == 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->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } + } +} + +int main(int argc, char **argv) +{ + if (argc != 6) { + printf("\nlibzt example non-blocking client\n"); + printf("nonblockingclient \n"); + exit(0); + } + uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join + std::string remoteAddr = argv[3]; // Remote application's virtual ZT address + int remotePort = atoi(argv[4]); // Port the application will try to connect to the server on + int ztServicePort = atoi(argv[5]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) + + struct zts_sockaddr_in in4; + in4.sin_port = zts_htons(remotePort); +#if defined(_WIN32) + in4.sin_addr.S_addr = zts_inet_addr(remoteAddr.c_str()); +#else + in4.sin_addr.s_addr = zts_inet_addr(remoteAddr.c_str()); +#endif + in4.sin_family = ZTS_AF_INET; + + // Bring up ZeroTier service and join network + + // Enable/Disable caching of network details in networks.d + // (read function documentation before disabling!) + // zts_allow_network_caching(0) + + // Enable/Disable caching of peer details in peers.d + // (read function documentation before disabling!) + // zts_allow_network_caching(1) + + int err = ZTS_ERR_OK; + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!myNode.online) { zts_delay_ms(50); } + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } + + // Socket-like API example + + char *msgStr = (char*)"Welcome to the machine"; + int bytes=0, fd; + char recvBuf[128]; + memset(recvBuf, 0, sizeof(recvBuf)); + + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); + exit(1); + } + // Retries are often required since ZT uses transport-triggered links (explained above) + for (;;) { + printf("Connecting to remote host...\n"); + if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { + printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", + fd, err, zts_errno); + zts_close(fd); + printf("Creating socket...\n"); + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d). Exiting.\n", fd, zts_errno); + exit(1); + } + zts_delay_ms(250); + } + else { + printf("Connected.\n"); + break; + } + } + + // 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 writing to socket (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, bytes, zts_errno); + exit(1); + } + printf("zts_send()=%d\n", bytes); + zts_delay_ms((rand() % 100) * 50); + } + printf("Read %d bytes: %s\n", bytes, recvBuf); + printf("Closing socket\n"); + zts_close(fd); + printf("Shutting down service\n"); + zts_stop(); + return 0; +} diff --git a/examples/cpp/nonblockingserver.cpp b/examples/cpp/nonblockingserver.cpp new file mode 100644 index 0000000..1992db8 --- /dev/null +++ b/examples/cpp/nonblockingserver.cpp @@ -0,0 +1,364 @@ +/** + * libzt API example + */ + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * See ZeroTierSockets.h for values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * struct zts_sockaddr_in in4; <------ Note the zts_* prefix + * ... + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * + */ + +#include +#include +#include + +#include "ZeroTierSockets.h" + +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; + +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ +void myZeroTierEventCallback(void *msgPtr) +{ + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + myNode.id = msg->node->address; + myNode.online = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + myNode.online = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + myNode.online = false; + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", + msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == 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->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } + } +} + +int main(int argc, char **argv) +{ + if (argc != 5) { + printf("\nlibzt example non-blocking server\n"); + printf("nonblockingserver \n"); + exit(0); + } + uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join + int serverBindPort = atoi(argv[3]); // Port the application should bind to + int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) + + struct zts_sockaddr_in in4, acc_in4; + in4.sin_port = zts_htons(serverBindPort); +#if defined(_WIN32) + in4.sin_addr.S_addr = ZTS_INADDR_ANY; +#else + in4.sin_addr.s_addr = ZTS_INADDR_ANY; +#endif + in4.sin_family = ZTS_AF_INET; + + // Bring up ZeroTier service and join network + + // Enable/Disable caching of network details in networks.d + // (read function documentation before disabling!) + // zts_allow_network_caching(0) + + // Enable/Disable caching of peer details in peers.d + // (read function documentation before disabling!) + // zts_allow_network_caching(1) + + int fd, accfd; + int err = ZTS_ERR_OK; + + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d. Exiting.\n", err); + exit(1); + } + printf("Waiting for node to come online...\n"); + while (!myNode.online) { zts_delay_ms(50); } + printf("This node ID is %llx\n", myNode.id); + printf("This node's identity is stored in %s\n", argv[1]); + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d. Exiting.\n", err); + exit(1); + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } + + // Socket-like API example + + printf("Creating socket...\n"); + if ((fd = zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, ret=%d, zts_errno=%d). Exiting.\n",fd, err, zts_errno); + exit(1); + } + printf("Binding...\n"); + if ((err = zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0)) { + printf("Error binding to interface (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 placing socket in LISTENING state (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); + exit(1); + } + zts_socklen_t client_addrlen = sizeof(zts_sockaddr_in); + if ((accfd = zts_accept(fd, (struct zts_sockaddr *)&acc_in4, &client_addrlen)) < 0) { + printf("Error accepting connection (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); + } + + zts_socklen_t peer_addrlen = sizeof(struct zts_sockaddr_storage); + zts_getpeername(accfd, (struct zts_sockaddr*)&acc_in4, &peer_addrlen); + char ipstr[ZTS_INET_ADDRSTRLEN]; + memset(ipstr, 0, sizeof(ipstr)); + zts_inet_ntop(ZTS_AF_INET, &(acc_in4.sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("Accepted connection from %s:%d\n", ipstr, zts_ntohs(acc_in4.sin_port)); + + int bytes=0; + char recvBuf[128]; + memset(recvBuf, 0, sizeof(recvBuf)); + + // + // Technique 1: ZTS_O_NONBLOCK + // + if (false) { + 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_delay_ms(100); + } + } + + // + // Technique 2: zts_select + // + if (false) { + 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 Date: Thu, 28 May 2020 14:31:43 -0700 Subject: [PATCH 62/78] Adjust formatting of CMakeLists.txt --- CMakeLists.txt | 52 +++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 444f257..9e23698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,7 +47,7 @@ endif () if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "debug") set (DEBUG_BUILD ON) set (CMAKE_VERBOSE_MAKEFILE ON) - set (CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin) + set (CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/bin) set (LIBRARY_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/lib) endif() if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "release") @@ -247,7 +247,7 @@ set (LIBZT_SRC_DIR "${PROJ_DIR}/src") file (GLOB ztcoreSrcGlob ${ZTO_SRC_DIR}/node/*.cpp ${ZTO_SRC_DIR}/osdep/OSUtils.cpp - ${ZTO_SRC_DIR}/osdep/PortMapper.cpp + ${ZTO_SRC_DIR}/osdep/PortMapper.cpp ${ZTO_SRC_DIR}/osdep/ManagedRoute.cpp) file (GLOB libnatpmpSrcGlob @@ -256,19 +256,19 @@ file (GLOB libnatpmpSrcGlob ${ZTO_SRC_DIR}/ext/libnatpmp/getgateway.c) file (GLOB libminiupnpcSrcGlob - ${ZTO_SRC_DIR}/ext/miniupnpc/connecthostport.c - ${ZTO_SRC_DIR}/ext/miniupnpc/igd_desc_parse.c - ${ZTO_SRC_DIR}/ext/miniupnpc/minisoap.c - ${ZTO_SRC_DIR}/ext/miniupnpc/minissdpc.c - ${ZTO_SRC_DIR}/ext/miniupnpc/miniupnpc.c - ${ZTO_SRC_DIR}/ext/miniupnpc/miniwget.c - ${ZTO_SRC_DIR}/ext/miniupnpc/minixml.c - ${ZTO_SRC_DIR}/ext/miniupnpc/portlistingparse.c - ${ZTO_SRC_DIR}/ext/miniupnpc/receivedata.c - ${ZTO_SRC_DIR}/ext/miniupnpc/upnpcommands.c - ${ZTO_SRC_DIR}/ext/miniupnpc/upnpdev.c - ${ZTO_SRC_DIR}/ext/miniupnpc/upnperrors.c - ${ZTO_SRC_DIR}/ext/miniupnpc/upnpreplyparse.c) + ${ZTO_SRC_DIR}/ext/miniupnpc/connecthostport.c + ${ZTO_SRC_DIR}/ext/miniupnpc/igd_desc_parse.c + ${ZTO_SRC_DIR}/ext/miniupnpc/minisoap.c + ${ZTO_SRC_DIR}/ext/miniupnpc/minissdpc.c + ${ZTO_SRC_DIR}/ext/miniupnpc/miniupnpc.c + ${ZTO_SRC_DIR}/ext/miniupnpc/miniwget.c + ${ZTO_SRC_DIR}/ext/miniupnpc/minixml.c + ${ZTO_SRC_DIR}/ext/miniupnpc/portlistingparse.c + ${ZTO_SRC_DIR}/ext/miniupnpc/receivedata.c + ${ZTO_SRC_DIR}/ext/miniupnpc/upnpcommands.c + ${ZTO_SRC_DIR}/ext/miniupnpc/upnpdev.c + ${ZTO_SRC_DIR}/ext/miniupnpc/upnperrors.c + ${ZTO_SRC_DIR}/ext/miniupnpc/upnpreplyparse.c) file (GLOB libztSrcGlob ${LIBZT_SRC_DIR}/*.cpp) @@ -422,8 +422,8 @@ set_target_properties (zt_pic PROPERTIES # libztcore.a add_library (ztcore STATIC $) set_target_properties (ztcore PROPERTIES - OUTPUT_NAME ztcore - LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) + OUTPUT_NAME ztcore + LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # libzt.a add_library (${STATIC_LIB_NAME} STATIC $ @@ -432,8 +432,8 @@ $ $ $ ${libztSrcGlob}) set_target_properties (${STATIC_LIB_NAME} PROPERTIES - OUTPUT_NAME zt - LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) + OUTPUT_NAME zt + LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) set_target_properties (${STATIC_LIB_NAME} PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}") if (BUILDING_WIN) target_link_libraries ( @@ -448,7 +448,7 @@ add_library (${DYNAMIC_LIB_NAME} SHARED ${libztSrcGlob}) target_link_libraries (${DYNAMIC_LIB_NAME} zt_pic lwip_pic zto_pic natpmp_pic miniupnpc_pic) set_target_properties (${DYNAMIC_LIB_NAME} PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}") set_target_properties (${DYNAMIC_LIB_NAME} PROPERTIES OUTPUT_NAME ${DYNAMIC_LIB_OUTPUT_NAME} - WINDOWS_EXPORT_ALL_SYMBOLS true) + WINDOWS_EXPORT_ALL_SYMBOLS true) target_link_libraries ( ${DYNAMIC_LIB_NAME} ${ws2_32_LIBRARY_PATH} @@ -463,7 +463,7 @@ endif () # xcode framework if (IN_XCODE) - include_directories (${frameworkHeaderGlob}) + include_directories (${frameworkHeaderGlob}) add_library(${XCODE_FRAMEWORK_NAME} STATIC @@ -520,11 +520,11 @@ set (PUBLIC_ZT_HEADERS ${PROJECT_SOURCE_DIR}/include/ZeroTierSockets.h) set_target_properties(${STATIC_LIB_NAME} PROPERTIES PUBLIC_HEADER "${PUBLIC_ZT_HEADERS}") install (TARGETS ${STATIC_LIB_NAME} - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - PUBLIC_HEADER DESTINATION include + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include ) install (TARGETS ${DYNAMIC_LIB_NAME} - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib ) From f0d6330735ac31c83c881e3e778482199ecab60c Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 28 May 2020 18:32:39 -0700 Subject: [PATCH 63/78] Update C++, Objective-C and Swift examples --- examples/cpp/adhoc.cpp | 83 +++--- examples/cpp/client.cpp | 86 ++++--- examples/cpp/comprehensive.cpp | 458 +++++++++++++++++++-------------- examples/cpp/earthtest.cpp | 82 +++--- examples/cpp/server.cpp | 88 ++++--- examples/objective-c/adhoc.m | 26 +- examples/swift/Makefile | 2 + examples/swift/main.swift | 267 ++++++++++++++----- 8 files changed, 698 insertions(+), 394 deletions(-) create mode 100644 examples/swift/Makefile diff --git a/examples/cpp/adhoc.cpp b/examples/cpp/adhoc.cpp index ec6ee0e..cdad3f6 100644 --- a/examples/cpp/adhoc.cpp +++ b/examples/cpp/adhoc.cpp @@ -12,7 +12,7 @@ * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api @@ -51,12 +51,12 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -74,7 +74,7 @@ * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -91,10 +91,19 @@ #include "ZeroTierSockets.h" -bool nodeReady = false; -bool networkReady = false; +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; -// Example callbacks +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ void myZeroTierEventCallback(void *msgPtr) { struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; @@ -102,11 +111,12 @@ void myZeroTierEventCallback(void *msgPtr) // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - nodeReady = true; + myNode.id = msg->node->address; + myNode.online = true; } if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - nodeReady = false; + myNode.online = false; } // Virtual network events @@ -116,7 +126,7 @@ void myZeroTierEventCallback(void *msgPtr) } if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } + } if (msg->eventCode == 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->nwid); @@ -124,7 +134,7 @@ void myZeroTierEventCallback(void *msgPtr) if (msg->eventCode == 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->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); @@ -132,17 +142,15 @@ void myZeroTierEventCallback(void *msgPtr) // Network stack events if (msg->eventCode == ZTS_EVENT_NETIF_UP) { - printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", msg->netif->nwid, msg->netif->mac, msg->netif->mtu); - networkReady = true; } if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { - printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", msg->netif->nwid, msg->netif->mac); - networkReady = true; } // Address events @@ -150,19 +158,32 @@ void myZeroTierEventCallback(void *msgPtr) char ipstr[ZTS_INET6_ADDRSTRLEN]; 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", + printf("ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", msg->addr->nwid, ipstr); } // Peer events - // Don't worry if you don't recognize a peer ID, it's most likely our infrastructure - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- There is now a direct path to peer %llx\n", - msg->peer->address); - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- No direct path to peer %llx\n", - msg->peer->address); + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } } } @@ -191,7 +212,7 @@ be taken to avoid exposing vulnerable services or sharing unwanted files or othe */ -int main(int argc, char **argv) +int main(int argc, char **argv) { if (argc != 5) { printf("\nlibzt example\n"); @@ -212,7 +233,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { zts_delay_ms(50); } + while (!myNode.online) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(adhoc_nwid)) != ZTS_ERR_OK) { @@ -220,7 +241,7 @@ int main(int argc, char **argv) exit(1); } printf("Joining network %llx\n", adhoc_nwid); - while (!networkReady) { zts_delay_ms(50); } + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } // Idle and just show callback events, stack statistics, etc diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index 75b35d3..e247094 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -9,10 +9,19 @@ #include "ZeroTierSockets.h" -bool nodeReady = false; -bool networkReady = false; +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; -// Example callbacks +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ void myZeroTierEventCallback(void *msgPtr) { struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; @@ -20,11 +29,12 @@ void myZeroTierEventCallback(void *msgPtr) // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - nodeReady = true; + myNode.id = msg->node->address; + myNode.online = true; } if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - nodeReady = false; + myNode.online = false; } if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); @@ -37,7 +47,7 @@ void myZeroTierEventCallback(void *msgPtr) } if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } + } if (msg->eventCode == 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->nwid); @@ -45,12 +55,12 @@ void myZeroTierEventCallback(void *msgPtr) if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", msg->network->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == 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->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); @@ -61,38 +71,52 @@ void myZeroTierEventCallback(void *msgPtr) char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { char ipstr[ZTS_INET6_ADDRSTRLEN]; 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 --- This node's virtual address on network %llx is %s\n", + printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { char ipstr[ZTS_INET6_ADDRSTRLEN]; 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); - // A direct path is known for nodeId - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); - // No direct path is known for nodeId + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } } } @@ -104,7 +128,7 @@ void myZeroTierEventCallback(void *msgPtr) * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api @@ -143,12 +167,12 @@ void myZeroTierEventCallback(void *msgPtr) * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -166,7 +190,7 @@ void myZeroTierEventCallback(void *msgPtr) * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -178,7 +202,7 @@ void myZeroTierEventCallback(void *msgPtr) * */ -int main(int argc, char **argv) +int main(int argc, char **argv) { if (argc != 6) { printf("\nlibzt example client\n"); @@ -208,7 +232,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { zts_delay_ms(50); } + while (!myNode.online) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -217,7 +241,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { zts_delay_ms(50); } + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } // Socket-like API example @@ -243,7 +267,7 @@ int main(int argc, char **argv) exit(1); } zts_delay_ms(250); - } + } else { printf("Connected.\n"); break; diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index eeaf08a..46f25c2 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -12,7 +12,7 @@ * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api @@ -51,12 +51,12 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -74,7 +74,7 @@ * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -88,126 +88,45 @@ #include #include +#include #include "ZeroTierSockets.h" -bool nodeReady = false; -bool networkReady = false; +#include "winsock.h" -// Example callbacks -void myZeroTierEventCallback(void *msgPtr) +struct Node { - struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; - // Node events - if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { - printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - nodeReady = true; - } - if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { - printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - nodeReady = false; - } - if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { - printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); - } - - // Virtual network events - if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { - printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", - msg->network->nwid); - } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } - if (msg->eventCode == 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->nwid); - } - if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { - printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", - msg->network->nwid); - networkReady = true; - } - if (msg->eventCode == 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->nwid); - networkReady = true; - } - if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { - printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); - } - - // Network stack events - if (msg->eventCode == ZTS_EVENT_NETIF_UP) { - printf("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", - msg->netif->nwid, - msg->netif->mac, - msg->netif->mtu); - networkReady = true; - } - if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { - printf("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", - msg->netif->nwid, - msg->netif->mac); - networkReady = true; - } - - // Address events - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[ZTS_INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", - msg->addr->nwid, ipstr); - } - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[ZTS_INET6_ADDRSTRLEN]; - 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 --- This node's virtual address on network %llx is %s\n", - msg->addr->nwid, ipstr); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[ZTS_INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg->addr->nwid); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[ZTS_INET6_ADDRSTRLEN]; - 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg->addr->nwid); - } - - // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); - // A direct path is known for nodeId - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); - // No direct path is known for nodeId - } +void printNodeDetails(const char *msgStr, struct zts_node_details *d) +{ + printf("\n%s\n", msgStr); + printf("\t- id : %llx\n", d->address); + printf("\t- version : %d.%d.%d\n", d->versionMajor, d->versionMinor, d->versionRev); + printf("\t- primaryPort : %d\n", d->primaryPort); + printf("\t- secondaryPort : %d\n", d->secondaryPort); } -void printPeerDetails(struct zts_peer_details *pd) +void printPeerDetails(const char *msgStr, struct zts_peer_details *d) { - printf("\npeer=%llx, latency=%d, version=%d.%d.%d, pathCount=%d\n", - pd->address, - pd->latency, - pd->versionMajor, - pd->versionMinor, - pd->versionRev, - pd->pathCount); + printf("\n%s\n", msgStr); + printf("\t- peer : %llx\n", d->address); + printf("\t- role : %llx\n", d->role); + printf("\t- latency : %llx\n", d->latency); + printf("\t- pathCount : %llx\n", d->pathCount); + printf("\t- version : %d.%d.%d\n", d->versionMajor, d->versionMinor, d->versionRev); + printf("\t- paths:\n"); + // Print all known paths for each peer - for (unsigned int j=0; jpathCount; j++) { + for (unsigned int j=0; jpathCount; j++) { char ipstr[ZTS_INET6_ADDRSTRLEN]; int port = 0; - struct zts_sockaddr *sa = (struct zts_sockaddr *)&(pd->paths[j].address); + struct zts_sockaddr *sa = (struct zts_sockaddr *)&(d->paths[j].address); if (sa->sa_family == ZTS_AF_INET) { // TODO: Probably broken struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)sa; zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); @@ -217,51 +136,219 @@ void printPeerDetails(struct zts_peer_details *pd) struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)sa; zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); } - printf("\tpath[%d]=%s, port=%d\n", j, ipstr, port); + printf("\t - %15s : %6d\n", ipstr, port); } + printf("\n"); } -void getSinglePeerDetails(uint64_t peerId) +void printNetworkDetails(const char *msgStr, struct zts_network_details *d) { - struct zts_peer_details pd; - int err = zts_get_peer(&pd, peerId); + printf("\n%s\n", msgStr); + printf("\t- nwid : %llx\n", d->nwid); + printf("\t- mac : %lx\n", d->mac); + printf("\t- name : %s\n", d->name); + printf("\t- type : %d\n", d->type); + /* MTU for the virtual network can be set via our web API */ + printf("\t- mtu : %d\n", d->mtu); + printf("\t- dhcp : %d\n", d->dhcp); + printf("\t- bridge : %d\n", d->bridge); + printf("\t- broadcastEnabled : %d\n", d->broadcastEnabled); + printf("\t- portError : %d\n", d->portError); + printf("\t- netconfRevision : %d\n", d->netconfRevision); + printf("\t- routeCount : %d\n", d->routeCount); + printf("\t- multicastSubscriptionCount : %d\n", d->multicastSubscriptionCount); - if (err == ZTS_ERR_OK) { - printf("(%d) call succeeded\n", err); - printPeerDetails(&pd); - } if (err == ZTS_ERR_ARG) { - printf("(%d) invalid argument\n", err); - return; - } if (err == ZTS_ERR_SERVICE) { - printf("(%d) error: invalid API operation or service error\n", err); - return; - } if (err == ZTS_ERR_NO_RESULT) { - printf("(%d) error: object or result not found\n", err); - return; + for (int i=0; imulticastSubscriptionCount; i++) { + printf("\t - mac=%llx, adi=%x\n", d->multicastSubscriptions[i].mac, d->multicastSubscriptions[i].adi); } -} -// Similar to "zerotier-cli listpeers" -void getAllPeerDetails() -{ - struct zts_peer_details pd[128]; - /* This number should be large enough to handle the - expected number of peers. This call can also get - expensive for large numbers of peers. Consider using - get_peer(struct zts_peer_details *pds, uint64_t peerId) - instead */ - unsigned int num = 128; - int err; - if ((err = zts_get_peers(pd, &num)) < 0) { - printf("error (%d)\n", err); - return; - } - if (num) { - printf("num=%d\n", num); - for (unsigned int i=0; iassignedAddressCount; i++) { + if (d->assignedAddresses[i].ss_family == ZTS_AF_INET) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(d->assignedAddresses[i]); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("\t - %s\n",ipstr); + } + if (d->assignedAddresses[i].ss_family == ZTS_AF_INET6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(d->assignedAddresses[i]); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); + printf("\t - %s\n",ipstr); } } + + printf("\t- routes:\n"); + + for (int i=0; irouteCount; i++) { + if (d->routes[i].target.ss_family == ZTS_AF_INET) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(d->routes[i].target); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("\t - target : %s\n",ipstr); + in4 = (struct zts_sockaddr_in*)&(d->routes[i].via); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("\t - via : %s\n",ipstr); + } + if (d->routes[i].target.ss_family == ZTS_AF_INET6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(d->routes[i].target); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); + printf("\t - target : %s\n",ipstr); + in6 = (struct zts_sockaddr_in6*)&(d->routes[i].via); + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); + printf("\t - via : %s\n",ipstr); + } + printf("\t - flags : %d\n", d->routes[i].flags); + printf("\t - metric : %d\n", d->routes[i].metric); + } +} + +void printNetifDetails(const char *msgStr, struct zts_netif_details *d) +{ + printf("\n%s\n", msgStr); + printf("\t- nwid : %llx\n", d->nwid); + printf("\t- mac : %llx\n", d->mac); + printf("\t- mtu : %d\n", d->mtu); +} + +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ +void myZeroTierEventCallback(void *msgPtr) +{ + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + printf("code=%d\n", msg->eventCode); + + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printNodeDetails("nZTS_EVENT_NODE_ONLINE", msg->node); + myNode.id = msg->node->address; + myNode.online = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("\nZTS_EVENT_NODE_OFFLINE --- Check your Internet connection, router, firewall, etc. What ports are you blocking?\n"); + myNode.online = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("\nZTS_EVENT_NODE_NORMAL_TERMINATION -- A call to zts_start() will restart ZeroTier.\n"); + myNode.online = false; + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("\nZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("\nZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) { + printf("\nZTS_EVENT_NETWORK_ACCESS_DENIED --- Access to virtual network %llx has been denied. Did you authorize the node yet?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printNetworkDetails("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received.", msg->network); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP6) { + printNetworkDetails("ZTS_EVENT_NETWORK_READY_IP6 --- Network config received.", msg->network); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("\nZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_UPDATE) { + printNetworkDetails("ZTS_EVENT_NETWORK_UPDATE --- Network config received.", msg->network); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("\nZTS_EVENT_ADDR_ADDED_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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("\nZTS_EVENT_ADDR_ADDED_IP6 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("\nZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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("\nZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printPeerDetails("ZTS_EVENT_PEER_DIRECT --- A direct path is known.", msg->peer); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printPeerDetails("ZTS_EVENT_PEER_RELAY --- No direct path known.", msg->peer); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printPeerDetails("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered.", msg->peer); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printPeerDetails("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died.", msg->peer); + } + } + + // Network stack (netif) events (used for debugging, can be ignored) + if (msg->eventCode == ZTS_EVENT_NETIF_UP) { + printNetifDetails("ZTS_EVENT_NETIF_UP --- No action required.", msg->netif); + } + if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { + printNetifDetails("ZTS_EVENT_NETIF_DOWN --- No action required.", msg->netif); + } + if (msg->eventCode == ZTS_EVENT_NETIF_REMOVED) { + printNetifDetails("ZTS_EVENT_NETIF_REMOVED --- No action required.", msg->netif); + } + if (msg->eventCode == ZTS_EVENT_NETIF_LINK_UP) { + printNetifDetails("ZTS_EVENT_NETIF_LINK_UP --- No action required.", msg->netif); + } + if (msg->eventCode == ZTS_EVENT_NETIF_LINK_DOWN) { + printNetifDetails("ZTS_EVENT_NETIF_LINK_DOWN --- No action required.", msg->netif); + } + // Network stack events (used for debugging, can be ignored) + if (msg->eventCode == ZTS_EVENT_STACK_UP) { + printf("\nZTS_EVENT_STACK_UP --- No action required.\n"); + } + if (msg->eventCode == ZTS_EVENT_STACK_DOWN) { + printf("\nZTS_EVENT_STACK_DOWN --- No action required. An app restart is needed to use ZeroTier again.\n"); + } +} + +void get6PLANEAddressOfPeer(uint64_t peerId, uint64_t nwId) +{ + char peerAddrStr[ZTS_INET6_ADDRSTRLEN] = {0}; + struct zts_sockaddr_storage sixplane_addr; + zts_get_6plane_addr(&sixplane_addr, nwId, peerId); + struct zts_sockaddr_in6 *p6 = (struct zts_sockaddr_in6*)&sixplane_addr; + zts_inet_ntop(ZTS_AF_INET6, &(p6->sin6_addr), peerAddrStr, ZTS_INET6_ADDRSTRLEN); + printf("6PLANE address of peer is: %s\n", peerAddrStr); } struct zts_stats_proto protoSpecificStats; @@ -283,35 +370,36 @@ void display_stack_stats() printf("tcp.drop=%d\n", protoSpecificStats.drop); } -int main(int argc, char **argv) +int main(int argc, char **argv) { + fprintf(stderr, "AF_INET=%d, SOCK_STREAM=%d, IPPROTO_TCP=%d", AF_INET, SOCK_STREAM, IPPROTO_TCP); if (argc != 4) { printf("\nlibzt example server\n"); printf("comprehensive \n"); exit(0); } + std::string configPath = std::string(argv[1]); uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join int ztServicePort = atoi(argv[3]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) - + // Bring up ZeroTier service and join network + // Enable/Disable caching of network details in networks.d + // (read function documentation before disabling!) + // zts_allow_network_caching(0) + + // Enable/Disable caching of peer details in peers.d + // (read function documentation before disabling!) + // zts_allow_network_caching(1) + int err = ZTS_ERR_OK; - // Disable caching of network details in networks.d - // (read function documentation before disabling!) - // zts_set_network_caching(false) - - // Disable caching of peer details in peers.d - // (read function documentation before disabling!) - // zts_set_network_caching(false) - - if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { + if((err = zts_start(configPath.c_str(), &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { zts_delay_ms(50); } - printf("This node ID is %llx\n", zts_get_node_id()); + while (!myNode.online) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -320,35 +408,29 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { zts_delay_ms(50); } - - // Get multiple peer's details - getAllPeerDetails(); - - // Get a single peer's details - uint64_t peerId = 0xabcdef1234; - getSinglePeerDetails(peerId); - int status = -1; - - // Get status of the node/service - status = zts_get_node_status(); - printf("zts_get_node_status()=%d\n", status); - - // Get status of a network - status = zts_get_network_status(nwid); - printf("zts_get_network_status()=%d\n", status); + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } // Idle and just show callback events, stack statistics, etc + // Alternatively, this is where you could start making calls to the socket API - while (true) { - zts_delay_ms(1000); - status = zts_get_node_status(); - printf("zts_get_node_status()=%d\n", status); + /* + while(true) { display_stack_stats(); + zts_delay_ms(1000); } + */ - // Shut down service and stack threads - + int delay = 500000; + printf("This program will delay for %d seconds and then shut down.\n", (delay / 1000)); + zts_delay_ms(delay); + //printf("Leaving network %llx\n", nwid); + //zts_leave(nwid); + //zts_delay_ms(3000); /* added for demo purposes so that events show up */ + printf("Stopping ZeroTier\n"); zts_stop(); + zts_delay_ms(delay); /* added for demo purposes so that events show up */ + printf("Stopping network stack\n"); + zts_free(); + zts_delay_ms(delay); /* added for demo purposes so that events show up */ return 0; } diff --git a/examples/cpp/earthtest.cpp b/examples/cpp/earthtest.cpp index a6dff6a..89ee426 100644 --- a/examples/cpp/earthtest.cpp +++ b/examples/cpp/earthtest.cpp @@ -12,7 +12,7 @@ * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api @@ -51,12 +51,12 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -74,7 +74,7 @@ * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -91,25 +91,35 @@ #include "ZeroTierSockets.h" -bool nodeReady = false; -bool networkReady = false; +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; -// Example callbacks +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ void myZeroTierEventCallback(void *msgPtr) { struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - nodeReady = true; + myNode.id = msg->node->address; + myNode.online = true; } if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - nodeReady = false; + myNode.online = false; } if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } + } if (msg->eventCode == 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->nwid); @@ -117,7 +127,7 @@ void myZeroTierEventCallback(void *msgPtr) if (msg->eventCode == 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->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); @@ -126,28 +136,42 @@ void myZeroTierEventCallback(void *msgPtr) char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- Join %llx and ping me at %s\n", + printf("ZTS_EVENT_ADDR_NEW_IP4 --- Join %llx and ping me at %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { char ipstr[ZTS_INET6_ADDRSTRLEN]; 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", + printf("ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", msg->addr->nwid, ipstr); } - // Don't worry if you don't recognize a peer ID, it's most likely our infrastructure - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- There is now a direct path to peer %llx\n", - msg->peer->address); - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- No direct path to peer %llx\n", - msg->peer->address); + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } } } -int main(int argc, char **argv) +int main(int argc, char **argv) { if (argc != 3) { printf("\nlibzt example\n"); @@ -155,7 +179,7 @@ int main(int argc, char **argv) exit(0); } int ztServicePort = atoi(argv[2]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) - + int err = ZTS_ERR_OK; zts_allow_network_caching(false); @@ -164,7 +188,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { zts_delay_ms(50); } + while (!myNode.online) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); uint64_t nwid = 0x8056c2e21c000001; @@ -174,7 +198,7 @@ int main(int argc, char **argv) exit(1); } printf("Joining network %llx\n", nwid); - while (!networkReady) { zts_delay_ms(50); } + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } // Idle and just show callback events, stack statistics, etc diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp index 51e61bb..3588937 100644 --- a/examples/cpp/server.cpp +++ b/examples/cpp/server.cpp @@ -9,10 +9,19 @@ #include "ZeroTierSockets.h" -bool nodeReady = false; -bool networkReady = false; +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc +} myNode; -// Example callbacks +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ void myZeroTierEventCallback(void *msgPtr) { struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; @@ -20,11 +29,12 @@ void myZeroTierEventCallback(void *msgPtr) // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - nodeReady = true; + myNode.id = msg->node->address; + myNode.online = true; } if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - nodeReady = false; + myNode.online = false; } if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); @@ -37,7 +47,7 @@ void myZeroTierEventCallback(void *msgPtr) } if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } + } if (msg->eventCode == 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->nwid); @@ -45,12 +55,12 @@ void myZeroTierEventCallback(void *msgPtr) if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", msg->network->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == 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->nwid); - networkReady = true; + myNode.joinedAtLeastOneNetwork = true; } if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); @@ -61,38 +71,52 @@ void myZeroTierEventCallback(void *msgPtr) char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { char ipstr[ZTS_INET6_ADDRSTRLEN]; 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 --- This node's virtual address on network %llx is %s\n", + printf("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", msg->addr->nwid, ipstr); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { char ipstr[ZTS_INET_ADDRSTRLEN]; struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { char ipstr[ZTS_INET6_ADDRSTRLEN]; 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + printf("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", ipstr, msg->addr->nwid); } // Peer events - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg->peer->address); - // A direct path is known for nodeId - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg->peer->address); - // No direct path is known for nodeId + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } } } @@ -104,7 +128,7 @@ void myZeroTierEventCallback(void *msgPtr) * the location given in the first argument to zts_start(path, ...). If you accidentally * duplicate the identity files and use them simultaneously in a different node instance * you will experience undefined behavior and it is likely nothing will work. - * + * * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join * your network, otherwise nothing will happen. This can be done manually or via * our web API: https://my.zerotier.com/help/api @@ -143,12 +167,12 @@ void myZeroTierEventCallback(void *msgPtr) * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -166,7 +190,7 @@ void myZeroTierEventCallback(void *msgPtr) * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -178,7 +202,7 @@ void myZeroTierEventCallback(void *msgPtr) * */ -int main(int argc, char **argv) +int main(int argc, char **argv) { if (argc != 5) { printf("\nlibzt example server\n"); @@ -188,7 +212,7 @@ int main(int argc, char **argv) uint64_t nwid = strtoull(argv[2],NULL,16); // Network ID to join int serverBindPort = atoi(argv[3]); // Port the application should bind to int ztServicePort = atoi(argv[4]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) - + struct zts_sockaddr_in in4, acc_in4; in4.sin_port = zts_htons(serverBindPort); #if defined(_WIN32) @@ -208,7 +232,7 @@ int main(int argc, char **argv) exit(1); } printf("Waiting for node to come online...\n"); - while (!nodeReady) { zts_delay_ms(50); } + while (!myNode.online) { zts_delay_ms(50); } printf("This node's identity is stored in %s\n", argv[1]); if((err = zts_join(nwid)) != ZTS_ERR_OK) { @@ -217,7 +241,7 @@ int main(int argc, char **argv) } printf("Joining network %llx\n", nwid); printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); - while (!networkReady) { zts_delay_ms(50); } + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } // Socket-like API example @@ -237,7 +261,7 @@ int main(int argc, char **argv) printf("Error placing socket in LISTENING state (fd=%d, ret=%d, zts_errno=%d). Exiting.\n", fd, err, zts_errno); exit(1); } - + int bytes=0; char recvBuf[128]; memset(recvBuf, 0, sizeof(recvBuf)); diff --git a/examples/objective-c/adhoc.m b/examples/objective-c/adhoc.m index 46b2c67..46ab811 100644 --- a/examples/objective-c/adhoc.m +++ b/examples/objective-c/adhoc.m @@ -60,12 +60,12 @@ * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -83,7 +83,7 @@ * are a few guidelines: * * If you are calling a zts_* function, use the appropriate ZTS_* constants: - * + * * zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) * zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) * @@ -119,7 +119,7 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) } if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { NSLog(@"ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } + } if (msg->eventCode == ZTS_EVENT_NETWORK_ACCESS_DENIED) { NSLog(@"ZTS_EVENT_NETWORK_ACCESS_DENIED --- Access to virtual network %llx has been denied. Did you authorize the node yet?\n", msg->network->nwid); @@ -134,17 +134,17 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) } // Network stack events if (msg->eventCode == ZTS_EVENT_NETIF_UP) { - NSLog(@"ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", + NSLog(@"ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", msg->netif->nwid, msg->netif->mac, msg->netif->mtu); networkReady = true; } if (msg->eventCode == ZTS_EVENT_NETIF_DOWN) { - NSLog(@"ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", + NSLog(@"ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", msg->netif->nwid, msg->netif->mac); - + networkReady = true; } // Address events @@ -152,7 +152,7 @@ void myZeroTierEventCallback(struct zts_callback_msg *msg) char ipstr[INET6_ADDRSTRLEN]; struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg->addr->addr); inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - NSLog(@"ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", + NSLog(@"ZTS_EVENT_ADDR_NEW_IP6 --- Join %llx and ping me at %s\n", msg->addr->nwid, ipstr); } // Peer events @@ -192,7 +192,7 @@ be taken to avoid exposing vulnerable services or sharing unwanted files or othe */ -int main(int argc, char **argv) +int main(int argc, char **argv) { if (argc != 5) { NSLog(@"\nlibzt example\n"); diff --git a/examples/swift/Makefile b/examples/swift/Makefile new file mode 100644 index 0000000..91247b1 --- /dev/null +++ b/examples/swift/Makefile @@ -0,0 +1,2 @@ +all: + swiftc -g -lc++ -import-objc-header ../../include/ZeroTierSockets.h ../../lib/debug/macos-x86_64/zt.framework/zt main.swift -o main diff --git a/examples/swift/main.swift b/examples/swift/main.swift index bb82621..5056f3c 100644 --- a/examples/swift/main.swift +++ b/examples/swift/main.swift @@ -1,10 +1,5 @@ /** - * libzt Swift example - * - * swiftc -lc++ -import-objc-header ../../include/ZeroTierSockets.h -L. -lzt main.swift -o main; - * ./main - * - * TODO: This example is incomplete + * I'll order you a pizza if you can rewrite this in modern idomatic Swift */ import Swift @@ -56,12 +51,12 @@ import Foundation * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors * returned by these functions can be any of the following: * - * ZTS_ERR_OK 0 // No error - * ZTS_ERR_SOCKET -1 // Socket error, see zts_errno - * ZTS_ERR_SERVICE -2 // You probably did something at the wrong time - * ZTS_ERR_ARG -3 // Invalid argument - * ZTS_ERR_NO_RESULT -4 // No result (not necessarily an error) - * ZTS_ERR_GENERAL -5 // Consider filing a bug report + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report * * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). * Errors returned by these functions can be the same as the above. With @@ -91,6 +86,119 @@ import Foundation * */ +let printNodeDetails : @convention(c) (UnsafeMutableRawPointer?) -> Void = +{ + (msgPtr) -> Void in + let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) + let d = msg?.pointee.node; + print(String(format: "\t- id : %llx", d!.pointee.address)); + print(String(format: "\t- version : %d.%d.%d", d!.pointee.versionMajor, d!.pointee.versionMinor, d!.pointee.versionRev)); + print(String(format: "\t- primaryPort : %d", d!.pointee.primaryPort)); + print(String(format: "\t- secondaryPort : %d", d!.pointee.secondaryPort)); +} + +/* +func convertTupleToArray(from tuple: Tuple) -> [Value] { + let tupleMirror = Mirror(reflecting: tuple) + func convert(child: Mirror.Child) -> Value? { + let valueMirror = Mirror(reflecting: child.value) + return child.value as? Value + } + return tupleMirror.children.flatMap(convert) +} +*/ + +let printNetworkDetails : @convention(c) (UnsafeMutableRawPointer?) -> Void = +{ + (msgPtr) -> Void in + let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) + let d = msg?.pointee.network; + let name = ""; // String(d!.pointee.name); + + print(String(format: "\t- nwid : %llx", d!.pointee.nwid)); + print(String(format: "\t- mac : %lx", d!.pointee.mac)); + print(String(format: "\t- name : %s", name)); + print(String(format: "\t- type : %d", Int(d!.pointee.type.rawValue))); + /* MTU for the virtual network can be set via our web API */ + print(String(format: "\t- mtu : %d", d!.pointee.mtu)); + print(String(format: "\t- dhcp : %d", d!.pointee.dhcp)); + print(String(format: "\t- bridge : %d", d!.pointee.bridge)); + print(String(format: "\t- broadcastEnabled : %d", d!.pointee.broadcastEnabled)); + print(String(format: "\t- portError : %d", d!.pointee.portError)); + print(String(format: "\t- netconfRevision : %d", d!.pointee.netconfRevision)); + print(String(format: "\t- routeCount : %d", d!.pointee.routeCount)); + print(String(format: "\t- multicastSubscriptionCount : %d", d!.pointee.multicastSubscriptionCount)); +/* + var addresses: [zts_sockaddr_storage] = convertTupleToArray(from: d!.pointee.assignedAddresses) + + print("\t- addresses:\n"); + for i in 0...d!.pointee.assignedAddressCount { + if (addresses[Int(i)].ss_family == ZTS_AF_INET) { + // Allocate a byte array that can hold the largest possible IPv4 human-readable string + var ipCharByteArray = Array(repeating: 0, count: Int(ZTS_INET_ADDRSTRLEN)) + // Cast unsafe pointer from zts_sockaddr_storage to zts_sockaddr_in + var addr:zts_sockaddr_in = withUnsafePointer(to: &(addresses[Int(i)])) { + $0.withMemoryRebound(to: zts_sockaddr_in.self, capacity: 1) { + $0.pointee + } + } + // Pass unsafe pointer (addr) to a ntop to convert into human-readable byte array + zts_inet_ntop(ZTS_AF_INET, &(addr.sin_addr), &ipCharByteArray, UInt32(ZTS_INET_ADDRSTRLEN)) + //print(ipCharByteArray) // [49, 55, 50, 46, 50, 55, 46, 49, 49, 54, 46, 49, 54, 55, 0, 0] + // Somehow convery Int8 byte array to Swift String ??? + //let ipString = String(bytes: ipStr, encoding: .utf8) + //print(ipString) + + // Pass unsafe pointer (addr) to a ntop to convert into human-readable byte array + // convert to UInt8 byte array + let uintArray = ipCharByteArray.map { UInt8(bitPattern: $0) } + if let string = String(bytes: uintArray, encoding: .utf8) { + print("\t\t-", string) + } + } + if (addresses[Int(i)].ss_family == ZTS_AF_INET6) { + // ... + } + } +*/ +/* + print("\t- routes:\n"); + + for i in 0...d!.pointee.routeCount { + // ... + } +*/ +} + +let printPeerDetails : @convention(c) (UnsafeMutableRawPointer?) -> Void = +{ + (msgPtr) -> Void in + let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) + let d = msg?.pointee.peer; + print(String(format: "\t- peer : %llx", d!.pointee.address)); + print(String(format: "\t- role : %d", Int(d!.pointee.role.rawValue))); + print(String(format: "\t- latency : %llx", d!.pointee.latency)); + print(String(format: "\t- pathCount : %llx", d!.pointee.pathCount)); + print(String(format: "\t- version : %d.%d.%d", d!.pointee.versionMajor, d!.pointee.versionMinor, d!.pointee.versionRev)); + print(String(format: "\t- paths:\n")); + +/* + for i in 0...d!.pointee.pathCount { + // ... + } +*/ +} + +let printNetifDetails : @convention(c) (UnsafeMutableRawPointer?) -> Void = +{ + (msgPtr) -> Void in + let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) + let d = msg?.pointee.netif; + print(String(format: "\t- nwid : %llx", d!.pointee.nwid)); + print(String(format: "\t- mac : %llx", d!.pointee.mac)); + print(String(format: "\t- mtu : %d", d!.pointee.mtu)); +} + var nodeReady:Bool = false var networkReady:Bool = false @@ -99,16 +207,18 @@ let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void (msgPtr) -> Void in let msg = msgPtr?.bindMemory(to: zts_callback_msg.self, capacity: 1) - var eventCode = msg!.pointee.eventCode - - let node = msg?.pointee.node; + let eventCode = msg!.pointee.eventCode let network = msg?.pointee.network; + let peer = msg?.pointee.peer; + + switch Int32(eventCode) + { + case ZTS_EVENT_NODE_UP: + print("ZTS_EVENT_NODE_UP (you can ignore this)\n") - switch Int32(eventCode) - { case ZTS_EVENT_NODE_ONLINE: - let nodeId:UInt64 = node!.pointee.address - print(String(format: "ZTS_EVENT_NODE_ONLINE (%llx)", nodeId)) + print("ZTS_EVENT_NODE_ONLINE\n") + printNodeDetails(msg) nodeReady = true; case ZTS_EVENT_NODE_OFFLINE: @@ -144,71 +254,68 @@ let myZeroTierEventCallback : @convention(c) (UnsafeMutableRawPointer?) -> Void let networkId:UInt64 = network!.pointee.nwid print(String(format: "ZTS_EVENT_NETWORK_DOWN (%llx)", networkId)) -/* - // Network stack events - case ZTS_EVENT_NETIF_UP: - print("ZTS_EVENT_NETIF_UP --- network=%llx, mac=%llx, mtu=%d\n", - msg.netif->nwid, - msg.netif->mac, - msg.netif->mtu) - //networkReady = true; + case ZTS_EVENT_NETWORK_UPDATE: + print("ZTS_EVENT_NETWORK_UPDATE\n") + printNetworkDetails(msg) - case ZTS_EVENT_NETIF_DOWN: - print("ZTS_EVENT_NETIF_DOWN --- network=%llx, mac=%llx\n", - msg.netif->nwid, - msg.netif->mac) - //networkReady = true; - // Address events case ZTS_EVENT_ADDR_ADDED_IP4: - print("ZTS_EVENT_ADDR_ADDED_IP4") - /* - char ipstr[INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg.addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - print("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", - msg.addr->nwid, ipstr) - */ + print("ZTS_EVENT_ADDR_ADDED_IP4\n") case ZTS_EVENT_ADDR_ADDED_IP6: - print("ZTS_EVENT_ADDR_ADDED_IP6") - /* - char ipstr[INET6_ADDRSTRLEN]; - struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg.addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - print("ZTS_EVENT_ADDR_NEW_IP6 --- This node's virtual address on network %llx is %s\n", - msg.addr->nwid, ipstr) - */ + print("ZTS_EVENT_ADDR_ADDED_IP6\n") case ZTS_EVENT_ADDR_REMOVED_IP4: - print("ZTS_EVENT_ADDR_REMOVED_IP4") - /* - char ipstr[INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg.addr->addr); - inet_ntop(AF_INET, &(in4->sin_addr), ipstr, INET_ADDRSTRLEN); - print("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg.addr->nwid) - */ + print("ZTS_EVENT_ADDR_REMOVED_IP4\n") case ZTS_EVENT_ADDR_REMOVED_IP6: - print("ZTS_EVENT_ADDR_REMOVED_IP6") - /* - char ipstr[INET6_ADDRSTRLEN]; - struct zts_sockaddr_in6 *in6 = (struct zts_sockaddr_in6*)&(msg.addr->addr); - inet_ntop(AF_INET6, &(in6->sin6_addr), ipstr, INET6_ADDRSTRLEN); - print("ZTS_EVENT_ADDR_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg.addr->nwid) - */ - // Peer events + print("ZTS_EVENT_ADDR_REMOVED_IP6\n") + + case ZTS_EVENT_PEER_DIRECT: - print("ZTS_EVENT_PEER_DIRECT --- node=%llx\n", msg.peer->address) + let peerId:UInt64 = peer!.pointee.address + print(String(format: "ZTS_EVENT_PEER_DIRECT (%llx)", peerId)) + printPeerDetails(msg) + case ZTS_EVENT_PEER_RELAY: - print("ZTS_EVENT_PEER_RELAY --- node=%llx\n", msg.peer->address) + let peerId:UInt64 = peer!.pointee.address + print(String(format: "ZTS_EVENT_PEER_RELAY (%llx)", peerId)) + printPeerDetails(msg) + + case ZTS_EVENT_PEER_PATH_DISCOVERED: + let peerId:UInt64 = peer!.pointee.address + print(String(format: "ZTS_EVENT_PEER_PATH_DISCOVERED (%llx)", peerId)) + printPeerDetails(msg) + + case ZTS_EVENT_PEER_PATH_DEAD: + let peerId:UInt64 = peer!.pointee.address + print(String(format: "ZTS_EVENT_PEER_PATH_DEAD (%llx)", peerId)) + printPeerDetails(msg) -*/ + case ZTS_EVENT_NETIF_UP: + print("ZTS_EVENT_NETIF_UP\n") + + case ZTS_EVENT_NETIF_DOWN: + print("ZTS_EVENT_NETIF_DOWN\n") + + case ZTS_EVENT_NETIF_REMOVED: + print("ZTS_EVENT_NETIF_REMOVED\n") + + case ZTS_EVENT_NETIF_LINK_UP: + print("ZTS_EVENT_NETIF_LINK_UP\n") + + case ZTS_EVENT_NETIF_LINK_DOWN: + print("ZTS_EVENT_NETIF_LINK_DOWN\n") + + case ZTS_EVENT_STACK_UP: + print("ZTS_EVENT_STACK_UP\n") + + case ZTS_EVENT_STACK_DOWN: + print("ZTS_EVENT_STACK_DOWN\n") + default: - print("UNKNOWN_EVENT") + print("UNKNOWN_EVENT: ", eventCode) } } @@ -221,6 +328,26 @@ func main() } print("Joining network") + let nwId : UInt64 = 0x0123456789abcdef; // Specify your network ID here + zts_join(nwId); + + // create address structure + let addr_str = "0.0.0.0" + let port = 8080 + var in4 = zts_sockaddr_in(sin_len: UInt8(MemoryLayout.size), + sin_family: UInt8(ZTS_AF_INET), + sin_port: UInt16(port).bigEndian, + sin_addr: zts_in_addr(s_addr: 0), + sin_zero: (0,0,0,0,0,0,0,0)) + zts_inet_pton(ZTS_AF_INET, addr_str, &(in4.sin_addr)); + + print("fd=", zts_socket(ZTS_AF_INET, ZTS_SOCK_STREAM, 0)); + + // ... + + while(true) { + sleep(1); + } } main() From 3f1e9f00d1542cdf824acad59d5127b61e105df3 Mon Sep 17 00:00:00 2001 From: rossmc7 Date: Fri, 29 May 2020 19:24:22 +0100 Subject: [PATCH 64/78] Fixing Android/Java crash on boot due to missing init method (#70) * adding extern c to library methods to fix JNI crash on init * changed style to use ifdef cplusplus --- src/Controls.cpp | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/Controls.cpp b/src/Controls.cpp index 549b479..b9c2168 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -36,6 +36,8 @@ using namespace ZeroTier; #include #endif + + namespace ZeroTier { extern NodeService *service; @@ -153,6 +155,11 @@ int zts_start(const char *path, void (*callback)(void *), uint16_t port) return retval; } + +#ifdef __cplusplus + extern "C" { +#endif + #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_start( JNIEnv *env, jobject thisObj, jstring path, jobject callback, jint port) @@ -198,7 +205,7 @@ int zts_stop() return ZTS_ERR_SERVICE; } #ifdef SDK_JNI -JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop( + JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop( JNIEnv *env, jobject thisObj) { zts_stop(); @@ -248,7 +255,7 @@ int zts_restart() #endif } #ifdef SDK_JNI -JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( + JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( JNIEnv *env, jobject thisObj) { zts_restart(); @@ -266,7 +273,7 @@ int zts_free() // TODO: add stack shutdown logic } #ifdef SDK_JNI -JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( + JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( JNIEnv *env, jobject thisObj) { zts_free(); @@ -282,7 +289,7 @@ uint64_t zts_get_node_id() return service->getNode()->address(); } #ifdef SDK_JNI -JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( + JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( JNIEnv *env, jobject thisObj) { return zts_get_node_id(); @@ -298,7 +305,7 @@ int zts_get_node_status() && service->getNode()->online() ? ZTS_EVENT_NODE_ONLINE : ZTS_EVENT_NODE_OFFLINE; } #ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1status( + JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1status( JNIEnv *env, jobject thisObj) { return zts_get_node_status(); @@ -323,7 +330,7 @@ int zts_get_network_status(uint64_t networkId) return ZTS_ERR_NO_RESULT; } #ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1network_1status( + JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1network_1status( JNIEnv *env, jobject thisObj, jlong networkId) { return zts_get_network_status(networkId); @@ -340,7 +347,7 @@ int zts_get_peer_status(uint64_t peerId) return service->getPeerStatus(peerId); } #ifdef SDK_JNI -JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( + JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( JNIEnv *env, jobject thisObj, jlong peerId) { return zts_get_peer_status(peerId); @@ -352,7 +359,7 @@ JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( * Called from Java, saves a static reference to the VM so it can be used * later to call a user-specified callback method from C. */ -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init( + JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init( JNIEnv *env, jobject thisObj) { jint rs = env->GetJavaVM(&jvm); @@ -372,13 +379,18 @@ int zts_join(const uint64_t nwid) return ZTS_ERR_OK; } #ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( + JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( JNIEnv *env, jobject thisObj, jlong nwid) { return zts_join((uint64_t)nwid); } #endif +#ifdef __cplusplus +} +#endif + + int zts_leave(const uint64_t nwid) { Mutex::Lock _l(serviceLock); @@ -391,7 +403,7 @@ int zts_leave(const uint64_t nwid) return ZTS_ERR_OK; } #ifdef SDK_JNI -JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( + JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( JNIEnv *env, jobject thisObj, jlong nwid) { return zts_leave((uint64_t)nwid); @@ -617,6 +629,9 @@ int zts_get_all_network_details(struct zts_network_details *nds, int *num) #ifdef SDK_JNI #endif + + + void zts_delay_ms(long interval_ms) { #if defined(__WINDOWS__) @@ -627,3 +642,7 @@ void zts_delay_ms(long interval_ms) nanosleep(&sleepValue, NULL); #endif } + + + + From 2ae7ebb0fe93b2679d1a43af7b76cab2f61c7c90 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 29 May 2020 12:16:03 -0700 Subject: [PATCH 65/78] Minor tweak to example --- examples/cpp/comprehensive.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index 46f25c2..ab18786 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -120,6 +120,7 @@ void printPeerDetails(const char *msgStr, struct zts_peer_details *d) printf("\t- latency : %llx\n", d->latency); printf("\t- pathCount : %llx\n", d->pathCount); printf("\t- version : %d.%d.%d\n", d->versionMajor, d->versionMinor, d->versionRev); + printf("\t- pathCount : %d\n", d->pathCount); printf("\t- paths:\n"); // Print all known paths for each peer @@ -220,7 +221,7 @@ no state-change implications. */ void myZeroTierEventCallback(void *msgPtr) { struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; - printf("code=%d\n", msg->eventCode); + printf("eventCode=%d\n", msg->eventCode); // Node events if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { From 37c01e18cf0a3aa8562e1f83fe6bf4d4d5aabb7a Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Sat, 30 May 2020 18:29:04 -0700 Subject: [PATCH 66/78] Change event code numbering scheme, fix Windows startup bug, fix zts_free(), remove vestigial API functions, update documentation --- README.md | 281 +++++++++---------- examples/cpp/comprehensive.cpp | 9 +- include/ZeroTierSockets.h | 495 ++++++++++++++++++--------------- src/Controls.cpp | 346 ++++------------------- src/Debug.hpp | 1 - src/Events.cpp | 55 ++-- src/Events.hpp | 5 + src/NodeService.cpp | 208 +++++++++----- src/NodeService.hpp | 6 +- src/Sockets.cpp | 8 +- src/VirtualTap.cpp | 218 ++++++--------- src/VirtualTap.hpp | 39 +-- 12 files changed, 725 insertions(+), 946 deletions(-) diff --git a/README.md b/README.md index c915f20..965ceba 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,13 @@ Connect physical devices, virtual devices, and application instances as if everything is on a single LAN. *** -The ZeroTier SDK brings your network into userspace. We've paired our network hypervisor core with a network stack ([lwIP](https://savannah.nongnu.org/projects/lwip/)) to provide your application with an exclusive and private virtual network interface. All traffic on this interface is end-to-end encrypted between each peer and we provide an easy-to-use socket interface derived from [Berkeley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). Since we aren't using the kernel's network stack that means, no drivers, no root, and no host configuration requirements. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml). For troubleshooting advice see our [Knowledgebase](https://zerotier.atlassian.net/wiki/spaces/SD/overview). If you need further assistance, create an account at [my.zerotier.com](https://my.zerotier.com) and join our community of users and professionals. +The ZeroTier SDK brings your network into user-space. We've paired our network hypervisor core with a network stack ([lwIP](https://savannah.nongnu.org/projects/lwip/)) to provide your application with an exclusive and private virtual network interface. All traffic on this interface is end-to-end encrypted between each peer and we provide an easy-to-use socket interface derived from [Berkeley Sockets](https://en.wikipedia.org/wiki/Berkeley_sockets). Since we aren't using the kernel's network stack that means, no drivers, no root, and no host configuration requirements. For a more in-depth discussion on the technical side of ZeroTier, check out our [Manual](https://www.zerotier.com/manual.shtml). For troubleshooting advice see our [Knowledgebase](https://zerotier.atlassian.net/wiki/spaces/SD/overview). If you need further assistance, create an account at [my.zerotier.com](https://my.zerotier.com) and join our community of users and professionals. -*** +Downloads: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk)

-# Usage - -### Downloads and examples: [download.zerotier.com/dist/sdk](https://download.zerotier.com/dist/sdk) - -### Building from source +## Building from source To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `./lib`, test and example programs will be placed in `./bin`: @@ -29,15 +25,11 @@ lib | ├── libzt.a | └── libzt.so └── debug - └── linux-x86_64 - ├── libzt.a - └── libzt.so + └── ... bin └── release └── linux-x86_64 - ├── adhoc ├── client - ├── comprehensive └── server ``` @@ -49,9 +41,9 @@ clang++ -o yourApp yourApp.cpp -L./lib/release/linux-x86_64/ -lzt; ./yourApp
-# Starting the service +## 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** section and will result in the generation of callback events detailed in the **Event handling** section. It is your responsibility to handle these events. To start the service, simply call: +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: `zts_start(char *path, void (*userCallbackFunc)(struct zts_callback_msg*), int port)` @@ -90,47 +82,23 @@ For more complete examples see `./examples/`
-After calling `zts_start()` you will receive one or more of the following events: +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. -``` -ZTS_EVENT_NODE_ONLINE -ZTS_EVENT_NODE_OFFLINE -ZTS_EVENT_NODE_DOWN -ZTS_EVENT_NODE_IDENTITY_COLLISION -ZTS_EVENT_NODE_UNRECOVERABLE_ERROR -ZTS_EVENT_NODE_NORMAL_TERMINATION -``` - -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, but 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. +It is worth noting that while `zts_stop()` will stop the service, the user-space network stack will continue operating in a headless hibernation mode. This is intended behavior due to the fact that the network stack we've chosen doesn't currently support the notion of shutdown since it was initially designed for embedded applications that are simply switched off. If you do need a way to shut everything down and free all resources you can call `zts_free()`, but please note that calling this function will prevent all subsequent `zts_start()` calls from succeeding and will require a full application restart if you want to run the service again. The events `ZTS_EVENT_NODE_ONLINE` and `ZTS_EVENT_NODE_OFFLINE` can be seen periodically throughout the lifetime of your application depending on the reliability of your underlying network link, these events are lagging indicators and are typically only triggered every thirty (30) seconds. Lastly, the function `zts_restart()` is provided as a way to restart the ZeroTier service along with all of its virtual interfaces. The network stack will remain online and undisturbed during this call. Note that this call will temporarily block until the service has fully shut down, then will return and you may then watch for the appropriate startup callbacks mentioned above.
-# Joining a network +## Joining a network -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 be received before these calls will succeed. After calling `zts_join()` any one of the following events may be generated: - -``` -ZTS_EVENT_NETWORK_NOT_FOUND -ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQ_CONFIG -ZTS_EVENT_NETWORK_OK -ZTS_EVENT_NETWORK_ACCESS_DENIED -ZTS_EVENT_NETWORK_READY_IP4 -ZTS_EVENT_NETWORK_READY_IP6 -ZTS_EVENT_NETWORK_DOWN -``` - -`ZTS_EVENT_NETWORK_READY_IP4` and `ZTS_EVENT_NETWORK_READY_IP6` are combinations of a few different events. They signal that the network was found, joined successfully, an IP address was assigned and the network stack's interface is ready to process traffic of the indicated type. After this point you should be able to communicate with peers on the network. +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 and communicating with peers +## Connecting and communicating with peers 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. @@ -138,11 +106,9 @@ Creating a standard socket connection generally works the same as it would using 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)`. -One can use `zts_get_peer_status(uint64_t peerId)` to query the current reachability state of another peer. This function will actually **return** value of the previously observed callback event for the given peer, plus an additional possible value `ZTS_EVENT_PEER_UNREACHABLE` if no known path exists between the calling node and the remote node. -
-# Event handling +## Event handling 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`: @@ -154,7 +120,6 @@ struct zts_callback_msg struct zts_network_details *network; struct zts_netif_details *netif; struct zts_virtual_network_route *route; - struct zts_physical_path *path; struct zts_peer_details *peer; struct zts_addr_details *addr; }; @@ -179,106 +144,145 @@ In this callback function you can perform additional non-blocking API calls or o A typical ordering of messages may look like the following: ``` -ZTS_EVENT_NETIF_UP --- network=a09acf023be465c1, mac=73b7abcfc207, mtu=10000 -ZTS_EVENT_ADDR_NEW_IP4 --- addr=11.7.7.184 (on network=a09acf023be465c1) -ZTS_EVENT_ADDR_NEW_IP6 --- addr=fda0:9acf:233:e4b0:7099:9309:4c9b:c3c7 -ZTS_EVENT_NODE_ONLINE, node=c4c7ba3cf -ZTS_EVENT_NETWORK_READY_IP4 --- network=a09acf023be465c1 -ZTS_EVENT_NETWORK_READY_IP6 --- network=a09acf023be465c1 -ZTS_EVENT_PEER_DIRECT --- node=74d0f5e89d -ZTS_EVENT_PEER_DIRECT --- node=9d219039f3 -ZTS_EVENT_PEER_DIRECT --- node=a09acf0233 +... +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 -These events pertain to the state of the current node. This message type will arrive with a `zts_node_details` object accessible via `msg->node`. Additionally, one can query the status of the node with `zts_get_node_status()`, this will **return** the status as an integer value only. +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`:* ``` -ZTS_EVENT_NODE_OFFLINE -ZTS_EVENT_NODE_ONLINE -ZTS_EVENT_NODE_DOWN -ZTS_EVENT_NODE_IDENTITY_COLLISION -ZTS_EVENT_NODE_UNRECOVERABLE_ERROR -ZTS_EVENT_NODE_NORMAL_TERMINATION +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 -These events pertain to the state of the indicated network. This event type will arrive with a `zts_network_details` object accessible via `msg->network`. If for example you want to know the number of assigned routes for your network you can use `msg->network->num_routes`. Similarly for the MTU, use `msg->network->mtu`. Additionally, one can query the status of the network with `zts_get_network_status(uint64_t networkId)`, this will **return** the status as an integer value only. +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`:* ``` -ZTS_EVENT_NETWORK_NOT_FOUND -ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -ZTS_EVENT_NETWORK_REQ_CONFIG -ZTS_EVENT_NETWORK_OK -ZTS_EVENT_NETWORK_ACCESS_DENIED -ZTS_EVENT_NETWORK_READY_IP4 -ZTS_EVENT_NETWORK_READY_IP6 -ZTS_EVENT_NETWORK_DOWN +ZTS_EVENT_NETWORK_NOT_FOUND // The network does not exist. The provided networkID may be incorrect. +ZTS_EVENT_NETWORK_CLIENT_TOO_OLD // This client is too old. +ZTS_EVENT_NETWORK_REQ_CONFIG // Waiting for network config, this might take a few seconds. +ZTS_EVENT_NETWORK_OK // Node successfully joined. +ZTS_EVENT_NETWORK_ACCESS_DENIED // The network is private. Your node requires authorization. +ZTS_EVENT_NETWORK_READY_IP4 // Your node successfully received an IPv4 address. +ZTS_EVENT_NETWORK_READY_IP6 // Your node successfully received an IPv6 address. +ZTS_EVENT_NETWORK_DOWN // For some reason the network is no longer available. +ZTS_EVENT_NETWORK_UPDATE // The network's config has changed: mtu, name, managed route, etc. +``` + +*Example contents of `msg->network`:* + +``` +nwid : 8bd712bf36bdae5f +mac : ae53fa031fcf +name : cranky_hayes +type : 0 +mtu : 2800 +dhcp : 0 +bridge : 0 +broadcastEnabled : 1 +portError : 0 +netconfRevision : 34 +routeCount : 1 +multicastSubscriptionCount : 1 +- mac=ffffffffffff, adi=ac1b2561 +addresses: +- FC5D:69B6:E0F7:46D5:50DD::1 +- 172.27.37.97 +routes: +- target : 172.27.0.0 +- via : 0.0.0.0 + - flags : 0 + - metric : 0 ```
## Peer Events -These events are triggered when the reachability status of a peer has changed, this can happen at any time. This event type will arrive with a `zts_peer_details` object for additional context. Additionally, one can query the status of the network with `zts_get_peer_status(uint64_t peerId)`, this will **return** the status as an integer value only. +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`:* ``` -ZTS_EVENT_PEER_DIRECT -ZTS_EVENT_PEER_RELAY -ZTS_EVENT_PEER_UNREACHABLE +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. ``` -## Path Events - -These events are triggered when a direct path to a peer has been discovered or is now considered too old to be used. You will see these in conjunction with peer events. This event type will arrive with a `zts_physical_path` object for additional context. +*Example contents of `msg->peer`:* ``` -ZTS_EVENT_PATH_DISCOVERED -ZTS_EVENT_PATH_ALIVE -ZTS_EVENT_PATH_DEAD -``` - -## Route Events - -This event type will arrive with a `zts_virtual_network_route` object for additional context. - -``` -ZTS_EVENT_ROUTE_ADDED -ZTS_EVENT_ROUTE_REMOVED +peer : a747d5502d +role : 0 +latency : 4 +version : 1.4.6 +pathCount : 2 + - 172.27.37.97 + - F75D:69B6:E0C7:47D5:51DB::1 ``` ## Address Events -These events are triggered when new addresses are assigned to the node on a particular virtual network. This event type will arrive with a `zts_addr_details` object for additional context. +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`:* ``` -ZTS_EVENT_ADDR_ADDED_IP4 -ZTS_EVENT_ADDR_REMOVED_IP4 -ZTS_EVENT_ADDR_ADDED_IP6 -ZTS_EVENT_ADDR_REMOVED_IP6 +ZTS_EVENT_ADDR_ADDED_IP4 // A new IPv4 address was assigned to your node on the indicated network. +ZTS_EVENT_ADDR_REMOVED_IP4 // An IPv4 address assignment to your node was removed on the indicated network. +ZTS_EVENT_ADDR_ADDED_IP6 // A new IPv6 address was assigned to your node on the indicated network. +ZTS_EVENT_ADDR_REMOVED_IP6 // An IPv6 address assignment to your node was removed on the indicated network. +``` + +*Example contents of `msg->addr`:* + +``` +nwid : a747d5502d +addr : 172.27.37.97 ```
-# Error handling +## Error handling 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`. - - `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. +``` +ZTS_ERR_OK // No error +ZTS_ERR_SOCKET // Socket error (see zts_errno for more information) +ZTS_ERR_SERVICE // General ZeroTier internal error. Maybe you called something out of order? +ZTS_ERR_ARG // An argument provided is invalid. +ZTS_ERR_NO_RESULT // Call succeeded but no result was available. Not necessarily an error. +ZTS_ERR_GENERAL // General internal failure. Consider filing a bug report. +``` *NOTE: For Android/Java (or similar) which use JNI, the socket API's error codes are negative values encoded in the return values of function calls* *NOTE: For protocol-level errors (such as dropped packets) or internal network stack errors, see the section `Statistics`*
-# Common pitfalls +## Common pitfalls - 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. @@ -288,21 +292,21 @@ Calling a `zts_*` function will result in one of the following return codes. Onl - 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**. + - 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** section. - - See the **Debugging** section for more advice. + - Be sure to read and understand the [API compatibility with host OS](#api-compatibility-with-host-os) section. + - See the [Debugging](#debugging) section for more advice.
-# API compatibility with host OS - -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: - +## API compatibility with host OS + +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: -``` +``` zts_socket(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); (CORRECT) zts_socket(AF_INET6, SOCK_DGRAM, 0); (INCORRECT) ``` @@ -330,21 +334,21 @@ inet_ntop(AF_INET6, &(in6->sin6_addr), dstStr, INET6_ADDRSTRLEN);
-# Thread model +## Thread model (advanced) -The control API for `libzt` is thread safe and can be called at any time from any thread. There is a single internal lock guarding access to this API. The socket API is similar in this regard. 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. +Both the **socket** and **control** interfaces are thread-safe but are implemented differently. The socket interface is implemented using a relatively performant core locking mechanism in lwIP. This can be disabled if you know what you're doing. The control interface is implemented by a single coarse-grained lock. This lock is not a performance bottleneck since it only applies to functions that manipulate the ZeroTier service and are called seldomly. Callback events are generated by a separate thread and are independent from the rest of the API's internal locking mechanism. Not returning from a callback event won't impact the rest of the API but it will prevent your application from receiving future events so it is in your application's best interest to perform as little work as possible in the callback function and promptly return control back to ZeroTier. *Note: Internally, `libzt` will spawn a number of threads for various purposes: a thread for the core service, a thread for the network stack, a low priority thread to process callback events, and a thread for each network joined. The vast majority of work is performed by the core service and stack threads.*
-# Debugging +## Debugging 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** -* **API compatibility with host OS** -* **Thread model** +* [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. @@ -356,52 +360,19 @@ If the information in those sections hasn't helped, there are a couple of ways t ``` struct zts_stats_proto stats; - - // Get count of received pings if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_ICMP, &stats) == ZTS_ERR_OK) { - printf("icmp.recv=%d\n", stats.recv); + printf("icmp.recv=%d\n", stats.recv); // Count of received pings } - - // Get count of dropped TCP packets if (zts_get_protocol_stats(ZTS_STATS_PROTOCOL_TCP, &stats) == ZTS_ERR_OK) { - printf("tcp.drop=%d\n", stats.drop); + 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: +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_*`.
-**Network stack events** - These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with no additional contextual information. - -``` -ZTS_EVENT_STACK_UP -ZTS_EVENT_STACK_DOWN -``` - -**Netif events** - These signal whether the userspace networking stack was brought up successfully. You can ignore these in most cases. This event type will arrive with a `zts_netif_details` object for additional context. - -``` -ZTS_EVENT_NETIF_UP -ZTS_EVENT_NETIF_DOWN -ZTS_EVENT_NETIF_REMOVED -ZTS_EVENT_NETIF_LINK_UP -ZTS_EVENT_NETIF_LINK_DOWN -``` - -
- -# Network controller mode - -The library form of ZeroTier can act as a network controller and in `libzt` this is controlled via the `zts_controller_*` API calls specified in `include/ZeroTierSockets.h`. Currently controller mode is not available in the `iOS` and `macOS` framework builds. - -# Multipath - -The multipath features available in ZeroTier haven't yet been exposed via the `libzt` API but this is coming in the version `2.X` series. - -
- -# Licensing +## Licensing ZeroTier is licensed under the BSL version 1.1. See [LICENSE.txt](./LICENSE.txt) and the ZeroTier pricing page for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index ab18786..23fe872 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -92,8 +92,9 @@ #include "ZeroTierSockets.h" +#ifdef __WINDOWS__ #include "winsock.h" - +#endif struct Node { Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} @@ -117,8 +118,7 @@ void printPeerDetails(const char *msgStr, struct zts_peer_details *d) printf("\n%s\n", msgStr); printf("\t- peer : %llx\n", d->address); printf("\t- role : %llx\n", d->role); - printf("\t- latency : %llx\n", d->latency); - printf("\t- pathCount : %llx\n", d->pathCount); + printf("\t- latency : %d\n", d->latency); printf("\t- version : %d.%d.%d\n", d->versionMajor, d->versionMinor, d->versionRev); printf("\t- pathCount : %d\n", d->pathCount); printf("\t- paths:\n"); @@ -128,7 +128,7 @@ void printPeerDetails(const char *msgStr, struct zts_peer_details *d) char ipstr[ZTS_INET6_ADDRSTRLEN]; int port = 0; struct zts_sockaddr *sa = (struct zts_sockaddr *)&(d->paths[j].address); - if (sa->sa_family == ZTS_AF_INET) { // TODO: Probably broken + 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 = zts_ntohs(in4->sin_port); @@ -373,7 +373,6 @@ void display_stack_stats() int main(int argc, char **argv) { - fprintf(stderr, "AF_INET=%d, SOCK_STREAM=%d, IPPROTO_TCP=%d", AF_INET, SOCK_STREAM, IPPROTO_TCP); if (argc != 4) { printf("\nlibzt example server\n"); printf("comprehensive \n"); diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h index 9c870ad..d3f822f 100644 --- a/include/ZeroTierSockets.h +++ b/include/ZeroTierSockets.h @@ -59,6 +59,7 @@ extern "C" { #define ZTS_EVENT_NETWORK_READY_IP6 216 #define ZTS_EVENT_NETWORK_READY_IP4_IP6 217 #define ZTS_EVENT_NETWORK_DOWN 218 +#define ZTS_EVENT_NETWORK_UPDATE 219 // Network Stack events #define ZTS_EVENT_STACK_UP 220 #define ZTS_EVENT_STACK_DOWN 221 @@ -72,18 +73,16 @@ extern "C" { #define ZTS_EVENT_PEER_DIRECT 240 #define ZTS_EVENT_PEER_RELAY 241 #define ZTS_EVENT_PEER_UNREACHABLE 242 -// Path events -#define ZTS_EVENT_PATH_DISCOVERED 250 -#define ZTS_EVENT_PATH_ALIVE 251 -#define ZTS_EVENT_PATH_DEAD 252 +#define ZTS_EVENT_PEER_PATH_DISCOVERED 243 +#define ZTS_EVENT_PEER_PATH_DEAD 244 // Route events -#define ZTS_EVENT_ROUTE_ADDED 260 -#define ZTS_EVENT_ROUTE_REMOVED 261 +#define ZTS_EVENT_ROUTE_ADDED 250 +#define ZTS_EVENT_ROUTE_REMOVED 251 // Address events -#define ZTS_EVENT_ADDR_ADDED_IP4 270 -#define ZTS_EVENT_ADDR_REMOVED_IP4 271 -#define ZTS_EVENT_ADDR_ADDED_IP6 272 -#define ZTS_EVENT_ADDR_REMOVED_IP6 273 +#define ZTS_EVENT_ADDR_ADDED_IP4 260 +#define ZTS_EVENT_ADDR_REMOVED_IP4 261 +#define ZTS_EVENT_ADDR_ADDED_IP6 262 +#define ZTS_EVENT_ADDR_REMOVED_IP6 263 ////////////////////////////////////////////////////////////////////////////// // Return Error codes // @@ -345,7 +344,7 @@ typedef uint16_t zts_in_port_t; typedef uint8_t zts_sa_family_t; struct zts_in_addr { -#if defined(__WINDOWS__) +#if defined(_WIN32) zts_in_addr_t S_addr; #else // A definition in winsock may conflict with s_addr @@ -394,9 +393,7 @@ struct zts_sockaddr_storage { }; ////////////////////////////////////////////////////////////////////////////// -// Subset of: ZeroTierOne.h // -// We redefine a few ZT structures here so that we don't need to drag the // -// entire ZeroTierOne.h file into the user application // +// Structures used to convey details during various callback events // ////////////////////////////////////////////////////////////////////////////// /** @@ -412,7 +409,7 @@ struct zts_sockaddr_storage { /** * Maximum number of direct network paths to a given peer */ -#define ZT_MAX_PEER_NETWORK_PATHS 16 +#define ZTS_MAX_PEER_NETWORK_PATHS 16 /** * What trust hierarchy role does this peer have? @@ -424,10 +421,6 @@ enum zts_peer_role ZTS_PEER_ROLE_PLANET = 2 // planetary root }; -////////////////////////////////////////////////////////////////////////////// -// Structures used to convey details during various callback events // -////////////////////////////////////////////////////////////////////////////// - /** * A structure used to convey details about the current node * to the user application @@ -439,41 +432,17 @@ struct zts_node_details */ uint64_t address; - /** - * The current clock value accord to the node - */ - uint64_t clock; - - /** - * Whether or not this node is online - */ - uint8_t online; - - /** - * Whether port mapping is enabled - */ - uint8_t portMappingEnabled; - - /** - * Whether multipath support is enabled. If true, this node will - * be capable of utilizing multiple physical links simultaneously - * to create higher quality or more robust aggregate links. - * - * See: https://www.zerotier.com/manual.shtml#2_1_5 - */ - uint8_t multipathEnabled; - /** * The port used by the service to send and receive * all encapsulated traffic */ uint16_t primaryPort; + uint16_t secondaryPort; + uint16_t tertiaryPort; /** - * Planet ID + * ZT version */ - uint64_t planetWorldId; - uint64_t planetWorldTimestamp; uint8_t versionMajor; uint8_t versionMinor; uint8_t versionRev; @@ -494,7 +463,6 @@ struct zts_callback_msg struct zts_network_details *network; struct zts_netif_details *netif; struct zts_virtual_network_route *route; - struct zts_physical_path *path; struct zts_peer_details *peer; struct zts_addr_details *addr; }; @@ -525,26 +493,6 @@ struct zts_netif_details * The MTU for this interface */ int mtu; - - /** - * The IPv4 address assigned to this interface. - */ - //struct sockaddr_in ip4_addr; - - /** - * The IPv6 addresses assigned to this interface. - */ - //struct sockaddr_in6 ip6_addr[LWIP_IPV6_NUM_ADDRESSES]; - - /** - * Number of IPv4 addresses assigned to this interface - */ - //int num_ip4_addr; - - /** - * Number of IPv6 addresses assigned to this interface - */ - //int num_ip6_addr; }; /** @@ -573,42 +521,216 @@ struct zts_virtual_network_route uint16_t metric; }; + /** - * A structure used to convey network-specific details to the user application + * Maximum length of network short name + */ +#define ZTS_MAX_NETWORK_SHORT_NAME_LENGTH 127 + +/** + * Maximum number of pushed routes on a network + */ +#define ZTS_MAX_NETWORK_ROUTES 32 + +/** + * Maximum number of statically assigned IP addresses per network endpoint using ZT address management (not DHCP) + */ +#define ZTS_MAX_ZT_ASSIGNED_ADDRESSES 16 + +/** + * Maximum number of multicast groups a device / network interface can be subscribed to at once + */ +#define ZTS_MAX_MULTICAST_SUBSCRIPTIONS 1024 + +/** + * Virtual network status codes + */ +enum ZTS_VirtualNetworkStatus +{ + /** + * Waiting for network configuration (also means revision == 0) + */ + ZTS_NETWORK_STATUS_REQUESTING_CONFIGURATION = 0, + + /** + * Configuration received and we are authorized + */ + ZTS_NETWORK_STATUS_OK = 1, + + /** + * Netconf master told us 'nope' + */ + ZTS_NETWORK_STATUS_ACCESS_DENIED = 2, + + /** + * Netconf master exists, but this virtual network does not + */ + ZTS_NETWORK_STATUS_NOT_FOUND = 3, + + /** + * Initialization of network failed or other internal error + */ + ZTS_NETWORK_STATUS_PORT_ERROR = 4, + + /** + * ZeroTier core version too old + */ + ZTS_NETWORK_STATUS_CLIENT_TOO_OLD = 5 +}; + +/** + * Virtual network type codes + */ +enum ZTS_VirtualNetworkType +{ + /** + * Private networks are authorized via certificates of membership + */ + ZTS_NETWORK_TYPE_PRIVATE = 0, + + /** + * Public networks have no access control -- they'll always be AUTHORIZED + */ + ZTS_NETWORK_TYPE_PUBLIC = 1 +}; + +/** + * A route to be pushed on a virtual network + */ +typedef struct +{ + /** + * Target network / netmask bits (in port field) or NULL or 0.0.0.0/0 for default + */ + struct zts_sockaddr_storage target; + + /** + * Gateway IP address (port ignored) or NULL (family == 0) for LAN-local (no gateway) + */ + struct zts_sockaddr_storage via; + + /** + * Route flags + */ + uint16_t flags; + + /** + * Route metric (not currently used) + */ + uint16_t metric; +} ZTS_VirtualNetworkRoute; + +/** + * Virtual network configuration */ struct zts_network_details { /** - * Network ID + * 64-bit ZeroTier network ID */ uint64_t nwid; /** - * Maximum Transmission Unit size for this network + * Ethernet MAC (48 bits) that should be assigned to port */ - int mtu; + uint64_t mac; /** - * Number of addresses (actually) assigned to the node on this network + * Network name (from network configuration master) */ - short num_addresses; + char name[ZTS_MAX_NETWORK_SHORT_NAME_LENGTH + 1]; /** - * Array of IPv4 and IPv6 addresses assigned to the node on this network + * Network configuration request status */ - struct zts_sockaddr_storage addr[ZTS_MAX_ASSIGNED_ADDRESSES]; + enum ZTS_VirtualNetworkStatus status; /** - * Number of routes + * Network type */ - unsigned int num_routes; + enum ZTS_VirtualNetworkType type; /** - * Array of IPv4 and IPv6 addresses assigned to the node on this network + * Maximum interface MTU */ - struct zts_virtual_network_route routes[ZTS_MAX_NETWORK_ROUTES]; + unsigned int mtu; + + /** + * If nonzero, the network this port belongs to indicates DHCP availability + * + * This is a suggestion. The underlying implementation is free to ignore it + * for security or other reasons. This is simply a netconf parameter that + * means 'DHCP is available on this network.' + */ + int dhcp; + + /** + * If nonzero, this port is allowed to bridge to other networks + * + * This is informational. If this is false (0), bridged packets will simply + * be dropped and bridging won't work. + */ + int bridge; + + /** + * If nonzero, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic + */ + int broadcastEnabled; + + /** + * If the network is in PORT_ERROR state, this is the (negative) error code most recently reported + */ + int portError; + + /** + * Revision number as reported by controller or 0 if still waiting for config + */ + unsigned long netconfRevision; + + /** + * Number of assigned addresses + */ + unsigned int assignedAddressCount; + + /** + * ZeroTier-assigned addresses (in sockaddr_storage structures) + * + * For IP, the port number of the sockaddr_XX structure contains the number + * of bits in the address netmask. Only the IP address and port are used. + * Other fields like interface number can be ignored. + * + * This is only used for ZeroTier-managed address assignments sent by the + * virtual network's configuration master. + */ + struct zts_sockaddr_storage assignedAddresses[ZTS_MAX_ZT_ASSIGNED_ADDRESSES]; + + /** + * Number of ZT-pushed routes + */ + unsigned int routeCount; + + /** + * Routes (excluding those implied by assigned addresses and their masks) + */ + ZTS_VirtualNetworkRoute routes[ZTS_MAX_NETWORK_ROUTES]; + + /** + * Number of multicast groups subscribed + */ + unsigned int multicastSubscriptionCount; + + /** + * Multicast groups to which this network's device is subscribed + */ + struct { + uint64_t mac; /* MAC in lower 48 bits */ + uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP groups */ + } multicastSubscriptions[ZTS_MAX_MULTICAST_SUBSCRIPTIONS]; }; + + + /** * Physical network path to a peer */ @@ -688,7 +810,7 @@ struct zts_peer_details /** * Known network paths to peer */ - struct zts_physical_path paths[ZT_MAX_PEER_NETWORK_PATHS]; + struct zts_physical_path paths[ZTS_MAX_PEER_NETWORK_PATHS]; }; /** @@ -704,15 +826,15 @@ struct zts_peer_list // ZeroTier Service Controls // ////////////////////////////////////////////////////////////////////////////// -#if defined(__WINDOWS__) +#if defined(_WIN32) #ifdef ADD_EXPORTS - #define ZT_SOCKET_API __declspec(dllexport) + #define ZTS_API __declspec(dllexport) #else - #define ZT_SOCKET_API __declspec(dllimport) + #define ZTS_API __declspec(dllimport) #endif #define ZTCALL __cdecl #else - #define ZT_SOCKET_API + #define ZTS_API #define ZTCALL #endif @@ -730,7 +852,7 @@ struct zts_peer_list * @param enabled Whether or not this feature is enabled * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_allow_network_caching(uint8_t allowed); +ZTS_API int ZTCALL zts_allow_network_caching(uint8_t allowed); /** * @brief Enable or disable whether the service will cache peer details (enabled by default) @@ -746,7 +868,7 @@ ZT_SOCKET_API int ZTCALL zts_allow_network_caching(uint8_t allowed); * @param enabled Whether or not this feature is enabled * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_allow_peer_caching(uint8_t allowed); +ZTS_API int ZTCALL zts_allow_peer_caching(uint8_t allowed); /** * @brief Enable or disable whether the service will read from a local.conf @@ -756,7 +878,7 @@ ZT_SOCKET_API int ZTCALL zts_allow_peer_caching(uint8_t allowed); * @param enabled Whether or not this feature is enabled * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_allow_local_conf(uint8_t allowed); +ZTS_API int ZTCALL zts_allow_local_conf(uint8_t allowed); /** * @brief Starts the ZeroTier service and notifies user application of events via callback @@ -765,7 +887,7 @@ ZT_SOCKET_API int ZTCALL zts_allow_local_conf(uint8_t allowed); * @param callback User-specified callback for ZTS_EVENT_* events * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure */ -ZT_SOCKET_API int ZTCALL zts_start(const char *path, void (*callback)(void *), uint16_t port); +ZTS_API int ZTCALL zts_start(const char *path, void (*callback)(void *), uint16_t port); /** * @brief Stops the ZeroTier service and brings down all virtual network interfaces @@ -775,7 +897,7 @@ ZT_SOCKET_API int ZTCALL zts_start(const char *path, void (*callback)(void *), u * and free all resources use zts_free() instead. * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_stop(); +ZTS_API int ZTCALL zts_stop(); /** * @brief Restart the ZeroTier service. @@ -785,7 +907,7 @@ ZT_SOCKET_API int ZTCALL zts_stop(); * startup callback events. * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_restart(); +ZTS_API int ZTCALL zts_restart(); /** * @brief Stop all background services, bring down all interfaces, free all resources. After @@ -796,49 +918,23 @@ ZT_SOCKET_API int ZTCALL zts_restart(); * communicating over ZeroTier * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_free(); - -/** - * @brief Populate a structure with details for a given network - * - * @param nwid A 16-digit hexadecimal virtual network ID - * @param nd Pointer to a zts_network_details structure to populate - * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. - */ -ZT_SOCKET_API int ZTCALL zts_get_network_details(uint64_t nwid, struct zts_network_details *nd); - -/** - * @brief Populate an array of structures with details for any given number of networks - * - * @param nds Pointer to an array of zts_network_details structures to populate - * @param num Number of zts_network_details structures available to copy data into, will be updated - * to reflect number of structures that were actually populated - * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. - */ -ZT_SOCKET_API int ZTCALL zts_get_all_network_details(struct zts_network_details *nds, int *num); +ZTS_API int ZTCALL zts_free(); /** * @brief Join a network * - * @param nwid A 16-digit hexadecimal virtual network ID + * @param networkId A 16-digit hexadecimal virtual network ID * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_join(const uint64_t nwid); +ZTS_API int ZTCALL zts_join(const uint64_t networkId); /** * @brief Leave a network * - * @param nwid A 16-digit hexadecimal virtual network ID + * @param networkId A 16-digit hexadecimal virtual network ID * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_leave(const uint64_t nwid); - -/** - * @brief Leave all networks - * - * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. - */ -ZT_SOCKET_API int ZTCALL zts_leave_all(); +ZTS_API int ZTCALL zts_leave(const uint64_t networkId); /** * @brief Orbit a given moon (user-defined root server) @@ -847,7 +943,7 @@ ZT_SOCKET_API int ZTCALL zts_leave_all(); * @param moonSeed A 16-digit hexadecimal seed ID * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed); +ZTS_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed); /** * @brief De-orbit a given moon (user-defined root server) @@ -855,36 +951,28 @@ ZT_SOCKET_API int ZTCALL zts_orbit(uint64_t moonWorldId, uint64_t moonSeed); * @param moonWorldId A 16-digit world ID * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_deorbit(uint64_t moonWorldId); - -/** - * @brief Return the node ID of this instance - * - * @return 64-bit node ID - */ -ZT_SOCKET_API uint64_t ZTCALL zts_get_node_id(); +ZTS_API int ZTCALL zts_deorbit(uint64_t moonWorldId); /** * @brief Compute a 6PLANE IPv6 address for the given Network ID and Node ID * * @param addr Destination structure for address - * @param nwid Network ID + * @param networkId Network ID * @param nodeId Node ID * @return ZTS_ERR_OK on success. ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_get_6plane_addr( - struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); +ZTS_API int ZTCALL zts_get_6plane_addr(struct zts_sockaddr_storage *addr, const uint64_t networkId, const uint64_t nodeId); /** * @brief Compute a RFC4193 IPv6 address for the given Network ID and Node ID * * @param addr Destination structure for address - * @param nwid Network ID + * @param networkId Network ID * @param nodeId Node ID * @return ZTS_ERR_OK on success. ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( - struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId); +ZTS_API int ZTCALL zts_get_rfc4193_addr( + struct zts_sockaddr_storage *addr, const uint64_t networkId, const uint64_t nodeId); /** * @brief Compute a RFC4193 IPv6 address for the given Network ID and Node ID @@ -915,71 +1003,15 @@ ZT_SOCKET_API int ZTCALL zts_get_rfc4193_addr( * @param endPortOfRange End of allowed port range * @return An Ad-hoc network ID */ -ZT_SOCKET_API uint64_t ZTCALL zts_generate_adhoc_nwid_from_range( +ZTS_API uint64_t ZTCALL zts_generate_adhoc_nwid_from_range( uint16_t startPortOfRange, uint16_t endPortOfRange); -/** - * @brief Return details of all peers - * - * @param pds Pointer to array of zts_peer_details structs to be filled out - * @param num Length of destination array, will be filled out with actual number - * of peers that details were available for. - * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. - */ -ZT_SOCKET_API int ZTCALL zts_get_peers(struct zts_peer_details *pds, uint32_t *num); - -/** - * @brief Return details of a given peer. - * - * @param pds Pointer to zts_peer_details struct to be filled out - * @param peerId ID of peer that the caller wants details of - * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. - */ -ZT_SOCKET_API int ZTCALL zts_get_peer(struct zts_peer_details *pds, uint64_t peerId); - -#define ZTS_STATE_NODE_OFFLINE ZTS_EVENT_NODE_OFFLINE -#define ZTS_STATE_NODE_ONLINE ZTS_EVENT_NODE_ONLINE - -/** - * @brief Return the state of this node - * - * @return ZTS_STATE_NODE_ONLINE, ZTS_STATE_NODE_OFFLINE - */ -ZT_SOCKET_API int ZTCALL zts_get_node_status(); - -#define ZTS_STATE_NETWORK_NOT_FOUND ZTS_EVENT_NETWORK_NOT_FOUND -#define ZTS_STATE_NETWORK_CLIENT_TOO_OLD ZTS_EVENT_NETWORK_CLIENT_TOO_OLD -#define ZTS_STATE_NETWORK_REQUESTING_CONFIG ZTS_EVENT_NETWORK_REQUESTING_CONFIG -#define ZTS_STATE_NETWORK_OK ZTS_EVENT_NETWORK_OK -#define ZTS_STATE_NETWORK_ACCESS_DENIED ZTS_EVENT_NETWORK_ACCESS_DENIED -#define ZTS_STATE_NETWORK_DOWN ZTS_EVENT_NETWORK_DOWN - -/** - * @brief Return the state of a given network - * - * @param nwid Network ID - * @return ZTS_STATE_NETWORK_OK, ZTS_STATE_NETWORK_REQUESTING_CONFIG, etc - */ -ZT_SOCKET_API int ZTCALL zts_get_network_status(uint64_t nwid); - -#define ZTS_STATE_PEER_DIRECT ZTS_EVENT_PEER_DIRECT -#define ZTS_STATE_PEER_RELAY ZTS_EVENT_PEER_RELAY -#define ZTS_STATE_PEER_UNREACHABLE ZTS_EVENT_PEER_UNREACHABLE - -/** - * @brief Return the reachability state of a given remote peer - * - * @param peerId Remote peer ID - * @return ZTS_STATE_PEER_DIRECT, ZTS_STATE_PEER_RELAY, or ZTS_STATE_PEER_UNREACHABLE - */ -ZT_SOCKET_API int ZTCALL zts_get_peer_status(uint64_t peerId); - /** * @brief Platform-agnostic delay (provided for convenience) * * @param interval_ms Number of milliseconds to delay */ -ZT_SOCKET_API void ZTCALL zts_delay_ms(long interval_ms); +ZTS_API void ZTCALL zts_delay_ms(long interval_ms); ////////////////////////////////////////////////////////////////////////////// // Statistics // @@ -1082,7 +1114,7 @@ struct zts_stats { * @usage This function can only be used in debug builds. * @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure. */ -ZT_SOCKET_API int ZTCALL zts_get_all_stats(struct zts_stats *statsDest); +ZTS_API int ZTCALL zts_get_all_stats(struct zts_stats *statsDest); /** * @brief Populate the given structure with the requested protocol's @@ -1091,7 +1123,7 @@ ZT_SOCKET_API int ZTCALL zts_get_all_stats(struct zts_stats *statsDest); * @usage This function can only be used in debug builds. * @return ZTS_ERR_OK on success. ZTS_ERR_ARG or ZTS_ERR_NO_RESULT on failure. */ -ZT_SOCKET_API int ZTCALL zts_get_protocol_stats(int protocolType, void *protoStatsDest); +ZTS_API int ZTCALL zts_get_protocol_stats(int protocolType, void *protoStatsDest); ////////////////////////////////////////////////////////////////////////////// // Socket API // @@ -1105,7 +1137,7 @@ ZT_SOCKET_API int ZTCALL zts_get_protocol_stats(int protocolType, void *protoSta * @param protocol Protocols supported on this socket * @return Numbered file descriptor on success. ZTS_ERR_SERVICE or ZTS_ERR_SOCKET on failure. */ -ZT_SOCKET_API int ZTCALL zts_socket( +ZTS_API int ZTCALL zts_socket( const int socket_family, const int socket_type, const int protocol); /** @@ -1116,7 +1148,7 @@ ZT_SOCKET_API int ZTCALL zts_socket( * @param addrlen Length of address * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_connect( +ZTS_API int ZTCALL zts_connect( int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen); /** @@ -1127,7 +1159,7 @@ ZT_SOCKET_API int ZTCALL zts_connect( * @param addrlen Length of address * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_bind( +ZTS_API int ZTCALL zts_bind( int fd, const struct zts_sockaddr *addr, zts_socklen_t addrlen); /** @@ -1137,7 +1169,7 @@ ZT_SOCKET_API int ZTCALL zts_bind( * @param backlog Number of backlogged connections allowed * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_listen(int fd, int backlog); +ZTS_API int ZTCALL zts_listen(int fd, int backlog); /** * @brief Accept an incoming connection (sets zts_errno) @@ -1147,7 +1179,7 @@ ZT_SOCKET_API int ZTCALL zts_listen(int fd, int backlog); * @param addrlen Length of address * @return New socket file descriptor on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_accept(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); +ZTS_API int ZTCALL zts_accept(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); /** * @brief Accept an incoming connection (sets zts_errno) @@ -1295,7 +1327,7 @@ typedef struct zts_ipv6_mreq { * @param optlen Length of option value * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_setsockopt( +ZTS_API int ZTCALL zts_setsockopt( int fd, int level, int optname, const void *optval, zts_socklen_t optlen); /** @@ -1308,7 +1340,7 @@ ZT_SOCKET_API int ZTCALL zts_setsockopt( * @param optlen Length of value * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_getsockopt( +ZTS_API int ZTCALL zts_getsockopt( int fd, int level, int optname, void *optval, zts_socklen_t *optlen); /** @@ -1319,7 +1351,7 @@ ZT_SOCKET_API int ZTCALL zts_getsockopt( * @param addrlen Length of name * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_getsockname(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); +ZTS_API int ZTCALL zts_getsockname(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); /** * @brief Get the peer name for the remote end of a connected socket @@ -1329,7 +1361,7 @@ ZT_SOCKET_API int ZTCALL zts_getsockname(int fd, struct zts_sockaddr *addr, zts_ * @param addrlen Length of name * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_getpeername(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); +ZTS_API int ZTCALL zts_getpeername(int fd, struct zts_sockaddr *addr, zts_socklen_t *addrlen); /** * @brief Close a socket (sets zts_errno) @@ -1337,11 +1369,12 @@ ZT_SOCKET_API int ZTCALL zts_getpeername(int fd, struct zts_sockaddr *addr, zts_ * @param fd Socket file descriptor * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_close(int fd); +ZTS_API int ZTCALL zts_close(int fd); /* FD_SET used for lwip_select */ -#define MEMP_NUM_NETCONN 1024 +#define LWIP_SOCKET_OFFSET 0 +#define MEMP_NUM_NETCONN 1024 #ifndef ZTS_FD_SET #undef ZTS_FD_SETSIZE @@ -1387,7 +1420,7 @@ struct zts_timeval { * @param timeout How long this call should block * @return Number of ready file descriptors on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_select( +ZTS_API int ZTCALL zts_select( int nfds, zts_fd_set *readfds, zts_fd_set *writefds, zts_fd_set *exceptfds, struct zts_timeval *timeout); // fnctl() commands @@ -1409,7 +1442,7 @@ ZT_SOCKET_API int ZTCALL zts_select( * @param flags * @return */ -ZT_SOCKET_API int ZTCALL zts_fcntl(int fd, int cmd, int flags); +ZTS_API int ZTCALL zts_fcntl(int fd, int cmd, int flags); #define ZTS_POLLIN 0x001 #define ZTS_POLLOUT 0x002 @@ -1440,7 +1473,7 @@ struct zts_pollfd * @param timeout How long this call should block * @return Number of ready file descriptors on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_poll(struct zts_pollfd *fds, zts_nfds_t nfds, int timeout); +ZTS_API int ZTCALL zts_poll(struct zts_pollfd *fds, zts_nfds_t nfds, int timeout); /** * @brief Control a device (sets zts_errno) @@ -1450,7 +1483,7 @@ ZT_SOCKET_API int ZTCALL zts_poll(struct zts_pollfd *fds, zts_nfds_t nfds, int t * @param argp * @return ZTS_ERR_OK on success, ZTS_ERR_SERVICE on failure. */ -ZT_SOCKET_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); +ZTS_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); /** * @brief Send data to remote host (sets zts_errno) @@ -1461,7 +1494,7 @@ ZT_SOCKET_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); * @param flags * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int flags); +ZTS_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int flags); /** * @brief Send data to remote host (sets zts_errno) @@ -1474,7 +1507,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int f * @param addrlen Length of destination address * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_sendto( +ZTS_API ssize_t ZTCALL zts_sendto( int fd, const void *buf, size_t len, int flags, const struct zts_sockaddr *addr, zts_socklen_t addrlen); struct zts_iovec { @@ -1505,7 +1538,7 @@ struct zts_msghdr { * @param flags * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_sendmsg(int fd, const struct msghdr *msg, int flags); +ZTS_API ssize_t ZTCALL zts_sendmsg(int fd, const struct msghdr *msg, int flags); /** * @brief Receive data from remote host (sets zts_errno) @@ -1516,7 +1549,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_sendmsg(int fd, const struct msghdr *msg, int f * @param flags * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_recv(int fd, void *buf, size_t len, int flags); +ZTS_API ssize_t ZTCALL zts_recv(int fd, void *buf, size_t len, int flags); /** * @brief Receive data from remote host (sets zts_errno) @@ -1529,7 +1562,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_recv(int fd, void *buf, size_t len, int flags); * @param addrlen * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_recvfrom( +ZTS_API ssize_t ZTCALL zts_recvfrom( int fd, void *buf, size_t len, int flags, struct zts_sockaddr *addr, zts_socklen_t *addrlen); /** @@ -1540,7 +1573,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_recvfrom( * @param flags * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_recvmsg(int fd, struct msghdr *msg,int flags); +ZTS_API ssize_t ZTCALL zts_recvmsg(int fd, struct msghdr *msg,int flags); /** * @brief Read bytes from socket onto buffer (sets zts_errno) @@ -1550,7 +1583,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_recvmsg(int fd, struct msghdr *msg,int flags); * @param len Length of data buffer to receive data * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_read(int fd, void *buf, size_t len); +ZTS_API ssize_t ZTCALL zts_read(int fd, void *buf, size_t len); /** * @brief Read bytes from socket into multiple buffers (sets zts_errno) @@ -1560,7 +1593,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_read(int fd, void *buf, size_t len); * @param iovcnt Number of buffers to read into * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_readv(int fd, const struct zts_iovec *iov, int iovcnt); +ZTS_API ssize_t ZTCALL zts_readv(int fd, const struct zts_iovec *iov, int iovcnt); /** * @brief Write bytes from buffer to socket (sets zts_errno) @@ -1570,7 +1603,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_readv(int fd, const struct zts_iovec *iov, int * @param len Length of buffer to write * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_write(int fd, const void *buf, size_t len); +ZTS_API ssize_t ZTCALL zts_write(int fd, const void *buf, size_t len); /** * @brief Write data from multiple buffers to socket. (sets zts_errno) @@ -1580,7 +1613,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_write(int fd, const void *buf, size_t len); * @param iovcnt Number of buffers to read from * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec *iov, int iovcnt); +ZTS_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec *iov, int iovcnt); #define ZTS_SHUT_RD 0x0 #define ZTS_SHUT_WR 0x1 @@ -1593,7 +1626,7 @@ ZT_SOCKET_API ssize_t ZTCALL zts_writev(int fd, const struct zts_iovec *iov, int * @param how Which aspects of the socket should be shut down * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ -ZT_SOCKET_API int ZTCALL zts_shutdown(int fd, int how); +ZTS_API int ZTCALL zts_shutdown(int fd, int how); /** * @brief Add a DNS nameserver for the network stack to use @@ -1601,7 +1634,7 @@ ZT_SOCKET_API int ZTCALL zts_shutdown(int fd, int how); * @param addr Address for DNS nameserver * @return ZTS_ERR_SERVICE */ -ZT_SOCKET_API int ZTCALL zts_add_dns_nameserver(struct zts_sockaddr *addr); +ZTS_API int ZTCALL zts_add_dns_nameserver(struct zts_sockaddr *addr); /** * @brief Remove a DNS nameserver @@ -1609,7 +1642,7 @@ ZT_SOCKET_API int ZTCALL zts_add_dns_nameserver(struct zts_sockaddr *addr); * @param addr Address for DNS nameserver * @return ZTS_ERR_SERVICE */ -ZT_SOCKET_API int ZTCALL zts_del_dns_nameserver(struct zts_sockaddr *addr); +ZTS_API int ZTCALL zts_del_dns_nameserver(struct zts_sockaddr *addr); /** * Convert an uint16_t from host to network byte order. @@ -1617,7 +1650,7 @@ ZT_SOCKET_API int ZTCALL zts_del_dns_nameserver(struct zts_sockaddr *addr); * @param n uint16_t in host byte order * @return n in network byte order */ -ZT_SOCKET_API uint16_t ZTCALL zts_htons(uint16_t n); +ZTS_API uint16_t ZTCALL zts_htons(uint16_t n); /** * Convert an uint32_t from host to network byte order. @@ -1625,7 +1658,7 @@ ZT_SOCKET_API uint16_t ZTCALL zts_htons(uint16_t n); * @param n uint32_t in host byte order * @return n in network byte order */ -ZT_SOCKET_API uint32_t ZTCALL zts_htonl(uint32_t n); +ZTS_API uint32_t ZTCALL zts_htonl(uint32_t n); /** * Length of human-readable MAC address string @@ -1642,7 +1675,7 @@ ZT_SOCKET_API uint32_t ZTCALL zts_htonl(uint32_t n); * @param n uint16_t in network byte order * @return n in host byte order */ -ZT_SOCKET_API uint16_t ZTCALL zts_ntohs(uint16_t n); +ZTS_API uint16_t ZTCALL zts_ntohs(uint16_t n); /** * Convert an uint32_t from network to host byte order. @@ -1650,7 +1683,7 @@ ZT_SOCKET_API uint16_t ZTCALL zts_ntohs(uint16_t n); * @param n uint32_t in network byte order * @return n in host byte order */ -ZT_SOCKET_API uint32_t ZTCALL zts_ntohl(uint32_t n); +ZTS_API uint32_t ZTCALL zts_ntohl(uint32_t n); /** * Convert IPv4 and IPv6 address structures to human-readable text form. @@ -1661,7 +1694,7 @@ ZT_SOCKET_API uint32_t ZTCALL zts_ntohl(uint32_t n); * @param size Size of the destination buffer * @return On success, returns a non-null pointer to the destination character array */ -ZT_SOCKET_API const char * ZTCALL zts_inet_ntop(int af, const void *src, char *dst, zts_socklen_t size); +ZTS_API const char * ZTCALL zts_inet_ntop(int af, const void *src, char *dst, zts_socklen_t size); /** * Convert C-string IPv4 and IPv6 addresses to binary form. @@ -1671,7 +1704,7 @@ ZT_SOCKET_API const char * ZTCALL zts_inet_ntop(int af, const void *src, char *d * @param dst Pointer to destination address structure * @return return 1 on success. 0 or -1 on failure. (Does not follow zts_* conventions) */ -ZT_SOCKET_API int ZTCALL zts_inet_pton(int af, const char *src, void *dst); +ZTS_API int ZTCALL zts_inet_pton(int af, const char *src, void *dst); /** * Convert a C-string IPv4 address to integer representation. @@ -1679,7 +1712,7 @@ ZT_SOCKET_API int ZTCALL zts_inet_pton(int af, const char *src, void *dst); * @param cp Human-readable IPv4 string * @return 32-bit integer representation of address */ -ZT_SOCKET_API uint32_t ZTCALL zts_inet_addr(const char *cp); +ZTS_API uint32_t ZTCALL zts_inet_addr(const char *cp); #ifdef __cplusplus } // extern "C" diff --git a/src/Controls.cpp b/src/Controls.cpp index b9c2168..5e42c1e 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -20,6 +20,7 @@ #include #include +#include "Constants.hpp" #include "Node.hpp" #include "Mutex.hpp" #include "OSUtils.hpp" @@ -36,7 +37,10 @@ using namespace ZeroTier; #include #endif - +#ifdef __WINDOWS__ +#include +WSADATA wsaData; +#endif namespace ZeroTier { @@ -55,6 +59,10 @@ namespace ZeroTier #endif } +#ifdef __cplusplus +extern "C" { +#endif + int zts_allow_network_caching(uint8_t allowed = 1) { Mutex::Lock _l(serviceLock); @@ -121,15 +129,14 @@ int zts_start(const char *path, void (*callback)(void *), uint16_t port) if (params->path.length() == 0) { return ZTS_ERR_ARG; } - int err; int retval = ZTS_ERR_OK; _setState(ZTS_STATE_CALLBACKS_RUNNING); _setState(ZTS_STATE_NODE_RUNNING); - // Start the ZT service thread #if defined(__WINDOWS__) + WSAStartup(MAKEWORD(2, 2), &wsaData); HANDLE serviceThread = CreateThread(NULL, 0, _runNodeService, (void*)params, 0, NULL); HANDLE callbackThread = CreateThread(NULL, 0, _runCallbacks, NULL, 0, NULL); #else @@ -155,11 +162,6 @@ int zts_start(const char *path, void (*callback)(void *), uint16_t port) return retval; } - -#ifdef __cplusplus - extern "C" { -#endif - #ifdef SDK_JNI JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_start( JNIEnv *env, jobject thisObj, jstring path, jobject callback, jint port) @@ -205,7 +207,7 @@ int zts_stop() return ZTS_ERR_SERVICE; } #ifdef SDK_JNI - JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop( +JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_stop( JNIEnv *env, jobject thisObj) { zts_stop(); @@ -255,7 +257,7 @@ int zts_restart() #endif } #ifdef SDK_JNI - JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( +JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_restart( JNIEnv *env, jobject thisObj) { zts_restart(); @@ -264,102 +266,29 @@ int zts_restart() int zts_free() { - Mutex::Lock _l(serviceLock); if (_getState(ZTS_STATE_FREE_CALLED)) { return ZTS_ERR_SERVICE; } _setState(ZTS_STATE_FREE_CALLED); - return zts_stop(); - // TODO: add stack shutdown logic + int err = zts_stop(); + Mutex::Lock _l(serviceLock); + _lwip_driver_shutdown(); + return err; } #ifdef SDK_JNI - JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( +JNIEXPORT void JNICALL Java_com_zerotier_libzt_ZeroTier_free( JNIEnv *env, jobject thisObj) { zts_free(); } #endif -uint64_t zts_get_node_id() -{ - Mutex::Lock _l(serviceLock); - if (!_canPerformServiceOperation()) { - return ZTS_ERR_OK; // Not really - } - return service->getNode()->address(); -} -#ifdef SDK_JNI - JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1id( - JNIEnv *env, jobject thisObj) -{ - return zts_get_node_id(); -} -#endif - -int zts_get_node_status() -{ - Mutex::Lock _l(serviceLock); - // Don't check _canPerformServiceOperation() here. - return service - && service->getNode() - && service->getNode()->online() ? ZTS_EVENT_NODE_ONLINE : ZTS_EVENT_NODE_OFFLINE; -} -#ifdef SDK_JNI - JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1node_1status( - JNIEnv *env, jobject thisObj) -{ - return zts_get_node_status(); -} -#endif - -int zts_get_network_status(uint64_t networkId) -{ - Mutex::Lock _l(serviceLock); - if (!networkId) { - return ZTS_ERR_ARG; - } - if (!_canPerformServiceOperation()) { - return ZTS_ERR_SERVICE; - } - /* - TODO: - ZTS_EVENT_NETWORK_READY_IP4 - ZTS_EVENT_NETWORK_READY_IP6 - ZTS_EVENT_NETWORK_READY_IP4_IP6 - */ - return ZTS_ERR_NO_RESULT; -} -#ifdef SDK_JNI - JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_get_1network_1status( - JNIEnv *env, jobject thisObj, jlong networkId) -{ - return zts_get_network_status(networkId); -} -#endif - -int zts_get_peer_status(uint64_t peerId) -{ - Mutex::Lock _l(serviceLock); - int retval = ZTS_ERR_OK; - if (!_canPerformServiceOperation()) { - return ZTS_ERR_SERVICE; - } - return service->getPeerStatus(peerId); -} -#ifdef SDK_JNI - JNIEXPORT jlong JNICALL Java_com_zerotier_libzt_ZeroTier_get_1peer_1status( - JNIEnv *env, jobject thisObj, jlong peerId) -{ - return zts_get_peer_status(peerId); -} -#endif - #ifdef SDK_JNI /* * Called from Java, saves a static reference to the VM so it can be used * later to call a user-specified callback method from C. */ - JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init( +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_init( JNIEnv *env, jobject thisObj) { jint rs = env->GetJavaVM(&jvm); @@ -367,63 +296,44 @@ int zts_get_peer_status(uint64_t peerId) } #endif -int zts_join(const uint64_t nwid) +int zts_join(const uint64_t networkId) { Mutex::Lock _l(serviceLock); if (!_canPerformServiceOperation()) { return ZTS_ERR_SERVICE; } else { - service->join(nwid); + service->join(networkId); } return ZTS_ERR_OK; } #ifdef SDK_JNI - JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( - JNIEnv *env, jobject thisObj, jlong nwid) +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_join( + JNIEnv *env, jobject thisObj, jlong networkId) { - return zts_join((uint64_t)nwid); + return zts_join((uint64_t)networkId); } #endif -#ifdef __cplusplus -} -#endif - - -int zts_leave(const uint64_t nwid) +int zts_leave(const uint64_t networkId) { Mutex::Lock _l(serviceLock); if (!_canPerformServiceOperation()) { return ZTS_ERR_SERVICE; } else { - service->leave(nwid); + service->leave(networkId); } return ZTS_ERR_OK; } #ifdef SDK_JNI - JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( - JNIEnv *env, jobject thisObj, jlong nwid) +JNIEXPORT jint JNICALL Java_com_zerotier_libzt_ZeroTier_leave( + JNIEnv *env, jobject thisObj, jlong networkId) { - return zts_leave((uint64_t)nwid); + return zts_leave((uint64_t)networkId); } #endif -int zts_leave_all() -{ - Mutex::Lock _l(serviceLock); - if (!_canPerformServiceOperation()) { - return ZTS_ERR_SERVICE; - } - else { - service->leaveAll(); - } - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -#endif - int zts_orbit(uint64_t moonWorldId, uint64_t moonSeed) { Mutex::Lock _l(serviceLock); @@ -452,197 +362,57 @@ int zts_deorbit(uint64_t moonWorldId) #ifdef SDK_JNI #endif -int zts_get_6plane_addr(struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +int zts_get_6plane_addr(struct zts_sockaddr_storage *addr, const uint64_t networkId, const uint64_t nodeId) { - if (!addr || !nwid || !nodeId) { + if (!addr || !networkId || !nodeId) { return ZTS_ERR_ARG; } - InetAddress _6planeAddr = InetAddress::makeIpv66plane(nwid,nodeId); + InetAddress _6planeAddr = InetAddress::makeIpv66plane(networkId,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _6planeAddr.rawIpData(), sizeof(struct in6_addr)); return ZTS_ERR_OK; } -int zts_get_rfc4193_addr(struct zts_sockaddr_storage *addr, const uint64_t nwid, const uint64_t nodeId) +int zts_get_rfc4193_addr(struct zts_sockaddr_storage *addr, const uint64_t networkId, const uint64_t nodeId) { - if (!addr || !nwid || !nodeId) { + if (!addr || !networkId || !nodeId) { return ZTS_ERR_ARG; } - InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(nwid,nodeId); + InetAddress _rfc4193Addr = InetAddress::makeIpv6rfc4193(networkId,nodeId); struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr; memcpy(in6->sin6_addr.s6_addr, _rfc4193Addr.rawIpData(), sizeof(struct in6_addr)); return ZTS_ERR_OK; } - uint64_t zts_generate_adhoc_nwid_from_range(uint16_t startPortOfRange, uint16_t endPortOfRange) { - char nwidStr[INET6_ADDRSTRLEN]; - sprintf(nwidStr, "ff%04x%04x000000", startPortOfRange, endPortOfRange); - return strtoull(nwidStr, NULL, 16); + char networkIdStr[INET6_ADDRSTRLEN]; + sprintf(networkIdStr, "ff%04x%04x000000", startPortOfRange, endPortOfRange); + return strtoull(networkIdStr, NULL, 16); } -int zts_get_peers(struct zts_peer_details *pds, uint32_t *num) -{ - Mutex::Lock _l(serviceLock); - if (!pds || !num) { - return ZTS_ERR_ARG; - } - if (!_canPerformServiceOperation()) { - return ZTS_ERR_SERVICE; - } - ZT_PeerList *pl = service->getNode()->peers(); - if (pl) { - if (*num < pl->peerCount) { - service->getNode()->freeQueryResult((void *)pl); - return ZTS_ERR_ARG; - } - *num = pl->peerCount; - for(unsigned long i=0;ipeerCount;++i) { - memcpy(&(pds[i]), &(pl->peers[i]), sizeof(struct zts_peer_details)); - for (unsigned int j=0; jpeers[i].pathCount; j++) { - memcpy(&(pds[i].paths[j].address), - &(pl->peers[i].paths[j].address), sizeof(struct sockaddr_storage)); - } - } - } - service->getNode()->freeQueryResult((void *)pl); - return ZTS_ERR_OK; -} -#ifdef SDK_JNI -#endif - -int zts_get_peer(struct zts_peer_details *pd, uint64_t peerId) -{ - Mutex::Lock _l(serviceLock); - if (!pd || !peerId) { - return ZTS_ERR_ARG; - } - if (!_canPerformServiceOperation()) { - return ZTS_ERR_SERVICE; - } - ZT_PeerList *pl = service->getNode()->peers(); - int retval = ZTS_ERR_NO_RESULT; - if (pl) { - for(unsigned long i=0;ipeerCount;++i) { - if (pl->peers[i].address == peerId) { - memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - for (unsigned int j=0; jpeers[i].pathCount; j++) { - memcpy(&(pd->paths[j].address), - &(pl->peers[i].paths[j].address), sizeof(struct sockaddr_storage)); - } - retval = ZTS_ERR_OK; - break; - } - } - } - service->getNode()->freeQueryResult((void *)pl); - return retval; -} -#ifdef SDK_JNI -#endif - -void _get_network_details_helper(uint64_t nwid, struct zts_network_details *nd) -{ - /* - socklen_t addrlen; - VirtualTap *tap = vtapMap[nwid]; - nd->nwid = tap->_nwid; - nd->mtu = tap->_mtu; - // assigned addresses - nd->num_addresses = tap->_ips.size() < ZTS_MAX_ASSIGNED_ADDRESSES ? tap->_ips.size() : ZTS_MAX_ASSIGNED_ADDRESSES; - for (int j=0; jnum_addresses; j++) { - addrlen = tap->_ips[j].isV4() ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); - memcpy(&(nd->addr[j]), &(tap->_ips[j]), addrlen); - } - // routes - nd->num_routes = ZTS_MAX_NETWORK_ROUTES;; - service->getRoutes(nwid, (ZT_VirtualNetworkRoute*)&(nd->routes)[0], &(nd->num_routes)); - */ -} - -void _get_network_details(uint64_t nwid, struct zts_network_details *nd) -{ - /* - _vtaps_lock.lock(); - _get_network_details_helper(nwid, nd); - _vtaps_lock.unlock(); - */ -} - -void _get_all_network_details(struct zts_network_details *nds, int *num) -{ - /* - _vtaps_lock.lock(); - *num = vtapMap.size(); - int idx = 0; - std::map::iterator it; - for (it = vtapMap.begin(); it != vtapMap.end(); it++) { - _get_network_details(it->first, &nds[idx]); - idx++; - } - _vtaps_lock.unlock(); - */ -} - -int zts_get_network_details(uint64_t nwid, struct zts_network_details *nd) -{ - /* - serviceLock.lock(); - int retval = ZTS_ERR_OK; - if (!nd || nwid == 0) { - retval = ZTS_ERR_ARG; - } - if (!service || _freeHasBeenCalled || _serviceIsShuttingDown) { - retval = ZTS_ERR_SERVICE; - } - if (retval == ZTS_ERR_OK) { - _get_network_details(nwid, nd); - } - serviceLock.unlock(); - return retval; - */ - return 0; -} -#ifdef SDK_JNI -#endif - -int zts_get_all_network_details(struct zts_network_details *nds, int *num) -{ - /* - serviceLock.lock(); - int retval = ZTS_ERR_OK; - if (!nds || !num) { - retval = ZTS_ERR_ARG; - } - if (!service || _freeHasBeenCalled || _serviceIsShuttingDown) { - retval = ZTS_ERR_SERVICE; - } - if (retval == ZTS_ERR_OK) { - _get_all_network_details(nds, num); - } - serviceLock.unlock(); - return retval; - */ - return 0; -} -#ifdef SDK_JNI -#endif - - - - -void zts_delay_ms(long interval_ms) -{ -#if defined(__WINDOWS__) - Sleep(interval_ms); +#ifdef __WINDOWS__ +#include +#elif _POSIX_C_SOURCE >= 199309L +#include // for nanosleep #else - struct timespec sleepValue = {0}; - sleepValue.tv_nsec = interval_ms * 500000; - nanosleep(&sleepValue, NULL); +#include // for usleep +#endif + +void zts_delay_ms(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 } - - - +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/Debug.hpp b/src/Debug.hpp index fc35225..4c966d5 100644 --- a/src/Debug.hpp +++ b/src/Debug.hpp @@ -136,7 +136,6 @@ #else #define DEBUG_TRANS(fmt, args...) #endif - #else // !LIBZT_DEBUG || !__NATIVE_TEST__ #if defined(_WIN32) #define DEBUG_ERROR(...) diff --git a/src/Events.cpp b/src/Events.cpp index 3cbd332..4025f6a 100644 --- a/src/Events.cpp +++ b/src/Events.cpp @@ -23,6 +23,7 @@ #include #endif +#include "Constants.hpp" #include "Node.hpp" #include "OSUtils.hpp" @@ -32,11 +33,10 @@ #include "NodeService.hpp" #define NODE_EVENT_TYPE(code) code >= ZTS_EVENT_NODE_UP && code <= ZTS_EVENT_NODE_NORMAL_TERMINATION -#define NETWORK_EVENT_TYPE(code) code >= ZTS_EVENT_NETWORK_NOT_FOUND && code <= ZTS_EVENT_NETWORK_DOWN +#define NETWORK_EVENT_TYPE(code) code >= ZTS_EVENT_NETWORK_NOT_FOUND && code <= ZTS_EVENT_NETWORK_UPDATE #define STACK_EVENT_TYPE(code) code >= ZTS_EVENT_STACK_UP && code <= ZTS_EVENT_STACK_DOWN #define NETIF_EVENT_TYPE(code) code >= ZTS_EVENT_NETIF_UP && code <= ZTS_EVENT_NETIF_LINK_DOWN -#define PEER_EVENT_TYPE(code) code >= ZTS_EVENT_PEER_DIRECT && code <= ZTS_EVENT_PEER_UNREACHABLE -#define PATH_EVENT_TYPE(code) code >= ZTS_EVENT_PATH_DISCOVERED && code <= ZTS_EVENT_PATH_DEAD +#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 @@ -52,41 +52,39 @@ Mutex _callbackLock; void (*_userEventCallbackFunc)(void *); -moodycamel::ConcurrentQueue _callbackMsgQueue; +moodycamel::ConcurrentQueue _callbackMsgQueue; void _enqueueEvent(int16_t eventCode, void *arg) { - struct ::zts_callback_msg *msg = new ::zts_callback_msg(); - - msg->node = NULL; - msg->network = NULL; - msg->netif = NULL; - msg->route = NULL; - msg->path = NULL; - msg->peer = NULL; - msg->addr = NULL; - + struct zts_callback_msg *msg = new zts_callback_msg(); msg->eventCode = eventCode; if (NODE_EVENT_TYPE(eventCode)) { msg->node = (struct zts_node_details*)arg; } if (NETWORK_EVENT_TYPE(eventCode)) { msg->network = (struct zts_network_details*)arg; + } if (STACK_EVENT_TYPE(eventCode)) { + /* nothing to convey to user */ } if (NETIF_EVENT_TYPE(eventCode)) { msg->netif = (struct zts_netif_details*)arg; } if (ROUTE_EVENT_TYPE(eventCode)) { msg->route = (struct zts_virtual_network_route*)arg; - } if (PATH_EVENT_TYPE(eventCode)) { - msg->path = (struct zts_physical_path*)arg; } if (PEER_EVENT_TYPE(eventCode)) { msg->peer = (struct zts_peer_details*)arg; } if (ADDR_EVENT_TYPE(eventCode)) { msg->addr = (struct zts_addr_details*)arg; } - _callbackMsgQueue.enqueue(msg); + + if (msg && _callbackMsgQueue.size_approx() > 1024) { + // Rate-limit number of events + _freeEvent(msg); + } + else { + _callbackMsgQueue.enqueue(msg); + } } -void _freeEvent(struct ::zts_callback_msg *msg) +void _freeEvent(struct zts_callback_msg *msg) { if (!msg) { return; @@ -95,21 +93,21 @@ void _freeEvent(struct ::zts_callback_msg *msg) if (msg->network) { delete msg->network; } if (msg->netif) { delete msg->netif; } if (msg->route) { delete msg->route; } - if (msg->path) { delete msg->path; } if (msg->peer) { delete msg->peer; } if (msg->addr) { delete msg->addr; } } -void _passDequeuedEventToUser(struct ::zts_callback_msg *msg) +void _passDequeuedEventToUser(struct zts_callback_msg *msg) { + bool bShouldStopCallbackThread = (msg->eventCode == ZTS_EVENT_STACK_DOWN); #ifdef SDK_JNI if(_userCallbackMethodRef) { JNIEnv *env; -#if defined(__ANDROID__) + #if defined(__ANDROID__) jint rs = jvm->AttachCurrentThread(&env, NULL); -#else + #else jint rs = jvm->AttachCurrentThread((void **)&env, NULL); -#endif + #endif assert (rs == JNI_OK); uint64_t arg = 0; uint64_t id = 0; @@ -131,6 +129,11 @@ void _passDequeuedEventToUser(struct ::zts_callback_msg *msg) _freeEvent(msg); } #endif + if (bShouldStopCallbackThread) { + /* Ensure last possible callback ZTS_EVENT_STACK_DOWN is + delivered before callback thread is finally stopped. */ + _clrState(ZTS_STATE_CALLBACKS_RUNNING); + } } bool _isCallbackRegistered() @@ -168,8 +171,8 @@ int _canPerformServiceOperation() } #define RESET_FLAGS( ) _serviceStateFlags = 0; -#define SET_FLAGS(f) _serviceStateFlags |= f; -#define CLR_FLAGS(f) _serviceStateFlags &= ~f; +#define SET_FLAGS(f) _serviceStateFlags |= f; +#define CLR_FLAGS(f) _serviceStateFlags &= ~f; #define GET_FLAGS(f) ((_serviceStateFlags & f) > 0) void _setState(uint8_t newFlags) @@ -222,7 +225,7 @@ void *_runCallbacks(void *thread_id) #endif while (_getState(ZTS_STATE_CALLBACKS_RUNNING) || _callbackMsgQueue.size_approx() > 0) { - struct ::zts_callback_msg *msg; + struct zts_callback_msg *msg; size_t sz = _callbackMsgQueue.size_approx(); for (size_t j = 0; j < sz; j++) { if (_callbackMsgQueue.try_dequeue(msg)) { diff --git a/src/Events.hpp b/src/Events.hpp index 58a3406..563140a 100644 --- a/src/Events.hpp +++ b/src/Events.hpp @@ -22,8 +22,13 @@ #include +#include "Constants.hpp" #include "ZeroTierSockets.h" +#ifdef __WINDOWS__ +#include +#endif + #ifdef SDK_JNI #include #endif diff --git a/src/NodeService.cpp b/src/NodeService.cpp index 3a04d61..7c44686 100644 --- a/src/NodeService.cpp +++ b/src/NodeService.cpp @@ -18,6 +18,9 @@ */ #include +#include + +#include "../version.h" #include "Debug.hpp" #include "Events.hpp" @@ -39,7 +42,7 @@ #include "BlockingQueue.hpp" #if defined(__WINDOWS__) -WSADATA wsaData; +//WSADATA wsaData; #include #include #include @@ -123,7 +126,7 @@ public: volatile unsigned int _udpPortPickerCounter; // - std::map peerCache; + std::map peerCache; // unsigned long _incomingPacketConcurrency; @@ -230,10 +233,6 @@ public: allowNetworkCaching = true; allowPeerCaching = true; allowLocalConf = false; -#ifdef __WINDOWS__ - // Initialize WinSock. Used in Phy for loopback pipe - WSAStartup(MAKEWORD(2, 2), &wsaData); -#endif } virtual ~NodeServiceImpl() @@ -609,14 +608,42 @@ public: newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end()); for(std::vector::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) { if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) { - if (!n.tap->removeIp(*ip)) + if (!n.tap->removeIp(*ip)) { fprintf(stderr,"ERROR: unable to remove ip address %s" ZT_EOL_S, ip->toString(ipbuf)); + } else { + struct zts_addr_details *ad = new zts_addr_details(); + ad->nwid = n.tap->_nwid; + if ((*ip).isV4()) { + struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); + memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4); + _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad); + } + if ((*ip).isV6()) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); + memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16); + _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad); + } + } } } for(std::vector::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) { if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) { - if (!n.tap->addIp(*ip)) + if (!n.tap->addIp(*ip)) { fprintf(stderr,"ERROR: unable to add ip address %s" ZT_EOL_S, ip->toString(ipbuf)); + } else { + struct zts_addr_details *ad = new zts_addr_details(); + ad->nwid = n.tap->_nwid; + if ((*ip).isV4()) { + struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); + memcpy(&(in4->sin_addr.s_addr), (*ip).rawIpData(), 4); + _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad); + } + if ((*ip).isV6()) { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); + memcpy(&(in6->sin6_addr.s6_addr), (*ip).rawIpData(), 16); + _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad); + } + } } } n.managedIps.swap(newManagedIps); @@ -694,6 +721,9 @@ public: _nets.erase(nwid); return -999; // tap init failed } + if (op == ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE) { // Prevent junk from ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP + _enqueueEvent(ZTS_EVENT_NETWORK_UPDATE, (void*)prepare_network_details_msg(n)); + } break; case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN: @@ -727,6 +757,12 @@ public: case ZT_EVENT_ONLINE: { struct zts_node_details *nd = new zts_node_details; nd->address = _node->address(); + nd->versionMajor = ZEROTIER_ONE_VERSION_MAJOR; + nd->versionMinor = ZEROTIER_ONE_VERSION_MINOR; + nd->versionRev = ZEROTIER_ONE_VERSION_REVISION; + nd->primaryPort = _primaryPort; + nd->secondaryPort = _secondaryPort; + nd->tertiaryPort = _tertiaryPort; _enqueueEvent(ZTS_EVENT_NODE_ONLINE, (void*)nd); } break; case ZT_EVENT_OFFLINE: { @@ -758,10 +794,65 @@ public: } } - inline struct zts_network_details *prepare_network_details_msg(uint64_t nwid) + void native_ss_to_zts_ss(struct zts_sockaddr_storage *ss_out, const struct sockaddr_storage *ss_in) { - struct zts_network_details *nd = new zts_network_details; - nd->nwid = nwid; + if (ss_in->ss_family == AF_INET) { + struct sockaddr_in *s_in4 = (struct sockaddr_in *)ss_in; + struct zts_sockaddr_in *d_in4 = (struct zts_sockaddr_in *)ss_out; +#ifndef __WINDOWS__ + d_in4->sin_len = 0; // s_in4->sin_len; +#endif + d_in4->sin_family = ZTS_AF_INET; + d_in4->sin_port = s_in4->sin_port; + memcpy(&(d_in4->sin_addr), &(s_in4->sin_addr), sizeof(s_in4->sin_addr)); + } + if (ss_in->ss_family == AF_INET6) { + struct sockaddr_in6 *s_in6 = (struct sockaddr_in6 *)ss_in; + struct zts_sockaddr_in6 *d_in6 = (struct zts_sockaddr_in6 *)ss_out; +#ifndef __WINDOWS__ + d_in6->sin6_len = 0; // s_in6->sin6_len; +#endif + d_in6->sin6_family = ZTS_AF_INET6; + d_in6->sin6_port = s_in6->sin6_port; + d_in6->sin6_flowinfo = s_in6->sin6_flowinfo; + memcpy(&(d_in6->sin6_addr), &(s_in6->sin6_addr), sizeof(s_in6->sin6_addr)); + d_in6->sin6_scope_id = s_in6->sin6_scope_id; + } + } + + struct zts_network_details *prepare_network_details_msg(const NetworkState &n) + { + struct zts_network_details *nd = new zts_network_details(); + + nd->nwid = n.config.nwid; + nd->mac = n.config.mac; + memcpy(nd->name, n.config.name, sizeof(n.config.name)); + nd->status = (ZTS_VirtualNetworkStatus)n.config.status; + nd->type = (ZTS_VirtualNetworkType)n.config.type; + nd->mtu = n.config.mtu; + nd->dhcp = n.config.dhcp; + nd->bridge = n.config.bridge; + nd->broadcastEnabled = n.config.broadcastEnabled; + nd->portError = n.config.portError; + nd->netconfRevision = n.config.netconfRevision; + + // Copy and convert address structures + nd->assignedAddressCount = n.config.assignedAddressCount; + for (int i=0; iassignedAddresses[i]), &(n.config.assignedAddresses[i])); + } + + nd->routeCount = n.config.routeCount; + for (int i=0; iroutes[i].target), &(n.config.routes[i].target)); + native_ss_to_zts_ss(&(nd->routes[i].via), &(n.config.routes[i].via)); + nd->routes[i].flags = n.config.routes[i].flags; + nd->routes[i].metric = n.config.routes[i].metric; + } + + nd->multicastSubscriptionCount = n.config.multicastSubscriptionCount; + memcpy(nd->multicastSubscriptions, &(n.config.multicastSubscriptions), sizeof(n.config.multicastSubscriptions)); + return nd; } @@ -783,34 +874,34 @@ public: } switch (mostRecentStatus) { case ZT_NETWORK_STATUS_NOT_FOUND: - _enqueueEvent(ZTS_EVENT_NETWORK_NOT_FOUND, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_NOT_FOUND, (void*)prepare_network_details_msg(n->second)); break; case ZT_NETWORK_STATUS_CLIENT_TOO_OLD: - _enqueueEvent(ZTS_EVENT_NETWORK_CLIENT_TOO_OLD, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_CLIENT_TOO_OLD, (void*)prepare_network_details_msg(n->second)); break; case ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION: - _enqueueEvent(ZTS_EVENT_NETWORK_REQ_CONFIG, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_REQ_CONFIG, (void*)prepare_network_details_msg(n->second)); break; case ZT_NETWORK_STATUS_OK: if (tap->hasIpv4Addr() && _lwip_is_netif_up(tap->netif4)) { - _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP4, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP4, (void*)prepare_network_details_msg(n->second)); } if (tap->hasIpv6Addr() && _lwip_is_netif_up(tap->netif6)) { - _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP6, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_READY_IP6, (void*)prepare_network_details_msg(n->second)); } // In addition to the READY messages, send one OK message - _enqueueEvent(ZTS_EVENT_NETWORK_OK, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_OK, (void*)prepare_network_details_msg(n->second)); break; case ZT_NETWORK_STATUS_ACCESS_DENIED: - _enqueueEvent(ZTS_EVENT_NETWORK_ACCESS_DENIED, (void*)prepare_network_details_msg(nwid)); + _enqueueEvent(ZTS_EVENT_NETWORK_ACCESS_DENIED, (void*)prepare_network_details_msg(n->second)); break; default: break; } n->second.tap->_networkStatus = mostRecentStatus; } - - // TODO: Add ZTS_EVENT_PEER_NEW + bool bShouldCopyPeerInfo = false; + int eventCode = 0; ZT_PeerList *pl = _node->peers(); struct zts_peer_details *pd; if (pl) { @@ -818,28 +909,41 @@ public: if (!peerCache.count(pl->peers[i].address)) { // New peer, add status if (pl->peers[i].pathCount > 0) { - pd = new zts_peer_details; - memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - _enqueueEvent(ZTS_EVENT_PEER_DIRECT, (void*)pd); + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_DIRECT; } if (pl->peers[i].pathCount == 0) { - pd = new zts_peer_details; - memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - _enqueueEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_RELAY, (void*)pd; } } // Previously known peer, update status else { - if ((peerCache[pl->peers[i].address] == false) && pl->peers[i].pathCount > 0) { - pd = new zts_peer_details; - memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - _enqueueEvent(ZTS_EVENT_PEER_DIRECT, (void*)pd); + if (peerCache[pl->peers[i].address] < pl->peers[i].pathCount) { + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_PATH_DISCOVERED, (void*)pd; } - if ((peerCache[pl->peers[i].address] == true) && pl->peers[i].pathCount == 0) { - pd = new zts_peer_details; - memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); - _enqueueEvent(ZTS_EVENT_PEER_RELAY, (void*)pd); + if (peerCache[pl->peers[i].address] > pl->peers[i].pathCount) { + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_PATH_DEAD, (void*)pd; } + if (peerCache[pl->peers[i].address] == 0 && pl->peers[i].pathCount > 0) { + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_DIRECT, (void*)pd; + } + if (peerCache[pl->peers[i].address] > 0 && pl->peers[i].pathCount == 0) { + bShouldCopyPeerInfo=true; + eventCode = ZTS_EVENT_PEER_RELAY, (void*)pd; + } + } + if (bShouldCopyPeerInfo) { + pd = new zts_peer_details(); + memcpy(pd, &(pl->peers[i]), sizeof(struct zts_peer_details)); + for (unsigned int j=0; jpeers[i].pathCount; j++) { + native_ss_to_zts_ss(&(pd->paths[j].address), &(pl->peers[i].paths[j].address)); + } + _enqueueEvent(eventCode, (void*)pd); + bShouldCopyPeerInfo = false; } // Update our cache with most recently observed path count peerCache[pl->peers[i].address] = pl->peers[i].pathCount; @@ -848,12 +952,6 @@ public: _node->freeQueryResult((void *)pl); } - inline size_t networkCount() - { - Mutex::Lock _l(_nets_m); - return _nets.size(); - } - inline void join(uint64_t nwid) { _node->join(nwid, NULL, NULL); @@ -864,30 +962,6 @@ public: _node->leave(nwid, NULL, NULL); } - inline void leaveAll() - { - Mutex::Lock _l(_nets_m); - for(std::map::iterator n(_nets.begin());n!=_nets.end();++n) { - _node->leave(n->first, NULL, NULL); - } - } - - inline int getPeerStatus(uint64_t id) - { - ZT_PeerList *pl = _node->peers(); - int status = ZTS_EVENT_PEER_UNREACHABLE; - if (pl) { - for(unsigned long i=0;ipeerCount;++i) { - if (pl->peers[i].address == id) { - status = pl->peers[i].pathCount > 0 ? ZTS_EVENT_PEER_DIRECT : ZTS_EVENT_PEER_RELAY; - break; - } - } - } - _node->freeQueryResult((void *)pl); - return status; - } - inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len) { char p[1024]; @@ -1232,7 +1306,7 @@ DWORD WINAPI _runNodeService(LPVOID arg) #else void *_runNodeService(void *arg) #endif -{ +{ #if defined(__APPLE__) pthread_setname_np(ZTS_SERVICE_THREAD_NAME); #endif @@ -1295,12 +1369,12 @@ void *_runNodeService(void *arg) service = (NodeService *)0; serviceLock.unlock(); _enqueueEvent(ZTS_EVENT_NODE_DOWN,NULL); - } catch ( ... ) { + } + catch ( ... ) { DEBUG_ERROR("unexpected exception starting ZeroTier instance"); } delete params; zts_delay_ms(ZTS_CALLBACK_PROCESSING_INTERVAL*2); - _clrState(ZTS_STATE_CALLBACKS_RUNNING); #ifndef __WINDOWS__ pthread_exit(0); #endif diff --git a/src/NodeService.hpp b/src/NodeService.hpp index 43e4ce8..6eb9bf9 100644 --- a/src/NodeService.hpp +++ b/src/NodeService.hpp @@ -23,6 +23,7 @@ #include #include +#include "Constants.hpp" #include "Node.hpp" #include "InetAddress.hpp" #include "Mutex.hpp" @@ -168,12 +169,9 @@ public: */ virtual void getRoutes(uint64_t nwid, void *routeArray, unsigned int *numRoutes) = 0; - virtual size_t networkCount() = 0; - virtual void leaveAll() = 0; virtual void join(uint64_t nwid) = 0; virtual void leave(uint64_t nwid) = 0; - virtual int getPeerStatus(uint64_t id) = 0; - + /** * Terminate background service (can be called from other threads) */ diff --git a/src/Sockets.cpp b/src/Sockets.cpp index 3366209..4ce7cdb 100644 --- a/src/Sockets.cpp +++ b/src/Sockets.cpp @@ -23,7 +23,13 @@ #include "lwip/stats.h" #include "ZeroTierSockets.h" -#include "Events.hpp" +//#include "Events.hpp" + +#define ZTS_STATE_NODE_RUNNING 0x01 +#define ZTS_STATE_STACK_RUNNING 0x02 +#define ZTS_STATE_NET_SERVICE_RUNNING 0x04 +#define ZTS_STATE_CALLBACKS_RUNNING 0x08 +#define ZTS_STATE_FREE_CALLED 0x10 #ifdef SDK_JNI #include diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index 52e2f56..c9063d2 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -17,6 +17,7 @@ * Virtual ethernet tap device and combined network stack driver */ +#include "Constants.hpp" #include "MAC.hpp" #include "Mutex.hpp" #include "InetAddress.hpp" @@ -90,7 +91,6 @@ VirtualTap::~VirtualTap() { struct zts_network_details *nd = new zts_network_details; nd->nwid = _nwid; - _enqueueEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); _run = false; #ifndef __WINDOWS__ ::write(_shutdownSignalPipe[1],"\0",1); @@ -100,6 +100,7 @@ VirtualTap::~VirtualTap() netif4 = NULL; _lwip_remove_netif(netif6); netif6 = NULL; + _enqueueEvent(ZTS_EVENT_NETWORK_DOWN, (void*)nd); Thread::join(_thread); #ifndef __WINDOWS__ ::close(_shutdownSignalPipe[0]); @@ -170,21 +171,7 @@ bool VirtualTap::addIp(const InetAddress &ip) } if (std::find(_ips.begin(),_ips.end(),ip) == _ips.end()) { _lwip_init_interface((void*)this, ip); - // TODO: Add ZTS_EVENT_ADDR_NEW ? _ips.push_back(ip); - // Send callback message - struct zts_addr_details *ad = new zts_addr_details; - ad->nwid = _nwid; - if (ip.isV4()) { - struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); - memcpy(&(in4->sin_addr.s_addr), ip.rawIpData(), 4); - _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP4, (void*)ad); - } - if (ip.isV6()) { - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); - memcpy(&(in6->sin6_addr.s6_addr), ip.rawIpData(), 16); - _enqueueEvent(ZTS_EVENT_ADDR_ADDED_IP6, (void*)ad); - } std::sort(_ips.begin(),_ips.end()); } return true; @@ -195,20 +182,7 @@ bool VirtualTap::removeIp(const InetAddress &ip) Mutex::Lock _l(_ips_m); std::vector::iterator i(std::find(_ips.begin(),_ips.end(),ip)); if (std::find(_ips.begin(),_ips.end(),ip) != _ips.end()) { - struct zts_addr_details *ad = new zts_addr_details; - ad->nwid = _nwid; - if (ip.isV4()) { - struct sockaddr_in *in4 = (struct sockaddr_in*)&(ad->addr); - memcpy(&(in4->sin_addr.s_addr), ip.rawIpData(), 4); - _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP4, (void*)ad); - // FIXME: De-register from network stack - } - if (ip.isV6()) { - // FIXME: De-register from network stack - struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(ad->addr); - memcpy(&(in6->sin6_addr.s6_addr), ip.rawIpData(), 16); - _enqueueEvent(ZTS_EVENT_ADDR_REMOVED_IP6, (void*)ad); - } + _lwip_remove_address_from_netif((void*)this, ip); _ips.erase(i); } return true; @@ -361,18 +335,12 @@ bool _lwip_is_up() return _getState(ZTS_STATE_STACK_RUNNING); } -bool _lwip_has_previously_shutdown() -{ - Mutex::Lock _l(stackLock); - return _has_exited; -} - void _lwip_driver_init() { if (_lwip_is_up()) { return; } - if (_lwip_has_previously_shutdown()) { + if (_has_exited) { return; } Mutex::Lock _l(stackLock); @@ -385,7 +353,7 @@ void _lwip_driver_init() void _lwip_driver_shutdown() { - if (_lwip_has_previously_shutdown()) { + if (_has_exited) { return; } Mutex::Lock _l(stackLock); @@ -393,11 +361,6 @@ void _lwip_driver_shutdown() _clrState(ZTS_STATE_STACK_RUNNING); // Wait until the main lwIP thread has exited while (!_has_exited) { zts_delay_ms(LWIP_DRIVER_LOOP_INTERVAL); } - /* - if (tcpip_shutdown() == ERR_OK) { - sys_timeouts_free(); - } - */ } void _lwip_remove_netif(void *netif) @@ -532,28 +495,6 @@ void _lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int } } -/* -static void print_netif_info(struct netif *n) { - DEBUG_INFO("n=%p, %c%c, %d, o=%p, o6=%p, mc=%x:%x:%x:%x:%x:%x, hwln=%d, st=%p, flgs=%d\n", - n, - n->name[0], - n->name[1], - n->mtu, - n->output, - n->output_ip6, - n->hwaddr[0], - n->hwaddr[1], - n->hwaddr[2], - n->hwaddr[3], - n->hwaddr[4], - n->hwaddr[5], - n->hwaddr_len, - n->state, - n->flags - ); -} -*/ - bool _lwip_is_netif_up(void *n) { if (!n) { @@ -565,10 +506,19 @@ bool _lwip_is_netif_up(void *n) return result; } -/** - * Called when a netif is removed (ZTS_EVENT_NETIF_INTERFACE_REMOVED) - */ -#if LWIP_NETIF_REMOVE_CALLBACK +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 @@ -576,75 +526,35 @@ static void _netif_remove_callback(struct netif *n) return; } VirtualTap *tap = (VirtualTap *)n->state; - uint64_t mac = 0; - memcpy(&mac, n->hwaddr, n->hwaddr_len); - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - _enqueueEvent(ZTS_EVENT_NETIF_REMOVED, (void*)ifd); + 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)); + } } -#endif -/** - * Called when a link is brought up or down (ZTS_EVENT_NETIF_LINK_UP, ZTS_EVENT_NETIF_LINK_DOWN) - */ -#if LWIP_NETIF_LINK_CALLBACK static void _netif_link_callback(struct netif *n) { // Called from core, no need to lock - if (!n || !n->state) { - return; - } - VirtualTap *tap = (VirtualTap *)n->state; - uint64_t mac = 0; - memcpy(&mac, n->hwaddr, n->hwaddr_len); - if (n->flags & NETIF_FLAG_LINK_UP) { - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - _enqueueEvent(ZTS_EVENT_NETIF_LINK_UP, (void*)ifd); - } - if (n->flags & NETIF_FLAG_LINK_UP) { - struct zts_netif_details *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = lwip_htonl(ifd->mac) >> 16; - _enqueueEvent(ZTS_EVENT_NETIF_LINK_DOWN, (void*)ifd); - } -} -#endif - -void _lwip_set_callbacks(struct netif *n) -{ 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)); } -#if LWIP_NETIF_STATUS_CALLBACK - // Not currently used - netif_set_status_callback(n, netif_status_callback); -#endif -#if LWIP_NETIF_REMOVE_CALLBACK - netif_set_remove_callback(n, netif_remove_callback); -#endif -#if LWIP_NETIF_LINK_CALLBACK - netif_set_link_callback(n, netif_link_callback); -#endif -} - -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 *ifd = new zts_netif_details; - ifd->nwid = tap->_nwid; - ifd->mtu = n->mtu; - memcpy(&(ifd->mac), n->hwaddr, n->hwaddr_len); - ifd->mac = htonll(ifd->mac) >> 16; - return ifd; } static err_t _netif_init4(struct netif *n) @@ -652,7 +562,7 @@ static err_t _netif_init4(struct netif *n) if (!n || !n->state) { return ERR_IF; } - // Called from netif code, no need to lock + // Called from core, no need to lock VirtualTap *tap = (VirtualTap*)(n->state); n->hwaddr_len = 6; n->name[0] = '4'; @@ -680,7 +590,7 @@ static err_t _netif_init6(struct netif *n) n->hwaddr_len = sizeof(n->hwaddr); VirtualTap *tap = (VirtualTap*)(n->state); tap->_mac.copyTo(n->hwaddr, n->hwaddr_len); - // Called from netif code, no need to lock + // Called from core, no need to lock n->hwaddr_len = 6; n->name[0] = '6'; n->name[1] = 'a'+netifCount; @@ -715,6 +625,11 @@ void _lwip_init_interface(void *tapref, const InetAddress &ip) isNewNetif = true; 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); + */ char nmbuf[INET6_ADDRSTRLEN]; static ip4_addr_t ip4, netmask, gw; IP4_ADDR(&gw,127,0,0,1); @@ -723,13 +638,12 @@ void _lwip_init_interface(void *tapref, const InetAddress &ip) LOCK_TCPIP_CORE(); netif_add(n, &ip4, &netmask, &gw, (void*)vtap, _netif_init4, tcpip_input); vtap->netif4 = (void*)n; - _enqueueEvent(ZTS_EVENT_NETIF_UP, (void*)_lwip_prepare_netif_status_msg(n)); UNLOCK_TCPIP_CORE(); snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, nm=%s, tap=%p]",n, - macbuf, ip.toString(ipbuf), ip.netmask().toString(nmbuf), vtap); + //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 (vtap->netif6) { @@ -739,7 +653,11 @@ void _lwip_init_interface(void *tapref, const InetAddress &ip) n = new struct netif; isNewNetif = true; 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; memcpy(&(ip6.addr), ip.rawIpData(), sizeof(ip6.addr)); LOCK_TCPIP_CORE(); @@ -756,13 +674,37 @@ void _lwip_init_interface(void *tapref, const InetAddress &ip) netif_add_ip6_address(n,&ip6,NULL); n->output_ip6 = ethip6_output; UNLOCK_TCPIP_CORE(); - _enqueueEvent(ZTS_EVENT_NETIF_UP, (void*)_lwip_prepare_netif_status_msg(n)); snprintf(macbuf, ZTS_MAC_ADDRSTRLEN, "%02x:%02x:%02x:%02x:%02x:%02x", n->hwaddr[0], n->hwaddr[1], n->hwaddr[2], n->hwaddr[3], n->hwaddr[4], n->hwaddr[5]); - DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n, - macbuf, ip.toString(ipbuf), vtap); + //DEBUG_INFO("initialized netif=%p as [mac=%s, addr=%s, tap=%p]", n, + // macbuf, ip.toString(ipbuf), vtap); } } +void _lwip_remove_address_from_netif(void *tapref, const InetAddress &ip) +{ + if (!tapref) { + return; + } + VirtualTap *vtap = (VirtualTap*)tapref; + struct netif *n = NULL; + /* When true multi-homing is implemented this will need to + be a bit more sophisticated */ + if (ip.isV4()) { + if (vtap->netif4) { + n = (struct netif*)vtap->netif4; + } + } + if (ip.isV6()) { + if (vtap->netif6) { + n = (struct netif*)vtap->netif6; + } + } + if (!n) { + return; + } + _lwip_remove_netif(n); +} + } // namespace ZeroTier diff --git a/src/VirtualTap.hpp b/src/VirtualTap.hpp index 486225c..be4660d 100644 --- a/src/VirtualTap.hpp +++ b/src/VirtualTap.hpp @@ -22,7 +22,7 @@ #include "lwip/err.h" -#define ZTS_LWIP_DRIVER_THREAD_NAME "NetworkStackThread" +#define ZTS_LWIP_DRIVER_THREAD_NAME "ZTNetworkStackThread" #include "MAC.hpp" #include "Phy.hpp" @@ -119,20 +119,6 @@ public: void threadMain() throw(); -#if defined(__MINGW32__) - /* The following is merely to make ZeroTier's OneService happy while building on Windows. - we won't use these in libzt */ - NET_LUID _deviceLuid; - std::string _deviceInstanceId; - - /** - * Returns whether the VirtualTap interface has been initialized - */ - bool isInitialized() const { return _initialized; }; - - inline const NET_LUID &luid() const { return _deviceLuid; } - inline const std::string &instanceId() const { return _deviceInstanceId; } -#endif /** * For moving data onto the ZeroTier virtual wire */ @@ -151,8 +137,6 @@ public: int _networkStatus = 0; - std::vector > routes; - char vtap_full_name[64]; std::vector ips() const; @@ -223,7 +207,6 @@ bool _lwip_is_up(); * @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 once - * @return */ void _lwip_driver_init(); @@ -233,30 +216,21 @@ void _lwip_driver_init(); * @usage This is to be called after it is determined that no further network activity will take place. * The tcpip thread will be stopped, all interfaces will be brought down and all resources will * be deallocated. A full application restart will be required to bring the stack back online. - * @return */ void _lwip_driver_shutdown(); /** * @brief Requests that a netif be brought down and removed. - * - * @return */ void _lwip_remove_netif(void *netif); /** * @brief Initialize and start the DNS client - * - * @usage Called after lwip_driver_init() - * @return */ void _lwip_dns_init(); /** * @brief Starts DHCP timers - * - * @usage lwip_driver_init() - * @return */ void _lwip_start_dhcp(void *netif); @@ -286,13 +260,19 @@ static void _netif_link_callback(struct netif *netif); /** * @brief Set up an interface in the network stack for the VirtualTap. * - * @param * @param tapref Reference to VirtualTap that will be responsible for sending and receiving data * @param ip Virtual IP address for this ZeroTier VirtualTap interface - * @return */ void _lwip_init_interface(void *tapref, const InetAddress &ip); +/** + * @brief Remove an assigned address from an lwIP netif + * + * @param tapref Reference to VirtualTap + * @param ip Virtual IP address to remove from this interface + */ +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 virtual wire here. * @@ -313,7 +293,6 @@ err_t _lwip_eth_tx(struct netif *netif, struct pbuf *p); * @param etherType Protocol type * @param data Pointer to Ethernet frame * @param len Length of Ethernet frame - * @return */ void _lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int etherType, const void *data, unsigned int len); From 087d8a820cf9aa57d7c6a028c1dadecbe4d7fed6 Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Tue, 4 Aug 2020 05:06:59 +0800 Subject: [PATCH 67/78] first commit of node example --- examples/node/auto-top.gypi | 8 + examples/node/auto.gypi | 11 + examples/node/autogypi.json | 6 + examples/node/binding.cc | 424 ++++++++++++++++++++++++++++++++++++ examples/node/binding.gyp | 22 ++ examples/node/package.json | 21 ++ 6 files changed, 492 insertions(+) create mode 100644 examples/node/auto-top.gypi create mode 100644 examples/node/auto.gypi create mode 100644 examples/node/autogypi.json create mode 100644 examples/node/binding.cc create mode 100644 examples/node/binding.gyp create mode 100644 examples/node/package.json diff --git a/examples/node/auto-top.gypi b/examples/node/auto-top.gypi new file mode 100644 index 0000000..7671b8c --- /dev/null +++ b/examples/node/auto-top.gypi @@ -0,0 +1,8 @@ +# Automatically generated file. Edits will be lost. +# Based on: autogypi.json + +{ + "includes": [ + "node_modules/nbind/src/nbind-common.gypi" + ] +} diff --git a/examples/node/auto.gypi b/examples/node/auto.gypi new file mode 100644 index 0000000..49d2b0b --- /dev/null +++ b/examples/node/auto.gypi @@ -0,0 +1,11 @@ +# Automatically generated file. Edits will be lost. +# Based on: autogypi.json + +{ + "include_dirs": [ + "node_modules/nan" + ], + "includes": [ + "node_modules/nbind/src/nbind.gypi" + ] +} diff --git a/examples/node/autogypi.json b/examples/node/autogypi.json new file mode 100644 index 0000000..a62cdbd --- /dev/null +++ b/examples/node/autogypi.json @@ -0,0 +1,6 @@ +{ + "dependencies": [ + "nbind" + ], + "includes": [] +} diff --git a/examples/node/binding.cc b/examples/node/binding.cc new file mode 100644 index 0000000..4dcf728 --- /dev/null +++ b/examples/node/binding.cc @@ -0,0 +1,424 @@ +/** + * libzt binding + */ + +#include +#include +#include +#include +#include + +#include "ZeroTierSockets.h" +#include "nbind/api.h" + +/** + * + * IDENTITIES and AUTHORIZATION: + * + * - Upon the first execution of this code, a new identity will be generated and placed in + * the location given in the first argument to zts_start(path, ...). If you accidentally + * duplicate the identity files and use them simultaneously in a different node instance + * you will experience undefined behavior and it is likely nothing will work. + * + * - You must authorize the node ID provided by the ZTS_EVENT_NODE_ONLINE callback to join + * your network, otherwise nothing will happen. This can be done manually or via + * our web API: https://my.zerotier.com/help/api + * + * - Exceptions to the above rule are: + * 1) Joining a public network (such as "earth") + * 2) Joining an Ad-hoc network, (no controller and therefore requires no authorization.) + * + * + * ESTABLISHING A CONNECTION: + * + * - Creating a standard socket connection generally works the same as it would using + * an ordinary socket interface, however with libzt there is a subtle difference in + * how connections are established which may cause confusion: + * + * The underlying virtual ZT layer creates what are called "transport-triggered links" + * between nodes. That is, links are not established until an attempt to communicate + * with a peer has taken place. The side effect is that the first few packets sent from + * a libzt instance are usually relayed via our free infrastructure and it isn't until a + * root server has passed contact information to both peers that a direct connection will be + * established. Therefore, it is required that multiple connection attempts be undertaken + * when initially communicating with a peer. After a transport-triggered link is + * established libzt will inform you via ZTS_EVENT_PEER_DIRECT for a specific peer ID. No + * action is required on your part for this callback event. + * + * Note: In these initial moments before ZTS_EVENT_PEER_DIRECT has been received for a + * specific peer, traffic may be slow, jittery and there may be high packet loss. + * This will subside within a couple of seconds. + * + * + * ERROR HANDLING: + * + * - libzt's API is actually composed of two categories of functions with slightly + * different error reporting mechanisms. + * + * Category 1: Control functions (zts_start, zts_join, zts_get_peer_status, etc). Errors + * returned by these functions can be any of the following: + * + * ZTS_ERR_OK // No error + * ZTS_ERR_SOCKET // Socket error, see zts_errno + * ZTS_ERR_SERVICE // You probably did something at the wrong time + * ZTS_ERR_ARG // Invalid argument + * ZTS_ERR_NO_RESULT // No result (not necessarily an error) + * ZTS_ERR_GENERAL // Consider filing a bug report + * + * Category 2: Sockets (zts_socket, zts_bind, zts_connect, zts_listen, etc). + * Errors returned by these functions can be the same as the above. With + * the added possibility of zts_errno being set. Much like standard + * errno this will provide a more specific reason for an error's occurrence. + * See ZeroTierSockets.h for values. + * + * + * API COMPATIBILITY WITH HOST OS: + * + * - While the ZeroTier socket interface can coexist with your host OS's own interface in + * the same file with no type and naming conflicts, try not to mix and match host + * OS/libzt structures, functions, or constants. It may look similar and may even work + * some of the time but there enough differences that it will cause headaches. Here + * are a few guidelines: + * + * If you are calling a zts_* function, use the appropriate ZTS_* constants: + * + * 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: + * + * struct zts_sockaddr_in in4; <------ Note the zts_* prefix + * ... + * zts_bind(fd, (struct zts_sockaddr *)&in4, sizeof(struct zts_sockaddr_in)) < 0) + * + */ +class ZeroTier +{ +public: + /** + * @brief Starts the ZeroTier service and notifies user application of events via callback + * + * @param path path directory where configuration files are stored + * @param callback User-specified callback for ZTS_EVENT_* events + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure + */ + static int start(const char *configFilePath, uint16_t servicePort) + { + // Bring up ZeroTier service + + int err = ZTS_ERR_OK; + + if((err = zts_start(configFilePath, &myZeroTierEventCallback, servicePort)) != ZTS_ERR_OK) { + printf("Unable to start service, error = %d.\n", err); + return err; + } + printf("Waiting for node to come online...\n"); + while (!myNode.online) { zts_delay_ms(50); } + + return err; + } + + /** + * @brief Stops the ZeroTier service and brings down all virtual network interfaces + * + * @usage While the ZeroTier service will stop, the stack driver (with associated timers) + * will remain active in case future traffic processing is required. To stop all activity + * and free all resources use zts_free() instead. + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ + static int stop() + { + return zts_stop(); + } + + /** + * @brief Restart the ZeroTier service. + * + * @usage This call will block until the service has been brought offline. Then + * it will return and the user application can then watch for the appropriate + * startup callback events. + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ + static int restart() + { + return zts_restart(); + } + + /** + * @brief Stop all background services, bring down all interfaces, free all resources. After + * calling this function an application restart will be required before the library can be + * used again. + * + * @usage This should be called at the end of your program or when you do not anticipate + * communicating over ZeroTier + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE on failure. + */ + static int free() + { + return zts_free(); + } + + static int join(const char *networkId) + { + // Join ZeroTier network + + uint64_t nwid = strtoull(networkId,NULL,16); // Network ID to join + + int err = ZTS_ERR_OK; + + if((err = zts_join(nwid)) != ZTS_ERR_OK) { + printf("Unable to join network, error = %d.\n", err); + return err; + } + printf("Joining network %llx\n", nwid); + printf("Don't forget to authorize this device in my.zerotier.com or the web API!\n"); + while (!myNode.joinedAtLeastOneNetwork) { zts_delay_ms(50); } + + return err; + } + + static int connectStream(const char *remoteAddr, const int remotePort) + { + return connect(ZTS_AF_INET, ZTS_SOCK_STREAM, 0, remoteAddr, remotePort); + } + + static int connectDgram(const char *remoteAddr, const int remotePort) + { + return connect(ZTS_AF_INET, ZTS_SOCK_DGRAM, 0, remoteAddr, remotePort); + } + + static int connectRaw(const char *remoteAddr, const int remotePort, const int protocol) + { + return connect(ZTS_AF_INET, ZTS_SOCK_RAW, protocol, remoteAddr, remotePort); + } + + static int connectStream6(const char *remoteAddr, const int remotePort) + { + return connect(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0, remoteAddr, remotePort); + } + + static int connectDgram6(const char *remoteAddr, const int remotePort) + { + return connect(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0, remoteAddr, remotePort); + } + + static int connectRaw6(const char *remoteAddr, const int remotePort, const int protocol) + { + return connect(ZTS_AF_INET6, ZTS_SOCK_RAW, protocol, remoteAddr, remotePort); + } + + static int connect(const int socket_family, const int socket_type, const int protocol, const char *remoteAddr, const int remotePort) + { + const struct zts_sockaddr *addr; + zts_socklen_t addrlen; + + if (socket_family == ZTS_AF_INET) { + struct zts_sockaddr_in in4 = sockaddr_in(remoteAddr, remotePort); + addr = (const struct zts_sockaddr *)&in4; + addrlen = sizeof(in4); + } else { + printf("IPv6 NOT IMPLEMENTED.\n"); + return -1; + } + + int fd; + if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); + return -1; + } + + // Retries are often required since ZT uses transport-triggered links (explained above) + int err = ZTS_ERR_OK; + for (;;) { + printf("Connecting to remote host...\n"); + if ((err = zts_connect(fd, addr, addrlen)) < 0) { + printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", + fd, err, zts_errno); + zts_close(fd); + printf("Creating socket...\n"); + if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); + return -1; + } + zts_delay_ms(250); + } + else { + printf("Connected.\n"); + break; + } + } + + return fd; + } + + static ssize_t read(int fd, nbind::Buffer buf) + { + return zts_read(fd, buf.data(), buf.length()); + } + + static ssize_t write(int fd, nbind::Buffer buf) + { + return zts_write(fd, buf.data(), buf.length()); + } + + static ssize_t recv(int fd, nbind::Buffer buf, int flags) + { + return zts_recv(fd, buf.data(), buf.length(), flags); + } + + static ssize_t send(int fd, nbind::Buffer buf, int flags) + { + return zts_send(fd, buf.data(), buf.length(), flags); + } + + static int close(int fd) + { + return zts_close(fd); + } + + static zts_sockaddr_in sockaddr_in(const char *remoteAddr, const int remotePort) + { + struct zts_sockaddr_in in4; + in4.sin_port = zts_htons(remotePort); + #if defined(_WIN32) + in4.sin_addr.S_addr = zts_inet_addr(remoteAddr); + #else + in4.sin_addr.s_addr = zts_inet_addr(remoteAddr); + #endif + in4.sin_family = ZTS_AF_INET; + return in4; + } + +private: + static struct Node + { + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc + } myNode; + + /* Callback handler, you should return control from this function as quickly as you can + to ensure timely receipt of future events. You should not call libzt API functions from + this function unless it's something trivial like zts_inet_ntop() or similar that has + no state-change implications. */ + static void myZeroTierEventCallback(void *msgPtr) + { + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + myNode.id = msg->node->address; + myNode.online = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + myNode.online = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == 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->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } + } + } +}; + +#include "nbind/nbind.h" + +NBIND_CLASS(ZeroTier) { + method(start); + method(join); + method(connectStream); + method(connectDgram); + method(connectRaw); + method(connectStream6); + method(connectDgram6); + method(connectRaw6); + method(connect); + method(read); + method(write); + method(recv); + method(send); + method(restart); + method(stop); + method(free); +} diff --git a/examples/node/binding.gyp b/examples/node/binding.gyp new file mode 100644 index 0000000..7b5eb69 --- /dev/null +++ b/examples/node/binding.gyp @@ -0,0 +1,22 @@ +{ + "targets": [ + { + "include_dirs": [ + "libzt/lib/debug/linux-x86_64", + "libzt/include" + ], + "libraries": [ + " Date: Tue, 4 Aug 2020 16:47:28 +0800 Subject: [PATCH 68/78] 1st working version of node example --- examples/node/.gitignore | 2 + examples/node/binding.cc | 338 +++++++++++++++++++++---------------- examples/node/binding.gyp | 18 +- examples/node/libzt.js | 41 +++++ examples/node/package.json | 3 +- 5 files changed, 254 insertions(+), 148 deletions(-) create mode 100644 examples/node/.gitignore create mode 100644 examples/node/libzt.js diff --git a/examples/node/.gitignore b/examples/node/.gitignore new file mode 100644 index 0000000..85342f4 --- /dev/null +++ b/examples/node/.gitignore @@ -0,0 +1,2 @@ +/.zerotier +/libzt \ No newline at end of file diff --git a/examples/node/binding.cc b/examples/node/binding.cc index 4dcf728..cf0d2f9 100644 --- a/examples/node/binding.cc +++ b/examples/node/binding.cc @@ -11,6 +11,122 @@ #include "ZeroTierSockets.h" #include "nbind/api.h" +struct Node +{ + Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} + + bool online; + bool joinedAtLeastOneNetwork; + uint64_t id; + // etc + + bool getOnline() { return online; } + bool getJoinedAtLeastOneNetwork() { return joinedAtLeastOneNetwork; } + uint64_t getId() { return id; } +} myNode; + +/* Callback handler, you should return control from this function as quickly as you can +to ensure timely receipt of future events. You should not call libzt API functions from +this function unless it's something trivial like zts_inet_ntop() or similar that has +no state-change implications. */ +void myZeroTierEventCallback(void *msgPtr) +{ + struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; + + // Node events + if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { + printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); + myNode.id = msg->node->address; + myNode.online = true; + } + if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { + printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); + myNode.online = false; + } + if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { + printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); + } + + // Virtual network events + if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { + printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", + msg->network->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { + printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); + } + if (msg->eventCode == 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->nwid); + } + if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { + printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", + msg->network->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == 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->nwid); + myNode.joinedAtLeastOneNetwork = true; + } + if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { + printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); + } + + // Address events + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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 --- This node's virtual address on network %llx is %s\n", + msg->addr->nwid, ipstr); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { + char ipstr[ZTS_INET_ADDRSTRLEN]; + struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { + char ipstr[ZTS_INET6_ADDRSTRLEN]; + 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", + ipstr, msg->addr->nwid); + } + // Peer events + if (msg->peer) { + if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { + /* Safe to ignore, these are our roots. They orchestrate the P2P connection. + You might also see other unknown peers, these are our network controllers. */ + return; + } + if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { + printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { + printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { + printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", + msg->peer->address); + } + if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { + printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", + msg->peer->address); + } + } +} + /** * * IDENTITIES and AUTHORIZATION: @@ -98,17 +214,17 @@ public: /** * @brief Starts the ZeroTier service and notifies user application of events via callback * - * @param path path directory where configuration files are stored - * @param callback User-specified callback for ZTS_EVENT_* events + * @param configPath path directory where configuration files are stored + * @param servicePort proit which ZeroTier service will listen on * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure */ - static int start(const char *configFilePath, uint16_t servicePort) + static int start(const char *configPath, uint16_t servicePort) { // Bring up ZeroTier service int err = ZTS_ERR_OK; - if((err = zts_start(configFilePath, &myZeroTierEventCallback, servicePort)) != ZTS_ERR_OK) { + if((err = zts_start(configPath, &myZeroTierEventCallback, servicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d.\n", err); return err; } @@ -158,6 +274,12 @@ public: return zts_free(); } + /** + * @brief Join a network + * + * @param networkId A 16-digit hexadecimal virtual network ID + * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure. + */ static int join(const char *networkId) { // Join ZeroTier network @@ -207,20 +329,25 @@ public: return connect(ZTS_AF_INET6, ZTS_SOCK_RAW, protocol, remoteAddr, remotePort); } + /** + * @brief Connect a socket to a remote host (sets zts_errno) + * + * @param socket_family Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param socket_type Type of socket (ZTS_SOCK_STREAM, ZTS_SOCK_DGRAM, ZTS_SOCK_RAW) + * @param protocol Protocols supported on this socket + * @param remoteAddr Remote Address to connect to + * @param remotePort Remote Port to connect to + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ static int connect(const int socket_family, const int socket_type, const int protocol, const char *remoteAddr, const int remotePort) { - const struct zts_sockaddr *addr; - zts_socklen_t addrlen; - - if (socket_family == ZTS_AF_INET) { - struct zts_sockaddr_in in4 = sockaddr_in(remoteAddr, remotePort); - addr = (const struct zts_sockaddr *)&in4; - addrlen = sizeof(in4); - } else { + if (socket_family == ZTS_AF_INET6) { printf("IPv6 NOT IMPLEMENTED.\n"); return -1; } + struct zts_sockaddr_in in4 = sockaddr_in(remoteAddr, remotePort); + int fd; if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); @@ -231,11 +358,11 @@ public: int err = ZTS_ERR_OK; for (;;) { printf("Connecting to remote host...\n"); - if ((err = zts_connect(fd, addr, addrlen)) < 0) { + if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", fd, err, zts_errno); zts_close(fd); - printf("Creating socket...\n"); + // printf("Creating socket...\n"); if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); return -1; @@ -248,9 +375,36 @@ public: } } + // Set non-blocking mode + fcntl(fd, ZTS_F_SETFL, ZTS_O_NONBLOCK); + return fd; } + static int fcntlSetBlocking(int fd, bool isBlocking) + { + int flags = fcntl(fd, ZTS_F_GETFL, 0); + if (isBlocking) { + flags &= ~ZTS_O_NONBLOCK; + } else { + flags &= ZTS_O_NONBLOCK; + } + return fcntl(fd, ZTS_F_SETFL, flags); + } + + /** + * @brief Issue file control commands on a socket + * + * @param fd File descriptor + * @param cmd + * @param flags + * @return + */ + static int fcntl(int fd, int cmd, int flags) + { + return zts_fcntl(fd, cmd, flags); + } + static ssize_t read(int fd, nbind::Buffer buf) { return zts_read(fd, buf.data(), buf.length()); @@ -289,136 +443,36 @@ public: return in4; } -private: - static struct Node - { - Node() : online(false), joinedAtLeastOneNetwork(false), id(0) {} - bool online; - bool joinedAtLeastOneNetwork; - uint64_t id; - // etc - } myNode; - - /* Callback handler, you should return control from this function as quickly as you can - to ensure timely receipt of future events. You should not call libzt API functions from - this function unless it's something trivial like zts_inet_ntop() or similar that has - no state-change implications. */ - static void myZeroTierEventCallback(void *msgPtr) - { - struct zts_callback_msg *msg = (struct zts_callback_msg *)msgPtr; - - // Node events - if (msg->eventCode == ZTS_EVENT_NODE_ONLINE) { - printf("ZTS_EVENT_NODE_ONLINE --- This node's ID is %llx\n", msg->node->address); - myNode.id = msg->node->address; - myNode.online = true; - } - if (msg->eventCode == ZTS_EVENT_NODE_OFFLINE) { - printf("ZTS_EVENT_NODE_OFFLINE --- Check your physical Internet connection, router, firewall, etc. What ports are you blocking?\n"); - myNode.online = false; - } - if (msg->eventCode == ZTS_EVENT_NODE_NORMAL_TERMINATION) { - printf("ZTS_EVENT_NODE_NORMAL_TERMINATION\n"); - } - - // Virtual network events - if (msg->eventCode == ZTS_EVENT_NETWORK_NOT_FOUND) { - printf("ZTS_EVENT_NETWORK_NOT_FOUND --- Are you sure %llx is a valid network?\n", - msg->network->nwid); - } - if (msg->eventCode == ZTS_EVENT_NETWORK_REQ_CONFIG) { - printf("ZTS_EVENT_NETWORK_REQ_CONFIG --- Requesting config for network %llx, please wait a few seconds...\n", msg->network->nwid); - } - if (msg->eventCode == 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->nwid); - } - if (msg->eventCode == ZTS_EVENT_NETWORK_READY_IP4) { - printf("ZTS_EVENT_NETWORK_READY_IP4 --- Network config received. IPv4 traffic can now be sent over network %llx\n", - msg->network->nwid); - myNode.joinedAtLeastOneNetwork = true; - } - if (msg->eventCode == 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->nwid); - myNode.joinedAtLeastOneNetwork = true; - } - if (msg->eventCode == ZTS_EVENT_NETWORK_DOWN) { - printf("ZTS_EVENT_NETWORK_DOWN --- %llx\n", msg->network->nwid); - } - - // Address events - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP4) { - char ipstr[ZTS_INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_NEW_IP4 --- This node's virtual address on network %llx is %s\n", - msg->addr->nwid, ipstr); - } - if (msg->eventCode == ZTS_EVENT_ADDR_ADDED_IP6) { - char ipstr[ZTS_INET6_ADDRSTRLEN]; - 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 --- This node's virtual address on network %llx is %s\n", - msg->addr->nwid, ipstr); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP4) { - char ipstr[ZTS_INET_ADDRSTRLEN]; - struct zts_sockaddr_in *in4 = (struct zts_sockaddr_in*)&(msg->addr->addr); - zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); - printf("ZTS_EVENT_ADDR_REMOVED_IP4 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg->addr->nwid); - } - if (msg->eventCode == ZTS_EVENT_ADDR_REMOVED_IP6) { - char ipstr[ZTS_INET6_ADDRSTRLEN]; - 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_REMOVED_IP6 --- The virtual address %s for this node on network %llx has been removed.\n", - ipstr, msg->addr->nwid); - } - // Peer events - if (msg->peer) { - if (msg->peer->role == ZTS_PEER_ROLE_PLANET) { - /* Safe to ignore, these are our roots. They orchestrate the P2P connection. - You might also see other unknown peers, these are our network controllers. */ - return; - } - if (msg->eventCode == ZTS_EVENT_PEER_DIRECT) { - printf("ZTS_EVENT_PEER_DIRECT --- A direct path is known for node=%llx\n", - msg->peer->address); - } - if (msg->eventCode == ZTS_EVENT_PEER_RELAY) { - printf("ZTS_EVENT_PEER_RELAY --- No direct path to node=%llx\n", msg->peer->address); - } - if (msg->eventCode == ZTS_EVENT_PEER_PATH_DISCOVERED) { - printf("ZTS_EVENT_PEER_PATH_DISCOVERED --- A new direct path was discovered for node=%llx\n", - msg->peer->address); - } - if (msg->eventCode == ZTS_EVENT_PEER_PATH_DEAD) { - printf("ZTS_EVENT_PEER_PATH_DEAD --- A direct path has died for node=%llx\n", - msg->peer->address); - } - } - } + static Node getMyNode() { return myNode; } }; #include "nbind/nbind.h" -NBIND_CLASS(ZeroTier) { - method(start); - method(join); - method(connectStream); - method(connectDgram); - method(connectRaw); - method(connectStream6); - method(connectDgram6); - method(connectRaw6); - method(connect); - method(read); - method(write); - method(recv); - method(send); - method(restart); - method(stop); - method(free); +NBIND_CLASS(Node) { + getter(getOnline); + getter(getJoinedAtLeastOneNetwork); + getter(getId); +} + +NBIND_CLASS(ZeroTier) { + method(start); + method(join); + method(connectStream); + method(connectDgram); + method(connectRaw); + method(connectStream6); + method(connectDgram6); + method(connectRaw6); + method(connect); + method(read); + method(write); + method(recv); + method(send); + method(fcntlSetBlocking); + method(fcntl); + method(close); + method(restart); + method(stop); + method(free); + method(getMyNode); } diff --git a/examples/node/binding.gyp b/examples/node/binding.gyp index 7b5eb69..5aa3e1d 100644 --- a/examples/node/binding.gyp +++ b/examples/node/binding.gyp @@ -2,17 +2,25 @@ "targets": [ { "include_dirs": [ - "libzt/lib/debug/linux-x86_64", - "libzt/include" - ], - "libraries": [ - " process.stderr.write('.'), 100) + +// Receive some data +const _read = () => { + const buf = Buffer.alloc(32) + let bytes = -1 + do { + bytes = ZeroTier.recv(fd, buf, 0) + if (bytes > 0) { process.stdout.write(buf.toString('utf8')) } + } while (bytes > 0); + + if (!ZeroTier.getMyNode().online || buf.toString('utf8').includes("exit")) { + // Close the socket + ZeroTier.close(fd) + // Stop ZeroTier service + ZeroTier.stop() + // Clear the interval + clearInterval(heartbeat) + } else { + setTimeout(_read, 500) + } +} +_read() + diff --git a/examples/node/package.json b/examples/node/package.json index e64dc76..74aa392 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "preinstall": "mkdir -p libzt; cd libzt; ln -sf ../../../include include; ln -sf ../../../lib lib", + "preinstall": "mkdir -p libzt; cd libzt; ln -sf ../../../include; ln -sf ../../../lib", "autogypi": "autogypi", "node-gyp": "node-gyp", "ndts": "ndts", @@ -14,6 +14,7 @@ "author": "", "license": "MIT", "dependencies": { + "@mcesystems/nbind": "^0.3.18", "autogypi": "^0.2.2", "nbind": "github:charto/nbind", "node-gyp": "^7.0.0" From 80861c11268016c5de05e1a891800fbd2519676e Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Tue, 4 Aug 2020 22:02:56 +0800 Subject: [PATCH 69/78] add libzt.createConnection() --- examples/node/binding.cc | 12 + examples/node/libzt.js | 371 ++++++++++++++- examples/node/package-lock.json | 788 ++++++++++++++++++++++++++++++++ examples/node/package.json | 1 + examples/node/test.js | 24 + 5 files changed, 1171 insertions(+), 25 deletions(-) create mode 100644 examples/node/package-lock.json create mode 100644 examples/node/test.js diff --git a/examples/node/binding.cc b/examples/node/binding.cc index cf0d2f9..1cf58b9 100644 --- a/examples/node/binding.cc +++ b/examples/node/binding.cc @@ -415,6 +415,17 @@ public: return zts_write(fd, buf.data(), buf.length()); } + static ssize_t writev(int fd, std::vector bufs) + { + std::size_t size = bufs.size(); + zts_iovec iov[size]; + for (std::size_t i = 0; i != size; ++i) { + iov[i].iov_base = bufs[i].data(); + iov[i].iov_len = bufs[i].length(); + } + return zts_writev(fd, iov, bufs.size()); + } + static ssize_t recv(int fd, nbind::Buffer buf, int flags) { return zts_recv(fd, buf.data(), buf.length(), flags); @@ -466,6 +477,7 @@ NBIND_CLASS(ZeroTier) { method(connect); method(read); method(write); + method(writev); method(recv); method(send); method(fcntlSetBlocking); diff --git a/examples/node/libzt.js b/examples/node/libzt.js index 62e7814..4e9744b 100644 --- a/examples/node/libzt.js +++ b/examples/node/libzt.js @@ -1,41 +1,362 @@ -var nbind = require('@mcesystems/nbind') -var ZeroTier = nbind.init().lib.ZeroTier +'use strict'; -// Start ZeroTier service -ZeroTier.start(".zerotier", 9994); +const net = require('net'); +const nbind = require('@mcesystems/nbind') +const ZeroTier = nbind.init().lib.ZeroTier -// Join virtual network -ZeroTier.join("8056c2e21c000001"); -// Open the socket -let fd = ZeroTier.connectStream("29.49.7.203", 4444); +/* + * EXAMPLE USAGE + * Usage: `nc -lv 4444` + */ -// Send some data -ZeroTier.send(fd, Buffer.from("Name?\n", 'utf8'), 0) +function example() { + // Start ZeroTier service + ZeroTier.start(".zerotier", 9994); -// Set blocking read mode -// ZeroTier.fcntlSetBlocking(fd, true); -let heartbeat = setInterval(() => process.stderr.write('.'), 100) + // Join virtual network + ZeroTier.join("8056c2e21c000001"); -// Receive some data -const _read = () => { + // Open the socket + let fd = ZeroTier.connectStream("29.49.7.203", 4444); + + // Send some data + ZeroTier.send(fd, Buffer.from("Name?\n", 'utf8'), 0) + + // Set blocking read mode + // ZeroTier.fcntlSetBlocking(fd, true); + let heartbeat = setInterval(() => process.stderr.write('.'), 100) + + // Receive some data + const _read = () => { const buf = Buffer.alloc(32) let bytes = -1 do { - bytes = ZeroTier.recv(fd, buf, 0) - if (bytes > 0) { process.stdout.write(buf.toString('utf8')) } + bytes = ZeroTier.recv(fd, buf, 0) + if (bytes > 0) { process.stdout.write(buf.toString('utf8')) } } while (bytes > 0); if (!ZeroTier.getMyNode().online || buf.toString('utf8').includes("exit")) { - // Close the socket - ZeroTier.close(fd) - // Stop ZeroTier service - ZeroTier.stop() - // Clear the interval - clearInterval(heartbeat) + // Close the socket + ZeroTier.close(fd) + // Stop ZeroTier service + ZeroTier.stop() + // Clear the interval + clearInterval(heartbeat) } else { - setTimeout(_read, 500) + setTimeout(_read, 500) } + } + _read() } -_read() + +// Target API: +// +// let s = net.connect({port: 80, host: 'google.com'}, function() { +// ... +// }); +// +// There are various forms: +// +// connect(options, [cb]) +// connect(port, [host], [cb]) +// connect(path, [cb]); +// +function connect(...args) { + const normalized = net._normalizeArgs(args); + const options = normalized[0]; + // debug('createConnection', normalized); + const socket = new Socket(options); + + if (options.timeout) { + socket.setTimeout(options.timeout); + } + + return socket.connect(normalized); +} + +/* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L1107 + */ +function afterConnect(status, self, req, readable, writable) { + // const self = handle[owner_symbol]; + + // Callback may come after call to destroy + if (self.destroyed) { + return; + } + + // debug('afterConnect'); + + // assert(self.connecting); + self.connecting = false; + self._sockname = null; + + if (status === 0) { + self.readable = readable; + if (!self._writableState.ended) + self.writable = writable; + self._unrefTimer(); + + self.emit('connect'); + self.emit('ready'); + + // Start the first read, or get an immediate EOF. + // this doesn't actually consume any bytes, because len=0. + if (readable && !self.isPaused()) + self.read(0); + + } else { + self.connecting = false; + let details; + if (req.localAddress && req.localPort) { + details = req.localAddress + ':' + req.localPort; + } + const ex = new Error(status, + 'connect', + req.address, + req.port, + details); + if (details) { + ex.localAddress = req.localAddress; + ex.localPort = req.localPort; + } + self.destroy(ex); + } +} + +function writeGeneric(self, chunk, encoding, callback) { + const buf = (!self.decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk + + let bytes + const err = ZeroTier.send(self._fd, buf, 0) + switch (err) { + case -1: + callback(new Error("ZeroTier Socket error")) + break + case -2: + callback(new Error("ZeroTier Service error")) + break + case -3: + callback(new Error("ZeroTier Invalid argument")) + break + default: + bytes = err + callback(null) + } + + return { + async: true, + bytes: bytes, + } +} + +function writevGeneric(self, chunks, callback) { + const bufs = chunks.map(({ chunk, encoding }) => (!self.decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk) + + let bytes + const err = ZeroTier.writev(self._fd, bufs) + switch (err) { + case -1: + callback(new Error("ZeroTier Socket error")) + break + case -2: + callback(new Error("ZeroTier Service error")) + break + case -3: + callback(new Error("ZeroTier Invalid argument")) + break + default: + bytes = err + callback(null) + } + + return { + async: true, + bytes: bytes, + } +} + +class Socket extends net.Socket { + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L929 + */ + connect(...args) { + let normalized; + // If passed an array, it's treated as an array of arguments that have + // already been normalized (so we don't normalize more than once). This has + // been solved before in https://github.com/nodejs/node/pull/12342, but was + // reverted as it had unintended side effects. + if (Array.isArray(args[0])) { + normalized = args[0]; + } else { + normalized = net._normalizeArgs(args); + } + const options = normalized[0]; + const cb = normalized[1]; + + // if (this.write !== net.Socket.prototype.write) + // this.write = net.Socket.prototype.write; + + if (this.destroyed) { + this._handle = null; + this._peername = null; + this._sockname = null; + } + + // const { path } = options; + // const pipe = !!path; + // debug('pipe', pipe, path); + + // if (!this._handle) { + // this._handle = pipe ? + // new Pipe(PipeConstants.SOCKET) : + // new TCP(TCPConstants.SOCKET); + // initSocketHandle(this); + // } + + if (cb !== null) { + this.once('connect', cb); + } + + this._unrefTimer(); + + this.connecting = true; + this.writable = true; + + // if (pipe) { + // validateString(path, 'options.path'); + // defaultTriggerAsyncIdScope( + // this[async_id_symbol], internalConnect, this, path + // ); + // } else { + // lookupAndConnect(this, options); + // } + + const { host, port } = options; + // If host is an IP, skip performing a lookup + const addressType = net.isIP(host); + if (addressType) { + this._fd = ZeroTier.connectStream(host, port); + afterConnect(0, this, {}, true, true); + } else { + throw new Error("DNS LOOKUP NOT IMPLEMENTED"); + } + + return this; + } + + /* + * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_readable_read_size_1 + */ + _read(size) { + // debug('_read'); + + if (this.connecting) { + // debug('_read wait for connection'); + this.once('connect', () => this._read(size)); + return + } + + if (!this.readChunk || this.readChunk.length < size) { + this.readChunk = Buffer.alloc(size) + } + + let bytes = -1 + let moreData = true + do { + bytes = ZeroTier.recv(this._fd, this.readChunk, 0) + switch (bytes) { + case -2: + throw new Error("ZeroTier Service error") + case -3: + throw new Error("ZeroTier Invalid argument") + default: + if (bytes > 0) { + // this.bytesRead += bytes + moreData = this.push(this.readChunk) + } + } + } while (bytes > 0 && moreData) + + if (moreData) { setTimeout(() => this._read(size), 500) } + } + + /* + * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_writable_writev_chunks_callback + */ + _writev(chunks, cb) { + this._writeGeneric(true, chunks, '', cb); + } + + /* + * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_writable_write_chunk_encoding_callback_1 + */ + _write(data, encoding, cb) { + this._writeGeneric(false, data, encoding, cb); + } + + /* + * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_writable_final_callback + */ + _final(callback) { + const err = ZeroTier.close(this._fd) + + switch (err) { + case -1: + return callback(new Error("ZeroTier Socket error")) + break + case -2: + return callback(new Error("ZeroTier Service error")) + break + default: + return super._final(callback) + } + } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L760 + */ + _writeGeneric(writev, data, encoding, cb) { + // If we are still connecting, then buffer this for later. + // The Writable logic will buffer up any more writes while + // waiting for this one to be done. + if (this.connecting) { + this._pendingData = data; + this._pendingEncoding = encoding; + this.once('connect', function connect() { + this._writeGeneric(writev, data, encoding, cb); + }); + return; + } + this._pendingData = null; + this._pendingEncoding = ''; + + // if (!this._handle) { + // cb(new ERR_SOCKET_CLOSED()); + // return false; + // } + + this._unrefTimer(); + + let req; + if (writev) + req = writevGeneric(this, data, cb); + else + req = writeGeneric(this, data, encoding, cb); + if (req.async) { + // this[kLastWriteQueueSize] = req.bytes; + } + } +} + +module.exports = { + example, + start: ZeroTier.start, + join: ZeroTier.join, + connect, + createConnection: connect, + Socket, + Stream: Socket, // Legacy naming +}; diff --git a/examples/node/package-lock.json b/examples/node/package-lock.json new file mode 100644 index 0000000..4a2e074 --- /dev/null +++ b/examples/node/package-lock.json @@ -0,0 +1,788 @@ +{ + "name": "libzt", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mcesystems/nbind": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@mcesystems/nbind/-/nbind-0.3.18.tgz", + "integrity": "sha512-gZFv881rT/nTNNl92K97DqbFVECiniEHg4ABle7WFKklQSCge7ILY58otnKvCgoqrrS/9Mv//mTf+2k3MPSGXA==", + "requires": { + "emscripten-library-decorator": "~0.2.2", + "mkdirp": "~0.5.1", + "nan": "^2.9.2" + } + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "autogypi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/autogypi/-/autogypi-0.2.2.tgz", + "integrity": "sha1-JYurX3hXdVsJvqxqZB/qEw/0Yi0=", + "requires": { + "bluebird": "^3.4.0", + "commander": "~2.9.0", + "resolve": "~1.1.7" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": ">= 1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emscripten-library-decorator": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/emscripten-library-decorator/-/emscripten-library-decorator-0.2.2.tgz", + "integrity": "sha1-0DXwI+KoTGgwXMhCze6jjmdoPEA=" + }, + "env-paths": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz", + "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz", + "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.0.tgz", + "integrity": "sha512-EzTZN/fjSvifSX0SlqUERCN39o6T40AMarPbv0MrarSFtIITCBh7bi+dU8nxGFHuqs9jdIAeoYoKuQAAASsPPA==", + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, + "nbind": { + "version": "github:charto/nbind#fe3abe05462d1b7559e0933e7f83802e8f05af27", + "from": "github:charto/nbind", + "requires": { + "emscripten-library-decorator": "~0.2.2", + "mkdirp": "~0.5.1", + "nan": "^2.9.2" + } + }, + "node-gyp": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-7.0.0.tgz", + "integrity": "sha512-ZW34qA3CJSPKDz2SJBHKRvyNQN0yWO5EGKKksJc+jElu9VA468gwJTyTArC1iOXU7rN3Wtfg/CMt/dBAOFIjvg==", + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^4.0.3", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^2.6.3", + "semver": "^7.3.2", + "tar": "^6.0.1", + "which": "^2.0.2" + } + }, + "nopt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", + "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "tar": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.2.tgz", + "integrity": "sha512-Glo3jkRtPcvpDlAs/0+hozav78yoXKFr+c4wgw62NNMO3oo4AaJdCo21Uu7lcwr55h39W2XD1LMERc64wtbItg==", + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.0", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + } + } + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/examples/node/package.json b/examples/node/package.json index 74aa392..05bf3b9 100644 --- a/examples/node/package.json +++ b/examples/node/package.json @@ -5,6 +5,7 @@ "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "start": "node test.js", "preinstall": "mkdir -p libzt; cd libzt; ln -sf ../../../include; ln -sf ../../../lib", "autogypi": "autogypi", "node-gyp": "node-gyp", diff --git a/examples/node/test.js b/examples/node/test.js new file mode 100644 index 0000000..2aeb3cb --- /dev/null +++ b/examples/node/test.js @@ -0,0 +1,24 @@ +'use strict' + +const libzt = require('./libzt') + +// libzt.example() + +libzt.start(".zerotier", 9994) + +libzt.join("8056c2e21c000001") + +// Usage: `nc -lv 4444` +let client = libzt.createConnection({ port: 4444, host: '29.49.7.203' }, () => { + // 'connect' listener. + console.log('connected to server!'); + // client.write('world!\r\n'); +}); +client.write("Name?\n", 'utf8'); +client.on('data', (data) => { + console.log(data.toString()); + client.end(); +}); +client.on('end', () => { + console.log('disconnected from server'); +}); From fcc8dcfd6cc4e11793ce83d11dfe8f92bef16088 Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Wed, 5 Aug 2020 17:25:55 +0800 Subject: [PATCH 70/78] small bug fix --- examples/node/libzt.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/node/libzt.js b/examples/node/libzt.js index 4e9744b..0908765 100644 --- a/examples/node/libzt.js +++ b/examples/node/libzt.js @@ -127,7 +127,8 @@ function afterConnect(status, self, req, readable, writable) { } function writeGeneric(self, chunk, encoding, callback) { - const buf = (!self.decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk + const decodeStrings = self._writableState && self._writableState.decodeStrings + const buf = (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk let bytes const err = ZeroTier.send(self._fd, buf, 0) @@ -153,7 +154,8 @@ function writeGeneric(self, chunk, encoding, callback) { } function writevGeneric(self, chunks, callback) { - const bufs = chunks.map(({ chunk, encoding }) => (!self.decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk) + const decodeStrings = self._writableState && self._writableState.decodeStrings + const bufs = chunks.map(({ chunk, encoding }) => (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk) let bytes const err = ZeroTier.writev(self._fd, bufs) @@ -359,4 +361,7 @@ module.exports = { createConnection: connect, Socket, Stream: Socket, // Legacy naming + restart: ZeroTier.restart, + stop: ZeroTier.stop, + free: ZeroTier.free, }; From 58c4cb9e7da63ac4d31674133075cfe3b8a571e9 Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Wed, 5 Aug 2020 17:42:31 +0800 Subject: [PATCH 71/78] make libzt.a position_independent_code --- CMakeLists.txt | 11 ++++++----- examples/node/binding.gyp | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e23698..0884505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,13 +426,14 @@ set_target_properties (ztcore PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) # libzt.a -add_library (${STATIC_LIB_NAME} STATIC $ -$ -$ -$ -$ ${libztSrcGlob}) +add_library (${STATIC_LIB_NAME} STATIC $ +$ +$ +$ +$ ${libztSrcGlob}) set_target_properties (${STATIC_LIB_NAME} PROPERTIES OUTPUT_NAME zt + POSITION_INDEPENDENT_CODE ON LIBRARY_OUTPUT_DIRECTORY ${INTERMEDIATE_LIBRARY_OUTPUT_PATH}) set_target_properties (${STATIC_LIB_NAME} PROPERTIES COMPILE_FLAGS "${ZT_FLAGS}") if (BUILDING_WIN) diff --git a/examples/node/binding.gyp b/examples/node/binding.gyp index 5aa3e1d..a53ef2a 100644 --- a/examples/node/binding.gyp +++ b/examples/node/binding.gyp @@ -2,7 +2,6 @@ "targets": [ { "include_dirs": [ - "libzt/lib/release/linux-x86_64", "libzt/include", ], "includes": [ @@ -13,7 +12,7 @@ ], "conditions":[ ["OS=='linux' and target_arch=='x64'", { - "libraries": [ "<(module_root_dir)/libzt/lib/release/linux-x86_64/libzt.so" ] + "libraries": [ "<(module_root_dir)/libzt/lib/release/linux-x86_64/libzt.a" ] }], ["OS=='mac' and target_arch=='x64'", { "libraries": [ "<(module_root_dir)/libzt/lib/release/macos-x86_64/libzt.a" ] From 42ab8c2aeeb85039c41ffc479b66daef0ff717dc Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Thu, 6 Aug 2020 11:48:36 +0800 Subject: [PATCH 72/78] major rewrite to handle schemantics --- examples/node/binding.cc | 440 ++++++++++++---- examples/node/libzt.js | 879 +++++++++++++++++++++++--------- examples/node/stream_commons.js | 242 +++++++++ examples/node/test.js | 13 +- 4 files changed, 1234 insertions(+), 340 deletions(-) create mode 100644 examples/node/stream_commons.js diff --git a/examples/node/binding.cc b/examples/node/binding.cc index 1cf58b9..f1d8b0e 100644 --- a/examples/node/binding.cc +++ b/examples/node/binding.cc @@ -127,6 +127,19 @@ void myZeroTierEventCallback(void *msgPtr) } } +zts_sockaddr_in sockaddr_in(const char *remoteAddr, const int remotePort) +{ + struct zts_sockaddr_in in4; + in4.sin_port = zts_htons(remotePort); +#if defined(_WIN32) + in4.sin_addr.S_addr = zts_inet_addr(remoteAddr); +#else + in4.sin_addr.s_addr = zts_inet_addr(remoteAddr); +#endif + in4.sin_family = ZTS_AF_INET; + return in4; +} + /** * * IDENTITIES and AUTHORIZATION: @@ -211,6 +224,8 @@ void myZeroTierEventCallback(void *msgPtr) class ZeroTier { public: + static Node getMyNode() { return myNode; } + /** * @brief Starts the ZeroTier service and notifies user application of events via callback * @@ -299,89 +314,220 @@ public: return err; } - static int connectStream(const char *remoteAddr, const int remotePort) + static int openStream() { - return connect(ZTS_AF_INET, ZTS_SOCK_STREAM, 0, remoteAddr, remotePort); + return open(ZTS_AF_INET, ZTS_SOCK_STREAM, 0); } - static int connectDgram(const char *remoteAddr, const int remotePort) + static int openDgram() { - return connect(ZTS_AF_INET, ZTS_SOCK_DGRAM, 0, remoteAddr, remotePort); + return open(ZTS_AF_INET, ZTS_SOCK_DGRAM, 0); } - static int connectRaw(const char *remoteAddr, const int remotePort, const int protocol) + static int openRaw(const int protocol) { - return connect(ZTS_AF_INET, ZTS_SOCK_RAW, protocol, remoteAddr, remotePort); + return open(ZTS_AF_INET, ZTS_SOCK_RAW, protocol); } - static int connectStream6(const char *remoteAddr, const int remotePort) + static int openStream6() { - return connect(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0, remoteAddr, remotePort); + return open(ZTS_AF_INET6, ZTS_SOCK_STREAM, 0); } - static int connectDgram6(const char *remoteAddr, const int remotePort) + static int openDgram6() { - return connect(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0, remoteAddr, remotePort); + return open(ZTS_AF_INET6, ZTS_SOCK_DGRAM, 0); } - static int connectRaw6(const char *remoteAddr, const int remotePort, const int protocol) + static int openRaw6(const int protocol) { - return connect(ZTS_AF_INET6, ZTS_SOCK_RAW, protocol, remoteAddr, remotePort); + return open(ZTS_AF_INET6, ZTS_SOCK_RAW, protocol); + } + + /** + * @brief Create a socket (sets zts_errno) + * + * @param socket_family Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param socket_type Type of socket (ZTS_SOCK_STREAM, ZTS_SOCK_DGRAM, ZTS_SOCK_RAW) + * @param protocol Protocols supported on this socket + * @return Numbered file descriptor on success. ZTS_ERR_SERVICE or ZTS_ERR_SOCKET on failure. + */ + static int open(const int socket_family, const int socket_type, const int protocol) + { + int fd; + if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { + printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); + } + return fd; + } + + /** + * @brief Close a socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE on failure. + */ + static int close(int fd) + { + return zts_close(fd); + } + + /** + * @brief Shut down some aspect of a socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static int shutdown(int fd) + { + return zts_shutdown(fd, ZTS_SHUT_RDWR); + } + + /** + * @brief Bind a socket to a virtual interface (sets zts_errno) + * + * @param fd Socket file descriptor + * @param remoteAddr Remote Address to connect to + * @param remotePort Remote Port to connect to + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static int bind(int fd, const char *localAddr, const int localPort) + { + struct zts_sockaddr_in in4 = sockaddr_in(localAddr, localPort); + + int err = ZTS_ERR_OK; + if ((err = zts_bind(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { + printf("Error binding to interface (fd=%d, ret=%d, zts_errno=%d).\n", + fd, err, zts_errno); + } + return err; + } + + static int bind6(int fd, const char *remoteAddr, const int remotePort) + { + printf("IPv6 NOT IMPLEMENTED.\n"); + return ZTS_ERR_ARG; } /** * @brief Connect a socket to a remote host (sets zts_errno) * - * @param socket_family Address family (ZTS_AF_INET, ZTS_AF_INET6) - * @param socket_type Type of socket (ZTS_SOCK_STREAM, ZTS_SOCK_DGRAM, ZTS_SOCK_RAW) - * @param protocol Protocols supported on this socket + * @param fd Socket file descriptor * @param remoteAddr Remote Address to connect to * @param remotePort Remote Port to connect to * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ - static int connect(const int socket_family, const int socket_type, const int protocol, const char *remoteAddr, const int remotePort) + static int connect(int fd, const char *remoteAddr, const int remotePort) { - if (socket_family == ZTS_AF_INET6) { - printf("IPv6 NOT IMPLEMENTED.\n"); - return -1; - } - struct zts_sockaddr_in in4 = sockaddr_in(remoteAddr, remotePort); - int fd; - if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { - printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); - return -1; - } - // Retries are often required since ZT uses transport-triggered links (explained above) int err = ZTS_ERR_OK; - for (;;) { - printf("Connecting to remote host...\n"); - if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { - printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", + if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { + printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d).\n", fd, err, zts_errno); - zts_close(fd); - // printf("Creating socket...\n"); - if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { - printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); - return -1; - } - zts_delay_ms(250); - } - else { - printf("Connected.\n"); - break; - } + } else { + // Set non-blocking mode + fcntl(fd, ZTS_F_SETFL, ZTS_O_NONBLOCK); } + return err; - // Set non-blocking mode - fcntl(fd, ZTS_F_SETFL, ZTS_O_NONBLOCK); - - return fd; + // int err = ZTS_ERR_OK; + // for (;;) { + // printf("Connecting to remote host...\n"); + // if ((err = zts_connect(fd, (const struct zts_sockaddr *)&in4, sizeof(in4))) < 0) { + // printf("Error connecting to remote host (fd=%d, ret=%d, zts_errno=%d). Trying again.\n", + // fd, err, zts_errno); + // zts_close(fd); + // // printf("Creating socket...\n"); + // if ((fd = zts_socket(socket_family, socket_type, protocol)) < 0) { + // printf("Error creating ZeroTier socket (fd=%d, zts_errno=%d).\n", fd, zts_errno); + // return -1; + // } + // zts_delay_ms(250); + // } + // else { + // printf("Connected.\n"); + // break; + // } + // } } - static int fcntlSetBlocking(int fd, bool isBlocking) + static int connect6(int fd, const char *remoteAddr, const int remotePort) + { + printf("IPv6 NOT IMPLEMENTED.\n"); + return ZTS_ERR_ARG; + } + + /** + * @brief Read bytes from socket onto buffer (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static ssize_t read(int fd, nbind::Buffer buf) + { + return zts_read(fd, buf.data(), buf.length()); + } + + /** + * @brief Write bytes from buffer to socket (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static ssize_t write(int fd, nbind::Buffer buf) + { + return zts_write(fd, buf.data(), buf.length()); + } + + /** + * @brief Write data from multiple buffers to socket. (sets zts_errno) + * + * @param fd Socket file descriptor + * @param bufs Array of source buffers + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static ssize_t writev(int fd, std::vector bufs) + { + std::size_t size = bufs.size(); + zts_iovec iov[size]; + for (std::size_t i = 0; i != size; ++i) { + iov[i].iov_base = bufs[i].data(); + iov[i].iov_len = bufs[i].length(); + } + return zts_writev(fd, iov, bufs.size()); + } + + /** + * @brief Receive data from remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf Pointer to data buffer + * @param flags + * @return Byte count received on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static ssize_t recv(int fd, nbind::Buffer buf, int flags) + { + return zts_recv(fd, buf.data(), buf.length(), flags); + } + + /** + * @brief Send data to remote host (sets zts_errno) + * + * @param fd Socket file descriptor + * @param buf data buffer + * @param flags + * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static ssize_t send(int fd, nbind::Buffer buf, int flags) + { + return zts_send(fd, buf.data(), buf.length(), flags); + } + + static int setBlocking(int fd, bool isBlocking) { int flags = fcntl(fd, ZTS_F_GETFL, 0); if (isBlocking) { @@ -392,6 +538,27 @@ public: return fcntl(fd, ZTS_F_SETFL, flags); } + static int setNoDelay(int fd, bool isNdelay) + { + int flags = fcntl(fd, ZTS_F_GETFL, 0); + if (isNdelay) { + flags &= ~ZTS_O_NDELAY; + } else { + flags &= ZTS_O_NDELAY; + } + return fcntl(fd, ZTS_F_SETFL, flags); + } + + static int setKeepalive(int fd, int yes) + { + return setsockopt(fd, ZTS_SOL_SOCKET, ZTS_SO_KEEPALIVE, &yes, sizeof(yes)); + } + + static int setKeepidle(int fd, int idle) + { + return setsockopt(fd, ZTS_IPPROTO_TCP, ZTS_TCP_KEEPIDLE, &idle, sizeof(idle)); + } + /** * @brief Issue file control commands on a socket * @@ -405,56 +572,109 @@ public: return zts_fcntl(fd, cmd, flags); } - static ssize_t read(int fd, nbind::Buffer buf) + /** + * @brief Set socket options (sets zts_errno) + * + * @param fd Socket file descriptor + * @param level Protocol level to which option name should apply + * @param optname Option name to set + * @param optval Source of option value to set + * @param optlen Length of option value + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static int setsockopt(int fd, int level, int optname, const void *optval, zts_socklen_t optlen) { - return zts_read(fd, buf.data(), buf.length()); + return zts_setsockopt(fd, level, optname, optval, optlen); } - static ssize_t write(int fd, nbind::Buffer buf) + /** + * @brief Get socket options (sets zts_errno) + * + * @param fd Socket file descriptor + * @param level Protocol level to which option name should apply + * @param optname Option name to get + * @param optval Where option value will be stored + * @param optlen Length of value + * @return ZTS_ERR_OK on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. + */ + static int getsockopt(int fd, int level, int optname, void *optval, zts_socklen_t *optlen) { - return zts_write(fd, buf.data(), buf.length()); + return zts_getsockopt(fd, level, optname, optval, optlen); } - static ssize_t writev(int fd, std::vector bufs) + /** + * @brief Get socket name (sets zts_errno) + * + * @param fd Socket file descriptor + * @param addr Name associated with this socket + * @param addrlen Length of name + * @return Sockaddress structure + */ + static zts_sockaddr_in getsockname(int fd) { - std::size_t size = bufs.size(); - zts_iovec iov[size]; - for (std::size_t i = 0; i != size; ++i) { - iov[i].iov_base = bufs[i].data(); - iov[i].iov_len = bufs[i].length(); - } - return zts_writev(fd, iov, bufs.size()); - } - - static ssize_t recv(int fd, nbind::Buffer buf, int flags) - { - return zts_recv(fd, buf.data(), buf.length(), flags); - } - - static ssize_t send(int fd, nbind::Buffer buf, int flags) - { - return zts_send(fd, buf.data(), buf.length(), flags); - } - - static int close(int fd) - { - return zts_close(fd); - } - - static zts_sockaddr_in sockaddr_in(const char *remoteAddr, const int remotePort) - { - struct zts_sockaddr_in in4; - in4.sin_port = zts_htons(remotePort); - #if defined(_WIN32) - in4.sin_addr.S_addr = zts_inet_addr(remoteAddr); - #else - in4.sin_addr.s_addr = zts_inet_addr(remoteAddr); - #endif - in4.sin_family = ZTS_AF_INET; + struct zts_sockaddr_in in4; + zts_socklen_t addrlen; + zts_getsockname(fd, (struct zts_sockaddr *)&in4, &addrlen); return in4; - } + } - static Node getMyNode() { return myNode; } + static zts_sockaddr_in6 getsockname6(int fd) + { + struct zts_sockaddr_in6 in6; + zts_socklen_t addrlen; + zts_getsockname(fd, (struct zts_sockaddr *)&in6, &addrlen); + return in6; + } + + /** + * @brief Get the peer name for the remote end of a connected socket + * + * @param fd Socket file descriptor + * @param addr Name associated with remote end of this socket + * @param addrlen Length of name + * @return Sockaddress structure + */ + static zts_sockaddr_in getpeername(int fd) + { + struct zts_sockaddr_in in4; + zts_socklen_t addrlen; + zts_getpeername(fd, (struct zts_sockaddr *)&in4, &addrlen); + return in4; + } + + static zts_sockaddr_in6 getpeername6(int fd) + { + struct zts_sockaddr_in6 in6; + zts_socklen_t addrlen; + zts_getpeername(fd, (struct zts_sockaddr *)&in6, &addrlen); + return in6; + } + + /** + * Convert IPv4 and IPv6 address structures to human-readable text form. + * + * @param af Address family (ZTS_AF_INET, ZTS_AF_INET6) + * @param src Pointer to source address structure + * @param dst Pointer to destination character array + * @param size Size of the destination buffer + * @return On success, returns a non-null pointer to the destination character array + */ + static const char * inet_ntop(const zts_sockaddr in) + { + if (in.sa_family == ZTS_AF_INET) { + const zts_sockaddr_in *in4 = (const zts_sockaddr_in *)∈ + char ipstr[ZTS_INET_ADDRSTRLEN]; + zts_inet_ntop(ZTS_AF_INET, &(in4->sin_addr), ipstr, ZTS_INET_ADDRSTRLEN); + return ipstr; + } else if (in.sa_family == ZTS_AF_INET6) { + const zts_sockaddr_in6 *in6 = (const zts_sockaddr_in6 *)∈ + char ipstr[ZTS_INET6_ADDRSTRLEN]; + zts_inet_ntop(ZTS_AF_INET6, &(in6->sin6_addr), ipstr, ZTS_INET6_ADDRSTRLEN); + return ipstr; + } else { + return ""; + } + } }; #include "nbind/nbind.h" @@ -467,24 +687,44 @@ NBIND_CLASS(Node) { NBIND_CLASS(ZeroTier) { method(start); + method(restart); + method(stop); + method(free); + method(join); - method(connectStream); - method(connectDgram); - method(connectRaw); - method(connectStream6); - method(connectDgram6); - method(connectRaw6); + + method(openStream); + method(openDgram); + method(openRaw); + method(openStream6); + method(openDgram6); + method(openRaw6); + method(open); + method(close); + method(shutdown); + + method(bind); + method(bind6); method(connect); + method(connect6); + method(read); method(write); method(writev); method(recv); method(send); - method(fcntlSetBlocking); + + method(setBlocking); + method(setNoDelay); + method(setKeepalive); + method(setKeepidle); method(fcntl); - method(close); - method(restart); - method(stop); - method(free); + + method(getsockname); + method(getsockname6); + method(getpeername); + method(getpeername6); + method(inet_ntop); + method(getMyNode); } diff --git a/examples/node/libzt.js b/examples/node/libzt.js index 0908765..683f596 100644 --- a/examples/node/libzt.js +++ b/examples/node/libzt.js @@ -1,53 +1,101 @@ 'use strict'; const net = require('net'); +const stream = require('stream'); +const { types: { isUint8Array } } = require('util'); + const nbind = require('@mcesystems/nbind') const ZeroTier = nbind.init().lib.ZeroTier +const { + errnoException, + writevGeneric, + writeGeneric, + onStreamRead, + kAfterAsyncWrite, + kHandle, + kUpdateTimer, + // setStreamTimeout, + kBuffer, + kBufferCb, + kBufferGen +} = require('./stream_commons'); + +const kLastWriteQueueSize = Symbol('lastWriteQueueSize'); + /* - * EXAMPLE USAGE + * EXAMPLE of Low-level usage * Usage: `nc -lv 4444` */ -function example() { +function example(nwid, address, port) { // Start ZeroTier service ZeroTier.start(".zerotier", 9994); // Join virtual network - ZeroTier.join("8056c2e21c000001"); + ZeroTier.join(nwid); - // Open the socket - let fd = ZeroTier.connectStream("29.49.7.203", 4444); + // Connect the socket + const _connect = (address, port, callback) => { + // Open the socket + const fd = ZeroTier.openStream(); + if (fd < 0) { callback(new Error('Could not open socket, errno: ' + fd)); return; } - // Send some data - ZeroTier.send(fd, Buffer.from("Name?\n", 'utf8'), 0) + // Try connect + const status = ZeroTier.connect(fd, address, port); - // Set blocking read mode - // ZeroTier.fcntlSetBlocking(fd, true); - let heartbeat = setInterval(() => process.stderr.write('.'), 100) + console.log(status); + if (status === 0) { + callback(null, fd); + } else { + // Close previous socket + ZeroTier.close(fd); + setTimeout(_connect, 250, address, port, callback); + } + } // Receive some data - const _read = () => { + const _read = (fd, callback) => { const buf = Buffer.alloc(32) let bytes = -1 do { bytes = ZeroTier.recv(fd, buf, 0) - if (bytes > 0) { process.stdout.write(buf.toString('utf8')) } + if (bytes > 0) { callback(null, buf); } } while (bytes > 0); if (!ZeroTier.getMyNode().online || buf.toString('utf8').includes("exit")) { - // Close the socket - ZeroTier.close(fd) - // Stop ZeroTier service - ZeroTier.stop() - // Clear the interval - clearInterval(heartbeat) + callback('end'); } else { - setTimeout(_read, 500) + setTimeout(_read, 500, fd, callback) } } - _read() + + _connect(address, port, (err, fd) => { + if (err) { console.error(err); return; } + console.debug("Connected."); + + // Send some data + ZeroTier.send(fd, Buffer.from("Name?\n", 'utf8'), 0); + + // Set blocking read mode + // ZeroTier.setBlocking(fd, true); + const heartbeat = setInterval(() => process.stderr.write('.'), 100); + + _read(fd, (stop, buf) => { + if (stop) { + // Close the socket + ZeroTier.close(fd); + // Stop ZeroTier service + ZeroTier.stop(); + // Clear the interval + clearInterval(heartbeat); + return; + } + + process.stdout.write(buf.toString('utf8')); + }); + }); } @@ -67,7 +115,8 @@ function connect(...args) { const normalized = net._normalizeArgs(args); const options = normalized[0]; // debug('createConnection', normalized); - const socket = new Socket(options); + + const socket = new Socket(Object.assign({ handle: new ZTCP() }, options)); if (options.timeout) { socket.setTimeout(options.timeout); @@ -76,199 +125,171 @@ function connect(...args) { return socket.connect(normalized); } +/* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L567 + */ +function tryReadStart(socket) { + // Not already reading, start the flow + // debug('Socket._handle.readStart'); + socket._handle.reading = true; + const err = socket._handle.readStart(); + if (err) + socket.destroy(errnoException(err, 'read')); +} + /* * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L1107 */ -function afterConnect(status, self, req, readable, writable) { - // const self = handle[owner_symbol]; +// function afterConnect(status, self, req, readable, writable) { +// // const self = handle[owner_symbol]; - // Callback may come after call to destroy - if (self.destroyed) { - return; +// // Callback may come after call to destroy +// if (self.destroyed) { +// return; +// } + +// // debug('afterConnect'); + +// // assert(self.connecting); +// self.connecting = false; +// self._sockname = null; + +// if (status === 0) { +// self.readable = readable; +// if (!self._writableState.ended) +// self.writable = writable; +// self._unrefTimer(); + +// self.emit('connect'); +// self.emit('ready'); + +// // Start the first read, or get an immediate EOF. +// // this doesn't actually consume any bytes, because len=0. +// if (readable && !self.isPaused()) +// self.read(0); + +// } else { +// self.connecting = false; +// let details; +// if (req.localAddress && req.localPort) { +// details = req.localAddress + ':' + req.localPort; +// } +// const ex = new Error(status, +// 'connect', +// req.address, +// req.port, +// details); +// if (details) { +// ex.localAddress = req.localAddress; +// ex.localPort = req.localPort; +// } +// self.destroy(ex); +// } +// } + +// function afterShutdown(self, _status) { +// // const self = this.handle[owner_symbol]; + +// // debug('afterShutdown destroyed=%j', self.destroyed, +// // self._readableState); + +// this.callback(); + +// // Callback may come after call to destroy. +// if (self.destroyed) +// return; + +// if (!self.readable || self.readableEnded) { +// // debug('readableState ended, destroying'); +// self.destroy(); +// } +// } + +// function writeGeneric(self, chunk, encoding, callback) { +// const decodeStrings = self._writableState && self._writableState.decodeStrings +// const buf = (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk + +// let bytes +// const err = ZeroTier.send(self._fd, buf, 0) +// switch (err) { +// case -1: +// callback(new Error("ZeroTier Socket error")) +// break +// case -2: +// callback(new Error("ZeroTier Service error")) +// break +// case -3: +// callback(new Error("ZeroTier Invalid argument")) +// break +// default: +// bytes = err +// callback() +// } + +// return { +// async: true, +// bytes: bytes, +// } +// } + +// function writevGeneric(self, chunks, callback) { +// const decodeStrings = self._writableState && self._writableState.decodeStrings +// const bufs = chunks.map(({ chunk, encoding }) => (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk) + +// let bytes +// const err = ZeroTier.writev(self._fd, bufs) +// switch (err) { +// case -1: +// callback(new Error("ZeroTier Socket error")) +// break +// case -2: +// callback(new Error("ZeroTier Service error")) +// break +// case -3: +// callback(new Error("ZeroTier Invalid argument")) +// break +// default: +// bytes = err +// callback() +// } + +// return { +// async: true, +// bytes: bytes, +// } +// } + + + +class ZTCP { + bytesRead = 0 + bytesWritten = 0 + writeQueueSize = 0 + + _fd = null + _reading = false + readTimer = null + + get reading() { + return this._reading; } - // debug('afterConnect'); - - // assert(self.connecting); - self.connecting = false; - self._sockname = null; - - if (status === 0) { - self.readable = readable; - if (!self._writableState.ended) - self.writable = writable; - self._unrefTimer(); - - self.emit('connect'); - self.emit('ready'); - - // Start the first read, or get an immediate EOF. - // this doesn't actually consume any bytes, because len=0. - if (readable && !self.isPaused()) - self.read(0); - - } else { - self.connecting = false; - let details; - if (req.localAddress && req.localPort) { - details = req.localAddress + ':' + req.localPort; - } - const ex = new Error(status, - 'connect', - req.address, - req.port, - details); - if (details) { - ex.localAddress = req.localAddress; - ex.localPort = req.localPort; - } - self.destroy(ex); - } -} - -function writeGeneric(self, chunk, encoding, callback) { - const decodeStrings = self._writableState && self._writableState.decodeStrings - const buf = (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk - - let bytes - const err = ZeroTier.send(self._fd, buf, 0) - switch (err) { - case -1: - callback(new Error("ZeroTier Socket error")) - break - case -2: - callback(new Error("ZeroTier Service error")) - break - case -3: - callback(new Error("ZeroTier Invalid argument")) - break - default: - bytes = err - callback(null) + set reading(val) { + return this._reading = val; } - return { - async: true, - bytes: bytes, - } -} - -function writevGeneric(self, chunks, callback) { - const decodeStrings = self._writableState && self._writableState.decodeStrings - const bufs = chunks.map(({ chunk, encoding }) => (!decodeStrings && !Buffer.isBuffer(chunk)) ? Buffer.from(chunk, encoding) : chunk) - - let bytes - const err = ZeroTier.writev(self._fd, bufs) - switch (err) { - case -1: - callback(new Error("ZeroTier Socket error")) - break - case -2: - callback(new Error("ZeroTier Service error")) - break - case -3: - callback(new Error("ZeroTier Invalid argument")) - break - default: - bytes = err - callback(null) - } - - return { - async: true, - bytes: bytes, - } -} - -class Socket extends net.Socket { - /* - * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L929 - */ - connect(...args) { - let normalized; - // If passed an array, it's treated as an array of arguments that have - // already been normalized (so we don't normalize more than once). This has - // been solved before in https://github.com/nodejs/node/pull/12342, but was - // reverted as it had unintended side effects. - if (Array.isArray(args[0])) { - normalized = args[0]; - } else { - normalized = net._normalizeArgs(args); - } - const options = normalized[0]; - const cb = normalized[1]; - - // if (this.write !== net.Socket.prototype.write) - // this.write = net.Socket.prototype.write; - - if (this.destroyed) { - this._handle = null; - this._peername = null; - this._sockname = null; + readStart() { + if (!this._buf) { + this._buf = Buffer.alloc(128); } - // const { path } = options; - // const pipe = !!path; - // debug('pipe', pipe, path); - - // if (!this._handle) { - // this._handle = pipe ? - // new Pipe(PipeConstants.SOCKET) : - // new TCP(TCPConstants.SOCKET); - // initSocketHandle(this); - // } - - if (cb !== null) { - this.once('connect', cb); - } - - this._unrefTimer(); - - this.connecting = true; - this.writable = true; - - // if (pipe) { - // validateString(path, 'options.path'); - // defaultTriggerAsyncIdScope( - // this[async_id_symbol], internalConnect, this, path - // ); - // } else { - // lookupAndConnect(this, options); - // } - - const { host, port } = options; - // If host is an IP, skip performing a lookup - const addressType = net.isIP(host); - if (addressType) { - this._fd = ZeroTier.connectStream(host, port); - afterConnect(0, this, {}, true, true); - } else { - throw new Error("DNS LOOKUP NOT IMPLEMENTED"); - } - - return this; - } - - /* - * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_readable_read_size_1 - */ - _read(size) { - // debug('_read'); - - if (this.connecting) { - // debug('_read wait for connection'); - this.once('connect', () => this._read(size)); - return - } - - if (!this.readChunk || this.readChunk.length < size) { - this.readChunk = Buffer.alloc(size) - } - - let bytes = -1 - let moreData = true + let bytes = 0 do { - bytes = ZeroTier.recv(this._fd, this.readChunk, 0) + bytes = ZeroTier.read(this._fd, this._buf) + if (bytes >= 0) { + this.bytesRead += bytes; + bytes = 0; + } switch (bytes) { case -2: throw new Error("ZeroTier Service error") @@ -276,13 +297,356 @@ class Socket extends net.Socket { throw new Error("ZeroTier Invalid argument") default: if (bytes > 0) { - // this.bytesRead += bytes - moreData = this.push(this.readChunk) + this.bytesRead += bytes + this._buf = this.onread(this._buf) } } - } while (bytes > 0 && moreData) + } while (bytes > 0 && this._reading) - if (moreData) { setTimeout(() => this._read(size), 500) } + if (this._reading) { readTimer = setTimeout(() => this._read(size), 500) } + } + + readStop() { + if (readTimer) { + clearTimeout(readTimer); + readTimer = null; + } + this._reading = false + } + + writev(req, chunks, allBuffers) { + let bufs = []; + + if (allBuffers) { + bufs = chunks; + } else { + const arr = chunks; + for (let i = 0; i < arr.length; i+=2) { + const chunk = arr[i]; + const encoding = arr[i+1]; + chunks.push(Buffer.from(chunk, encoding)); + } + } + + let bytes = ZeroTier.writev(this._fd, bufs); + if (bytes >= 0) { + this.bytesWritten += bytes; + bytes = 0; + } + + const status = bytes; + // https://github.com/nodejs/node/blob/v12.18.3/lib/internal/stream_base_commons.js#L80 + if (req.oncomplete) { req.oncomplete.call(req, status); } + return status; + } + + writeBuffer(req, buf) { + let bytes = ZeroTier.write(this._fd, buf); + if (bytes >= 0) { + this.bytesWritten += bytes; + bytes = 0; + } + + const status = bytes; + // https://github.com/nodejs/node/blob/v12.18.3/lib/internal/stream_base_commons.js#L80 + if (req.oncomplete) { req.oncomplete.call(req, status); } + return status; + } + + writeLatin1String(req, data) { + return this.writeBuffer(req, Buffer.from(data, 'latin1')); + } + + writeUtf8String(req, data) { + return this.writeBuffer(req, Buffer.from(data, 'utf8')); + } + + writeAsciiString(req, data) { + return this.writeBuffer(req, Buffer.from(data, 'ascii')); + } + + writeUcs2String(req, data) { + return this.writeBuffer(req, Buffer.from(data, 'ucs2')); + } + + getAsyncId() { + return -1; + } + + useUserBuffer(buf) { + this._buf = buf; + } + + setBlocking(newValue) { + return ZeroTier.setBlocking(this._fd, newValue); + } + + setNoDelay(newValue) { + return ZeroTier.setNoDelay(this._fd, newValue); + } + + setKeepalive(enable, initialDelay) { + ZeroTier.setKeepidle(initialDelay); + return ZeroTier.setKeepalive(this._fd, +enable); + } + + bind(localAddress, localPort) { + return ZeroTier.bind(this._fd, localAddress, localPort); + } + + bind6(localAddress, localPort, _flags) { + return ZeroTier.bind6(this._fd, localAddress, localPort); + } + + open(fd) { + if (fd) { + this._fd = fd; + return 0; + } else { + const err = ZeroTier.openStream(); + if (err < 0) { + return err; + } else { + this._fd = err; + return 0; + } + } + } + + close(callback) { + const err = ZeroTier.close(this._fd); + this._fd = null; + if (callback) { callback(err); } + } + + shutdown(req) { + const status = ZeroTier.shutdown(this._fd); + // https://github.com/nodejs/node/blob/v12.18.3/test/parallel/test-tcp-wrap-connect.js + if (req.oncomplete) { req.oncomplete.call(req, status, this); } + return status; + } + + connect(req, address, port) { + let status = ZeroTier.connect(this._fd, address, port); + + // Retries are often required since ZT uses transport-triggered links + if (status !== 0) { + let count = 0; + while (count < 10) { + // Close previous socket + this.close(); + status = this.open(); + if (status !== 0) { + // Break if reopen-socket fails + break; + } + + // Reconnect + status = ZeroTier.connect(this._fd, address, port); + if (status === 0) { break; } + + count++; + } + } + + // https://github.com/nodejs/node/blob/v12.18.3/test/parallel/test-tcp-wrap-connect.js + if (req && req.oncomplete) { req.oncomplete.call(status, this, req, true, true); } + + return status; + } + + connect6(req, address, port) { + let status = ZeroTier.connect6(this._fd, address, port); + + // Retries are often required since ZT uses transport-triggered links + if (status !== 0) { + let count = 0; + while (count < 10) { + // Close previous socket + this.close(); + status = this.open(); + if (status !== 0) { + // Break if reopen-socket fails + break; + } + + // Reconnect + status = ZeroTier.connect6(this._fd, address, port); + if (status === 0) { break; } + + count++; + } + } + + // https://github.com/nodejs/node/blob/v12.18.3/test/parallel/test-tcp-wrap-connect.js + if (req.oncomplete) { req.oncomplete.call(status, this, req, true, true); } + + return status; + } + + getpeername(out) { + const in4 = ZeroTier.getpeername(this._fd); + out.address = ZeroTier.inet_ntop(in4); + out.family = in4.sin_family; + out.port = in4.sin_port; + return 0 + } + + getsockname(out) { + const in4 = ZeroTier.getsockname(this._fd); + out.address = ZeroTier.inet_ntop(in4); + out.family = in4.sin_family; + out.port = in4.sin_port; + return 0; + } + + listen(port) { + // TODO + // this.onconnection + } + + fchmod(mode) { + // TODO + return 0; + } +} + +class Socket extends net.Socket { + [kLastWriteQueueSize] = 0; + [kBuffer] = null; + [kBufferCb] = null; + [kBufferGen] = null; + + [kHandle] = null; + get _handle() { return this[kHandle]; } + set _handle(v) { return this[kHandle] = v; } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L929 + */ + // connect(...args) { + // let normalized; + // // If passed an array, it's treated as an array of arguments that have + // // already been normalized (so we don't normalize more than once). This has + // // been solved before in https://github.com/nodejs/node/pull/12342, but was + // // reverted as it had unintended side effects. + // if (Array.isArray(args[0])) { + // normalized = args[0]; + // } else { + // normalized = net._normalizeArgs(args); + // } + // const options = normalized[0]; + // const cb = normalized[1]; + + // if (this.write !== net.Socket.prototype.write) + // this.write = net.Socket.prototype.write; + + // if (this.destroyed) { + // this._handle = null; + // this._peername = null; + // this._sockname = null; + // } + + // if (!this._handle) { + // this._handle = new ZTCP(); + // initSocketHandle(this); + // } + + // if (cb !== null) { + // this.once('connect', cb); + // } + + // this._unrefTimer(); + + // this.connecting = true; + // this.writable = true; + + // const { host, port } = options; + // // If host is an IP, skip performing a lookup + // const addressType = net.isIP(host); + // if (addressType) { + // this._fd = ZeroTier.connectStream(host, port); + // afterConnect(0, this, {}, true, true); + // } else { + // throw new Error("DNS LOOKUP NOT IMPLEMENTED"); + // } + + // return this; + // } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L596 + */ + pause() { + if (this[kBuffer] && !this.connecting && this._handle && + this._handle.reading) { + this._handle.reading = false; + if (!this.destroyed) { + const err = this._handle.readStop(); + if (err) + this.destroy(errnoException(err, 'read')); + } + } + return stream.Duplex.prototype.pause.call(this); + } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L610 + */ + resume() { + if (this[kBuffer] && !this.connecting && this._handle && + !this._handle.reading) { + tryReadStart(this); + } + return stream.Duplex.prototype.resume.call(this); + } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L619 + */ + read(n) { + if (this[kBuffer] && !this.connecting && this._handle && + !this._handle.reading) { + tryReadStart(this); + } + return stream.Duplex.prototype.read.call(this, n); + } + + /* + * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_readable_read_size_1 + */ + _read(n) { + // debug('_read'); + + if (this.connecting || !this._handle) { + // debug('_read wait for connection'); + this.once('connect', () => this._read(n)); + } else if (!this._handle.reading) { + tryReadStart(this); + } + + // if (!this.readChunk || this.readChunk.length < n) { + // this.readChunk = Buffer.alloc(n) + // } + + // let bytes = -1 + // let moreData = true + // do { + // bytes = ZeroTier.recv(this._fd, this.readChunk, 0) + // switch (bytes) { + // case -2: + // throw new Error("ZeroTier Service error") + // case -3: + // throw new Error("ZeroTier Invalid argument") + // default: + // if (bytes > 0) { + // // this.bytesRead += bytes + // moreData = this.push(this.readChunk) + // } + // } + // } while (bytes > 0 && moreData) + + // if (moreData) { setTimeout(() => this._read(n), 500) } } /* @@ -302,20 +666,27 @@ class Socket extends net.Socket { /* * https://nodejs.org/docs/latest-v12.x/api/stream.html#stream_writable_final_callback */ - _final(callback) { - const err = ZeroTier.close(this._fd) - - switch (err) { - case -1: - return callback(new Error("ZeroTier Socket error")) - break - case -2: - return callback(new Error("ZeroTier Service error")) - break - default: - return super._final(callback) - } - } + // _final(cb) { + // // If still connecting - defer handling `_final` until 'connect' will happen + // if (this.pending) { + // // debug('_final: not yet connected'); + // return this.once('connect', () => this._final(cb)); + // } + + // if (!this._handle) + // return cb(); + + // // debug('_final: not ended, call shutdown()'); + + // // const req = new ShutdownWrap(); + // const req = {}; + // req.oncomplete = afterShutdown; + // req.handle = this._handle; + // req.callback = cb; + // // const err = this._handle.shutdown(req); + // const err = ZeroTier.shutdown(this._fd); + // return afterShutdown.call(req, this, 0); + // } /* * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L760 @@ -334,34 +705,74 @@ class Socket extends net.Socket { } this._pendingData = null; this._pendingEncoding = ''; - - // if (!this._handle) { - // cb(new ERR_SOCKET_CLOSED()); - // return false; - // } - + + if (!this._handle) { + cb(new Error('ERR_SOCKET_CLOSED')); + return false; + } + this._unrefTimer(); - + let req; if (writev) req = writevGeneric(this, data, cb); else req = writeGeneric(this, data, encoding, cb); - if (req.async) { - // this[kLastWriteQueueSize] = req.bytes; + if (req.async) + this[kLastWriteQueueSize] = req.bytes; + } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L552 + */ + get bufferSize() { + if (this._handle) { + return this[kLastWriteQueueSize] + this.writableLength; } } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L756 + */ + [kAfterAsyncWrite]() { + this[kLastWriteQueueSize] = 0; + } + + /* + * https://github.com/nodejs/node/blob/v12.18.3/lib/net.js#L468 + */ + _onTimeout() { + const handle = this._handle; + const lastWriteQueueSize = this[kLastWriteQueueSize]; + if (lastWriteQueueSize > 0 && handle) { + // `lastWriteQueueSize !== writeQueueSize` means there is + // an active write in progress, so we suppress the timeout. + const { writeQueueSize } = handle; + if (lastWriteQueueSize !== writeQueueSize) { + this[kLastWriteQueueSize] = writeQueueSize; + this._unrefTimer(); + return; + } + } + // debug('_onTimeout'); + this.emit('timeout'); + } + + get [kUpdateTimer]() { + return this._unrefTimer; + } } module.exports = { - example, start: ZeroTier.start, join: ZeroTier.join, + restart: ZeroTier.restart, + stop: ZeroTier.stop, + free: ZeroTier.free, + example, connect, createConnection: connect, Socket, Stream: Socket, // Legacy naming - restart: ZeroTier.restart, - stop: ZeroTier.stop, - free: ZeroTier.free, + TCP: ZTCP, }; diff --git a/examples/node/stream_commons.js b/examples/node/stream_commons.js new file mode 100644 index 0000000..072641d --- /dev/null +++ b/examples/node/stream_commons.js @@ -0,0 +1,242 @@ +'use strict'; + +const kMaybeDestroy = Symbol('kMaybeDestroy'); +const kUpdateTimer = Symbol('kUpdateTimer'); +const kAfterAsyncWrite = Symbol('kAfterAsyncWrite'); +const kHandle = Symbol('kHandle'); +const kSession = Symbol('kSession'); + +// const debug = require('internal/util/debuglog').debuglog('stream'); +const kBuffer = Symbol('kBuffer'); +const kBufferGen = Symbol('kBufferGen'); +const kBufferCb = Symbol('kBufferCb'); + +let excludedStackFn; + +function errnoException(err, syscall, original) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); + const message = original ? + `${syscall} ${code} ${original}` : `${syscall} ${code}`; + + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + // TODO(joyeecheung): errno is supposed to err, like in uvException + ex.code = ex.errno = code; + ex.syscall = syscall; + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || errnoException); + return ex; +} + +function handleWriteReq(req, data, encoding) { + const { handle } = req; + + switch (encoding) { + case 'buffer': + { + const ret = handle.writeBuffer(req, data); + // if (streamBaseState[kLastWriteWasAsync]) + // req.buffer = data; + return ret; + } + case 'latin1': + case 'binary': + return handle.writeLatin1String(req, data); + case 'utf8': + case 'utf-8': + return handle.writeUtf8String(req, data); + case 'ascii': + return handle.writeAsciiString(req, data); + case 'ucs2': + case 'ucs-2': + case 'utf16le': + case 'utf-16le': + return handle.writeUcs2String(req, data); + default: + { + const buffer = Buffer.from(data, encoding); + const ret = handle.writeBuffer(req, buffer); + // if (streamBaseState[kLastWriteWasAsync]) + // req.buffer = buffer; + return ret; + } + } +} + +function onWriteComplete(status) { + debug('onWriteComplete', status, this.error); + + const stream = this.handle[owner_symbol]; + + if (stream.destroyed) { + if (typeof this.callback === 'function') + this.callback(null); + return; + } + + if (status < 0) { + const ex = errnoException(status, 'write', this.error); + stream.destroy(ex, this.callback); + return; + } + + stream[kUpdateTimer](); + stream[kAfterAsyncWrite](this); + + if (typeof this.callback === 'function') + this.callback(null); +} + +function createWriteWrap(handle) { + // const req = new WriteWrap(); + const req = {}; + + req.handle = handle; + req.oncomplete = onWriteComplete; + req.async = false; + req.bytes = 0; + req.buffer = null; + + return req; +} + +function writevGeneric(self, data, cb) { + const req = createWriteWrap(self[kHandle]); + const allBuffers = data.allBuffers; + let chunks; + if (allBuffers) { + chunks = data; + for (let i = 0; i < data.length; i++) + data[i] = data[i].chunk; + } else { + chunks = new Array(data.length << 1); + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + chunks[i * 2] = entry.chunk; + chunks[i * 2 + 1] = entry.encoding; + } + } + const err = req.handle.writev(req, chunks, allBuffers); + + // Retain chunks + if (err === 0) req._chunks = chunks; + + afterWriteDispatched(self, req, err, cb); + return req; +} + +function writeGeneric(self, data, encoding, cb) { + const req = createWriteWrap(self[kHandle]); + const err = handleWriteReq(req, data, encoding); + + afterWriteDispatched(self, req, err, cb); + return req; +} + +function afterWriteDispatched(self, req, err, cb) { + // req.bytes = streamBaseState[kBytesWritten]; + // req.async = !!streamBaseState[kLastWriteWasAsync]; + + if (err !== 0) + return self.destroy(errnoException(err, 'write', req.error), cb); + + if (!req.async) { + cb(); + } else { + req.callback = cb; + } +} + +function onStreamRead(arrayBuffer, offset, nread) { + // const nread = streamBaseState[kReadBytesOrError]; + + const handle = this; + const stream = this[owner_symbol]; + + stream[kUpdateTimer](); + + if (nread > 0 && !stream.destroyed) { + let ret; + let result; + const userBuf = stream[kBuffer]; + if (userBuf) { + result = (stream[kBufferCb](nread, userBuf) !== false); + const bufGen = stream[kBufferGen]; + if (bufGen !== null) { + const nextBuf = bufGen(); + if (isUint8Array(nextBuf)) + stream[kBuffer] = ret = nextBuf; + } + } else { + // const offset = streamBaseState[kArrayBufferOffset]; + const buf = Buffer.from(arrayBuffer, offset, nread); + result = stream.push(buf); + } + if (!result) { + handle.reading = false; + if (!stream.destroyed) { + const err = handle.readStop(); + if (err) + stream.destroy(errnoException(err, 'read')); + } + } + + return ret; + } + + if (nread === 0) { + return; + } + + // if (nread !== UV_EOF) { + // return stream.destroy(errnoException(nread, 'read')); + // } + + // Defer this until we actually emit end + if (stream._readableState.endEmitted) { + if (stream[kMaybeDestroy]) + stream[kMaybeDestroy](); + } else { + if (stream[kMaybeDestroy]) + stream.on('end', stream[kMaybeDestroy]); + + // TODO(ronag): Without this `readStop`, `onStreamRead` + // will be called once more (i.e. after Readable.ended) + // on Windows causing a ECONNRESET, failing the + // test-https-truncate test. + if (handle.readStop) { + const err = handle.readStop(); + if (err) + return stream.destroy(errnoException(err, 'read')); + } + + // Push a null to signal the end of data. + // Do it before `maybeDestroy` for correct order of events: + // `end` -> `close` + stream.push(null); + stream.read(0); + } +} + +module.exports = { + errnoException, + createWriteWrap, + writevGeneric, + writeGeneric, + onStreamRead, + kAfterAsyncWrite, + kMaybeDestroy, + kUpdateTimer, + kHandle, + kSession, + // setStreamTimeout, + kBuffer, + kBufferCb, + kBufferGen +}; diff --git a/examples/node/test.js b/examples/node/test.js index 2aeb3cb..91c3a82 100644 --- a/examples/node/test.js +++ b/examples/node/test.js @@ -2,7 +2,7 @@ const libzt = require('./libzt') -// libzt.example() +// libzt.example("8056c2e21c000001", "29.49.7.203", 4444) libzt.start(".zerotier", 9994) @@ -10,15 +10,16 @@ libzt.join("8056c2e21c000001") // Usage: `nc -lv 4444` let client = libzt.createConnection({ port: 4444, host: '29.49.7.203' }, () => { - // 'connect' listener. console.log('connected to server!'); - // client.write('world!\r\n'); }); -client.write("Name?\n", 'utf8'); +client.on('ready', () => { + client.write("Name?\n", 'utf8'); +}); client.on('data', (data) => { - console.log(data.toString()); - client.end(); + console.log(data.toString('utf8').trimEnd()); + if (data.toString('utf8').includes("exit")) { client.end(); } }); client.on('end', () => { console.log('disconnected from server'); + libzt.stop() }); From 57f7799e4f382385613f85f715ae20a9f876f89e Mon Sep 17 00:00:00 2001 From: heri16 <527101+heri16@users.noreply.github.com> Date: Fri, 7 Aug 2020 19:25:36 +0800 Subject: [PATCH 73/78] Update README --- README.md | 3 +++ examples/node/README.md | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 examples/node/README.md diff --git a/README.md b/README.md index 965ceba..e92057c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ To build both `release` and `debug` libraries for only your host's architecture ``` make update; make patch; make host +# OR +brew install cmake +make clean; make update && make patch && make host_release CC=clang CXX=clang++ ``` Typical build output: diff --git a/examples/node/README.md b/examples/node/README.md new file mode 100644 index 0000000..f5a6260 --- /dev/null +++ b/examples/node/README.md @@ -0,0 +1,35 @@ +## Building from source + +The npm install script will attempt to statically link to `libzt.a`. + +You first need `Cmake` to build the fPIC-version of the `libzt.a` library. + +To build both `release` and `debug` libraries for only your host's architecture use `make host`. Or optionally `make host_release` for release only. To build everything including things like iOS frameworks, Android packages, etc, use `make all`. Possible build targets can be seen by using `make list`. Resultant libraries will be placed in `/lib`: + +``` +brew install cmake +(cd ../.. ; make clean; make update && make patch && make host_release CC=clang CXX=clang++) +npm install +npm start +``` + +Typical build output: + +``` +lib +├── release +| └── linux-x86_64 +| ├── libzt.a +| └── libzt.so +| └── macos-x86_64 +| ├── libzt.a +└── debug + └── ... +``` + +## Licensing + +ZeroTier is licensed under the BSL version 1.1. See [LICENSE.txt](./LICENSE.txt) and the ZeroTier pricing page for details. ZeroTier is free to use internally in businesses and academic institutions and for non-commercial purposes. Certain types of commercial use such as building closed-source apps and devices based on ZeroTier or offering ZeroTier network controllers and network management as a SaaS service require a commercial license. + +A small amount of third party code is also included in ZeroTier and is not subject to our BSL license. See [AUTHORS.md](ext/ZeroTierOne/AUTHORS.md) for a list of third party code, where it is included, and the licenses that apply to it. All of the third party code in ZeroTier is liberally licensed (MIT, BSD, Apache, public domain, etc.). If you want a commercial license to use the ZeroTier SDK in your product contact us directly via [contact@zerotier.com](mailto:contact@zerotier.com) + From 96d25e4712b93ee822961fc5428dc4a9acc37c00 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Fri, 4 Sep 2020 15:07:59 -0700 Subject: [PATCH 74/78] Add minor bit of documentation to zts_send to address ticket #74 --- include/ZeroTierSockets.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h index d3f822f..8afb08c 100644 --- a/include/ZeroTierSockets.h +++ b/include/ZeroTierSockets.h @@ -1491,7 +1491,7 @@ ZTS_API int ZTCALL zts_ioctl(int fd, unsigned long request, void *argp); * @param fd Socket file descriptor * @param buf Pointer to data buffer * @param len Length of data to write - * @param flags + * @param flags (e.g. ZTS_MSG_DONTWAIT, ZTS_MSG_MORE) * @return Byte count sent on success. ZTS_ERR_SOCKET, ZTS_ERR_SERVICE, ZTS_ERR_ARG on failure. */ ZTS_API ssize_t ZTCALL zts_send(int fd, const void *buf, size_t len, int flags); From e503bf7a615020fa380a18c746b17cdd8551553a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Mazurski?= <59789761+lUNuXl@users.noreply.github.com> Date: Sun, 20 Sep 2020 22:06:38 +0200 Subject: [PATCH 75/78] Improved java example --- examples/java/README.md | 25 +++++++++ .../libzt/javasimpleexample/Main.java} | 56 +++++++++---------- .../MyZeroTierEventListener.java | 8 +-- 3 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 examples/java/README.md rename examples/java/src/{main/java/ExampleApp.java => com/zerotier/libzt/javasimpleexample/Main.java} (57%) rename examples/java/src/{main/java => com/zerotier/libzt/javasimpleexample}/MyZeroTierEventListener.java (96%) diff --git a/examples/java/README.md b/examples/java/README.md new file mode 100644 index 0000000..095649b --- /dev/null +++ b/examples/java/README.md @@ -0,0 +1,25 @@ +### How to compile/use this example: + +1. Follow "Building from source" section from README.md in the root of this git repository +(the linking step/example from that section does not apply to this example) + +2. Create the java library .jar file: + +``` +make host_jar_release +``` + +for other `host_jar` variants see Makefile in the root of this git repository + +2. Copy the output .jar to this directory: + +``` +cp lib/lib/release/linux-x86_64/zt.jar examples/java/simpleExample/ +``` + +3. Now you can compile this example: + +``` +cd src +javac -cp ../zt.jar ./com/zerotier/libzt/javasimpleexample/*.java +``` diff --git a/examples/java/src/main/java/ExampleApp.java b/examples/java/src/com/zerotier/libzt/javasimpleexample/Main.java similarity index 57% rename from examples/java/src/main/java/ExampleApp.java rename to examples/java/src/com/zerotier/libzt/javasimpleexample/Main.java index 3d024b4..014970b 100644 --- a/examples/java/src/main/java/ExampleApp.java +++ b/examples/java/src/com/zerotier/libzt/javasimpleexample/Main.java @@ -24,43 +24,41 @@ * of your own application. */ +package com.zerotier.libzt.javasimpleexample; + import com.zerotier.libzt.ZeroTier; -import com.zerotier.libzt.ZeroTierEventListener; -public class ExampleApp -{ - static void sleep(int ms) - { - try { Thread.sleep(ms); } - catch (InterruptedException e) { e.printStackTrace(); } - } +public class Main { - public static void main(String[] args) - { - // Set up event listener and start service - MyZeroTierEventListener listener = new MyZeroTierEventListener(); - int servicePort = 9994; - ZeroTier.start("test/path", listener, servicePort); - // Wait for EVENT_NODE_ONLINE - System.out.println("waiting for node to come online..."); - while (listener.isOnline == false) { sleep(50); } - System.out.println("joining network"); - ZeroTier.join(0x0123456789abcdefL); - // Wait for EVENT_NETWORK_READY_IP4/6 - System.out.println("waiting for network config..."); - while (listener.isNetworkReady == false) { sleep(50); } - System.out.println("joined"); + static void sleep(int ms) { + try { + Thread.sleep(ms); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + // Set up event listener and start service + MyZeroTierEventListener listener = new MyZeroTierEventListener(); + int servicePort = 9994; + ZeroTier.start("test/path", listener, servicePort); + // Wait for EVENT_NODE_ONLINE + System.out.println("waiting for node to come online..."); + while (listener.isOnline == false) { sleep(50); } + System.out.println("joining network"); + ZeroTier.join(0x0123456789abcdefL); + // Wait for EVENT_NETWORK_READY_IP4/6 + System.out.println("waiting for network config..."); + while (listener.isNetworkReady == false) { sleep(50); } + System.out.println("joined"); /* - + Begin using socket API after this point - Use ZeroTier.ZeroTierSocket, ZeroTier.ZeroTierSocketFactory, etc - (or) - ZeroTier.socket(), ZeroTier.connect(), etc - */ - } + } } diff --git a/examples/java/src/main/java/MyZeroTierEventListener.java b/examples/java/src/com/zerotier/libzt/javasimpleexample/MyZeroTierEventListener.java similarity index 96% rename from examples/java/src/main/java/MyZeroTierEventListener.java rename to examples/java/src/com/zerotier/libzt/javasimpleexample/MyZeroTierEventListener.java index 2e76ee5..e9b5219 100644 --- a/examples/java/src/main/java/MyZeroTierEventListener.java +++ b/examples/java/src/com/zerotier/libzt/javasimpleexample/MyZeroTierEventListener.java @@ -1,14 +1,14 @@ +package com.zerotier.libzt.javasimpleexample; + import com.zerotier.libzt.ZeroTier; import com.zerotier.libzt.ZeroTierEventListener; -import com.zerotier.libzt.ZeroTierPeerDetails; public class MyZeroTierEventListener implements ZeroTierEventListener { public boolean isNetworkReady = false; public boolean isOnline = false; - public void onZeroTierEvent(long id, int eventCode) - { + public void onZeroTierEvent(long id, int eventCode) { if (eventCode == ZeroTier.EVENT_NODE_UP) { System.out.println("EVENT_NODE_UP: nodeId=" + Long.toHexString(id)); } @@ -40,7 +40,7 @@ public class MyZeroTierEventListener implements ZeroTierEventListener { if (eventCode == ZeroTier.EVENT_NETWORK_READY_IP4) { // We have at least one assigned address and we've received a network configuration System.out.println("ZTS_EVENT_NETWORK_READY_IP4: nwid=" + Long.toHexString(id)); - if ( id == 0xa09acf0233e4b070L) { + if (id == 0xa09acf0233e4b070L) { isNetworkReady = true; } } From 8e06feef7d8a0c6c41a755eb8d1e881b2e62b802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Mazurski?= <59789761+lUNuXl@users.noreply.github.com> Date: Thu, 1 Oct 2020 22:16:27 +0200 Subject: [PATCH 76/78] Fixed host_jar target broken by previous refactoring of java example --- dist.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.sh b/dist.sh index a38aaf1..eb858ae 100755 --- a/dist.sh +++ b/dist.sh @@ -232,7 +232,7 @@ host_jar() # Build sample app classes # Remove old dynamic library if it exists rm -rf $(pwd)/examples/java/$DYNAMIC_LIB_NAME - javac -cp ".:"$LIB_OUTPUT_DIR/zt.jar $(pwd)/examples/java/src/main/java/*.java + javac -cp ".:"$LIB_OUTPUT_DIR/zt.jar $(pwd)/examples/java/src/com/zerotier/libzt/javasimpleexample/*.java # To run: # jar xf $LIB_OUTPUT_DIR/zt.jar libzt.dylib # cp libzt.dylib examples/java/ From b021e820781615486348a973b5129f24cb82a37b Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Wed, 21 Oct 2020 14:26:29 -0700 Subject: [PATCH 77/78] Fix zts_allow_*_caching() bug that prevents user from properly setting value before node startup --- examples/cpp/adhoc.cpp | 7 ++++++- examples/cpp/client.cpp | 7 +++++++ examples/cpp/comprehensive.cpp | 15 +++++++-------- examples/cpp/earthtest.cpp | 8 +++++++- examples/cpp/nonblockingclient.cpp | 15 +++++++-------- examples/cpp/nonblockingserver.cpp | 15 +++++++-------- examples/cpp/server.cpp | 7 +++++++ examples/objective-c/adhoc.m | 7 ++++++- include/ZeroTierSockets.h | 2 +- src/NodeService.cpp | 4 ---- 10 files changed, 55 insertions(+), 32 deletions(-) diff --git a/examples/cpp/adhoc.cpp b/examples/cpp/adhoc.cpp index cdad3f6..df37a91 100644 --- a/examples/cpp/adhoc.cpp +++ b/examples/cpp/adhoc.cpp @@ -226,7 +226,12 @@ int main(int argc, char **argv) uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); int err = ZTS_ERR_OK; - zts_allow_network_caching(false); + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); diff --git a/examples/cpp/client.cpp b/examples/cpp/client.cpp index e247094..5c33439 100644 --- a/examples/cpp/client.cpp +++ b/examples/cpp/client.cpp @@ -227,6 +227,13 @@ int main(int argc, char **argv) int err = ZTS_ERR_OK; + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); diff --git a/examples/cpp/comprehensive.cpp b/examples/cpp/comprehensive.cpp index 23fe872..9d04442 100644 --- a/examples/cpp/comprehensive.cpp +++ b/examples/cpp/comprehensive.cpp @@ -384,16 +384,15 @@ int main(int argc, char **argv) // Bring up ZeroTier service and join network - // Enable/Disable caching of network details in networks.d - // (read function documentation before disabling!) - // zts_allow_network_caching(0) - - // Enable/Disable caching of peer details in peers.d - // (read function documentation before disabling!) - // zts_allow_network_caching(1) - int err = ZTS_ERR_OK; + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); + if((err = zts_start(configPath.c_str(), &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); diff --git a/examples/cpp/earthtest.cpp b/examples/cpp/earthtest.cpp index 89ee426..a0275c8 100644 --- a/examples/cpp/earthtest.cpp +++ b/examples/cpp/earthtest.cpp @@ -181,7 +181,13 @@ int main(int argc, char **argv) int ztServicePort = atoi(argv[2]); // Port ZT uses to send encrypted UDP packets to peers (try something like 9994) int err = ZTS_ERR_OK; - zts_allow_network_caching(false); + + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); diff --git a/examples/cpp/nonblockingclient.cpp b/examples/cpp/nonblockingclient.cpp index 96d43ba..debead1 100644 --- a/examples/cpp/nonblockingclient.cpp +++ b/examples/cpp/nonblockingclient.cpp @@ -228,16 +228,15 @@ int main(int argc, char **argv) // Bring up ZeroTier service and join network - // Enable/Disable caching of network details in networks.d - // (read function documentation before disabling!) - // zts_allow_network_caching(0) - - // Enable/Disable caching of peer details in peers.d - // (read function documentation before disabling!) - // zts_allow_network_caching(1) - int err = ZTS_ERR_OK; + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); diff --git a/examples/cpp/nonblockingserver.cpp b/examples/cpp/nonblockingserver.cpp index 1992db8..0281f09 100644 --- a/examples/cpp/nonblockingserver.cpp +++ b/examples/cpp/nonblockingserver.cpp @@ -226,17 +226,16 @@ int main(int argc, char **argv) // Bring up ZeroTier service and join network - // Enable/Disable caching of network details in networks.d - // (read function documentation before disabling!) - // zts_allow_network_caching(0) - - // Enable/Disable caching of peer details in peers.d - // (read function documentation before disabling!) - // zts_allow_network_caching(1) - int fd, accfd; int err = ZTS_ERR_OK; + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); diff --git a/examples/cpp/server.cpp b/examples/cpp/server.cpp index 3588937..1684ab0 100644 --- a/examples/cpp/server.cpp +++ b/examples/cpp/server.cpp @@ -227,6 +227,13 @@ int main(int argc, char **argv) int fd, accfd; int err = ZTS_ERR_OK; + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); + if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { printf("Unable to start service, error = %d. Exiting.\n", err); exit(1); diff --git a/examples/objective-c/adhoc.m b/examples/objective-c/adhoc.m index 46ab811..bdac06d 100644 --- a/examples/objective-c/adhoc.m +++ b/examples/objective-c/adhoc.m @@ -206,7 +206,12 @@ int main(int argc, char **argv) uint64_t adhoc_nwid = zts_generate_adhoc_nwid_from_range(adhocStartPort, adhocEndPort); int err = ZTS_ERR_OK; - zts_allow_network_caching(false); + // If disabled: (network) details will NOT be written to or read from (networks.d/). It may take slightly longer to start the node + zts_allow_network_caching(1); + // If disabled: (peer) details will NOT be written to or read from (peers.d/). It may take slightly longer to contact a remote peer + zts_allow_peer_caching(1); + // If disabled: Settings will NOT be read from local.conf + zts_allow_local_conf(1); if((err = zts_start(argv[1], &myZeroTierEventCallback, ztServicePort)) != ZTS_ERR_OK) { NSLog(@"Unable to start service, error = %d. Exiting.\n", err); diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h index 8afb08c..d25d00b 100644 --- a/include/ZeroTierSockets.h +++ b/include/ZeroTierSockets.h @@ -871,7 +871,7 @@ ZTS_API int ZTCALL zts_allow_network_caching(uint8_t allowed); ZTS_API int ZTCALL zts_allow_peer_caching(uint8_t allowed); /** - * @brief Enable or disable whether the service will read from a local.conf + * @brief Enable or disable whether the service will read node configuration settings from a local.conf * * @usage Should be called before zts_start() if you intend on changing its state. * diff --git a/src/NodeService.cpp b/src/NodeService.cpp index 7c44686..d867680 100644 --- a/src/NodeService.cpp +++ b/src/NodeService.cpp @@ -229,10 +229,6 @@ public: _ports[0] = 0; _ports[1] = 0; _ports[2] = 0; - - allowNetworkCaching = true; - allowPeerCaching = true; - allowLocalConf = false; } virtual ~NodeServiceImpl() From e304a89ddb8a47cc4a8c44254221f360bcc24950 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 19 Nov 2020 10:08:31 -0800 Subject: [PATCH 78/78] Add check for netif before use in VirtualTap - Fixes bug mentioned in ticket #85 --- src/VirtualTap.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/VirtualTap.cpp b/src/VirtualTap.cpp index c9063d2..45eb853 100644 --- a/src/VirtualTap.cpp +++ b/src/VirtualTap.cpp @@ -482,15 +482,19 @@ void _lwip_eth_rx(VirtualTap *tap, const MAC &from, const MAC &to, unsigned int int err; if (Utils::ntoh(ethhdr.type) == 0x800 || Utils::ntoh(ethhdr.type) == 0x806) { - if ((err = ((struct netif *)tap->netif4)->input(p, (struct netif *)tap->netif4)) != ERR_OK) { - DEBUG_ERROR("packet input error (%d)", err); - pbuf_free(p); + if (tap->netif4) { + if ((err = ((struct netif *)tap->netif4)->input(p, (struct netif *)tap->netif4)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } } } if (Utils::ntoh(ethhdr.type) == 0x86DD) { - if ((err = ((struct netif *)tap->netif6)->input(p, (struct netif *)tap->netif6)) != ERR_OK) { - DEBUG_ERROR("packet input error (%d)", err); - pbuf_free(p); + if (tap->netif6) { + if ((err = ((struct netif *)tap->netif6)->input(p, (struct netif *)tap->netif6)) != ERR_OK) { + DEBUG_ERROR("packet input error (%d)", err); + pbuf_free(p); + } } } }